From 20affc6a6bd7e800f3cdbd296cd44b85d606fa5a Mon Sep 17 00:00:00 2001 From: bereck-work Date: Fri, 21 Nov 2025 08:38:22 +0000 Subject: [PATCH] (Feat): New UI panel for archive comparison feature --- internal/ui/compare.go | 189 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 internal/ui/compare.go diff --git a/internal/ui/compare.go b/internal/ui/compare.go new file mode 100644 index 0000000..c003679 --- /dev/null +++ b/internal/ui/compare.go @@ -0,0 +1,189 @@ +package ui + +import ( + "fmt" + "os" + + "zipprine/internal/archiver" + "zipprine/internal/models" + + "github.com/charmbracelet/huh" +) + +func RunCompareFlow() error { + var archive1Path, archive2Path string + var showDetails bool + + form := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("📦 First Archive"). + Description("Path to the first archive"). + Value(&archive1Path). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("path cannot be empty") + } + if _, err := os.Stat(s); os.IsNotExist(err) { + return fmt.Errorf("archive does not exist") + } + return nil + }). + Suggestions(getPathCompletions("")), + + huh.NewInput(). + Title("📦 Second Archive"). + Description("Path to the second archive"). + Value(&archive2Path). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("path cannot be empty") + } + if _, err := os.Stat(s); os.IsNotExist(err) { + return fmt.Errorf("archive does not exist") + } + return nil + }). + Suggestions(getPathCompletions("")), + + huh.NewConfirm(). + Title("📋 Show Detailed Differences"). + Description("Display detailed file-by-file comparison"). + Value(&showDetails), + ), + ).WithTheme(huh.ThemeCatppuccin()) + + if err := form.Run(); err != nil { + return err + } + + fmt.Println(InfoStyle.Render("🔍 Analyzing archives...")) + fmt.Println() + + // Detect archive types + type1, err := archiver.DetectArchiveType(archive1Path) + if err != nil { + return fmt.Errorf("failed to detect first archive type: %w", err) + } + + type2, err := archiver.DetectArchiveType(archive2Path) + if err != nil { + return fmt.Errorf("failed to detect second archive type: %w", err) + } + + // Compare archives + result, err := archiver.CompareArchives(archive1Path, archive2Path, type1, type2) + if err != nil { + return fmt.Errorf("failed to compare archives: %w", err) + } + + // Display results + fmt.Println(TitleStyle.Render("📊 Comparison Results")) + fmt.Println() + fmt.Println(result.Summary) + fmt.Println() + + if showDetails { + if len(result.OnlyInFirst) > 0 { + fmt.Println(InfoStyle.Render("📁 Files only in first archive:")) + for _, f := range result.OnlyInFirst { + fmt.Printf(" • %s\n", f) + } + fmt.Println() + } + + if len(result.OnlyInSecond) > 0 { + fmt.Println(InfoStyle.Render("📁 Files only in second archive:")) + for _, f := range result.OnlyInSecond { + fmt.Printf(" • %s\n", f) + } + fmt.Println() + } + + if len(result.Different) > 0 { + fmt.Println(WarningStyle.Render("⚠️ Files that differ:")) + for _, f := range result.Different { + fmt.Printf(" • %s\n", f.Name) + fmt.Printf(" Size: %d bytes → %d bytes\n", f.Size1, f.Size2) + fmt.Printf(" ModTime: %s → %s\n", f.ModTime1, f.ModTime2) + } + fmt.Println() + } + + if len(result.InBoth) > 0 && len(result.Different) == 0 { + fmt.Println(SuccessStyle.Render("✅ All common files are identical!")) + } + } + + return nil +} + +func RunConvertFlow() error { + var sourcePath, destPath string + var destTypeStr string + + form := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("📦 Source Archive"). + Description("Path to the archive to convert"). + Value(&sourcePath). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("path cannot be empty") + } + if _, err := os.Stat(s); os.IsNotExist(err) { + return fmt.Errorf("archive does not exist") + } + return nil + }). + Suggestions(getPathCompletions("")), + + huh.NewInput(). + Title("💾 Destination Path"). + Description("Path for the converted archive"). + Value(&destPath). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("path cannot be empty") + } + return nil + }). + Suggestions(getPathCompletions("")), + + huh.NewSelect[string](). + Title("🎨 Destination Format"). + Options( + huh.NewOption("ZIP", "ZIP"), + huh.NewOption("TAR.GZ", "TARGZ"), + huh.NewOption("TAR", "TAR"), + ). + Value(&destTypeStr), + ), + ).WithTheme(huh.ThemeCatppuccin()) + + if err := form.Run(); err != nil { + return err + } + + fmt.Println(InfoStyle.Render("🔄 Converting archive...")) + fmt.Println() + + // Detect source archive type + sourceType, err := archiver.DetectArchiveType(sourcePath) + if err != nil { + return fmt.Errorf("failed to detect source archive type: %w", err) + } + + destType := models.ArchiveType(destTypeStr) + + // Convert archive + if err := archiver.ConvertArchive(sourcePath, destPath, sourceType, destType); err != nil { + return fmt.Errorf("conversion failed: %w", err) + } + + fmt.Println(SuccessStyle.Render(fmt.Sprintf("✅ Successfully converted %s to %s", sourceType, destType))) + fmt.Println(InfoStyle.Render(fmt.Sprintf("📁 Output: %s", destPath))) + + return nil +}