From 6c70d6a60b286f7cfbbde4e428d41c8de2c7e77a Mon Sep 17 00:00:00 2001 From: diogo464 Date: Thu, 14 Aug 2025 15:34:42 +0100 Subject: feat: improve DriveLogEntry types with discriminated unions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace generic DriveLogEntry with action-specific discriminated unions - Add DriveLogEntryCreateFile, CreateDir, Remove, Rename types - Update Drive_log parsing to create correct union types based on action - Add type guards (isCreateFileEntry, isRenameEntry, etc.) for safe access - Enhance History UI to properly display rename operations (old → new) - Change log_id to revision to match Rust OperationHeader structure - Improve type safety and maintainability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/components/history/HistoryView.tsx | 14 +++--- frontend/lib/drive_server.ts | 66 +++++++++++++++++++++++++---- frontend/lib/drive_types.ts | 51 ++++++++++++++++++++-- 3 files changed, 115 insertions(+), 16 deletions(-) diff --git a/frontend/components/history/HistoryView.tsx b/frontend/components/history/HistoryView.tsx index b01e4c6..d9d3dc2 100644 --- a/frontend/components/history/HistoryView.tsx +++ b/frontend/components/history/HistoryView.tsx @@ -1,4 +1,4 @@ -import { DriveLogEntry } from "@/lib/drive_types" +import { DriveLogEntry, isCreateFileEntry, isRenameEntry } from "@/lib/drive_types" import { DriveHeader } from "@/components/drive/DriveHeader" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Button } from "@/components/ui/button" @@ -102,7 +102,7 @@ export function HistoryView({ entries, currentPage, hasNextPage, hasPrevPage, to ) : ( entries.map((entry) => ( - + {formatDateTime(entry.timestamp)} @@ -115,19 +115,23 @@ export function HistoryView({ entries, currentPage, hasNextPage, hasPrevPage, to {entry.email} - {entry.action === "create_file" && entry.blob_id !== "-" ? ( + {isCreateFileEntry(entry) ? ( {entry.path} + ) : isRenameEntry(entry) ? ( + + {entry.old_path} → {entry.new_path} + ) : ( - entry.path + 'path' in entry ? entry.path : '' )} - {formatFileSize(entry.size)} + {isCreateFileEntry(entry) ? formatFileSize(entry.size) : '-'} )) diff --git a/frontend/lib/drive_server.ts b/frontend/lib/drive_server.ts index 8af0356..e7c88e8 100644 --- a/frontend/lib/drive_server.ts +++ b/frontend/lib/drive_server.ts @@ -139,23 +139,73 @@ export async function Drive_log(): Promise { } const stdout = result.stdout - const entries = [] + const entries: DriveLogEntry[] = [] for (const line of stdout.split('\n')) { if (line.trim() === "") continue; const parts = line.split('\t'); const timestamp = parseInt(parts[0]); - const log_id = parseInt(parts[1]); + const revision = parseInt(parts[1]); const email = parts[2]; const action = parts[3]; - const path = parts[4]; - const blob_id = parts[5]; - const size = parseInt(parts[6]); - entries.push({ - timestamp, log_id, email, action, path, blob_id, size - } as DriveLogEntry); + // Parse based on action type to create the correct discriminated union + switch (action) { + case "create_file": { + const path = parts[4]; + const blob_id = parts[5]; + const size = parseInt(parts[6]); + entries.push({ + timestamp, + revision, + email, + action: "create_file", + path, + blob_id, + size + }); + break; + } + case "create_dir": { + const path = parts[4]; + entries.push({ + timestamp, + revision, + email, + action: "create_dir", + path + }); + break; + } + case "remove": { + const path = parts[4]; + entries.push({ + timestamp, + revision, + email, + action: "remove", + path + }); + break; + } + case "rename": { + const old_path = parts[4]; + const new_path = parts[5]; + entries.push({ + timestamp, + revision, + email, + action: "rename", + old_path, + new_path + }); + break; + } + default: + console.warn(`Unknown log action: ${action}`, parts); + continue; + } } return entries; } \ No newline at end of file diff --git a/frontend/lib/drive_types.ts b/frontend/lib/drive_types.ts index 9220d82..a168a72 100644 --- a/frontend/lib/drive_types.ts +++ b/frontend/lib/drive_types.ts @@ -22,12 +22,57 @@ export interface DriveTreeResponse { root: DriveTreeNode[] } -export interface DriveLogEntry { +// Base interface for all log entries with common fields +export interface DriveLogEntryBase { timestamp: number - log_id: number + revision: number // Changed from log_id to match Rust OperationHeader email: string - action: string +} + +// Specific log entry types based on Rust OperationData variants +export interface DriveLogEntryCreateFile extends DriveLogEntryBase { + action: "create_file" path: string blob_id: string size: number } + +export interface DriveLogEntryCreateDir extends DriveLogEntryBase { + action: "create_dir" + path: string +} + +export interface DriveLogEntryRemove extends DriveLogEntryBase { + action: "remove" + path: string +} + +export interface DriveLogEntryRename extends DriveLogEntryBase { + action: "rename" + old_path: string + new_path: string +} + +// Discriminated union of all possible log entry types +export type DriveLogEntry = + | DriveLogEntryCreateFile + | DriveLogEntryCreateDir + | DriveLogEntryRemove + | DriveLogEntryRename + +// Type guards for working with discriminated unions +export function isCreateFileEntry(entry: DriveLogEntry): entry is DriveLogEntryCreateFile { + return entry.action === "create_file" +} + +export function isCreateDirEntry(entry: DriveLogEntry): entry is DriveLogEntryCreateDir { + return entry.action === "create_dir" +} + +export function isRemoveEntry(entry: DriveLogEntry): entry is DriveLogEntryRemove { + return entry.action === "remove" +} + +export function isRenameEntry(entry: DriveLogEntry): entry is DriveLogEntryRename { + return entry.action === "rename" +} -- cgit