package cli import ( "flag" "fmt" "os" "strings" "zipprine/internal/archiver" "zipprine/internal/fetcher" "zipprine/internal/models" "zipprine/internal/version" ) func Run() bool { // Define flags compress := flag.String("compress", "", "Compress files/folders (source path)") extract := flag.String("extract", "", "Extract archive (archive path)") analyze := flag.String("analyze", "", "Analyze archive (archive path)") output := flag.String("output", "", "Output path for compression or extraction") archiveType := flag.String("type", "zip", "Archive type (zip, tar, tar.gz, gzip, rar)") level := flag.Int("level", 6, "Compression level (1=fast, 6=balanced, 9=best)") overwrite := flag.Bool("overwrite", false, "Overwrite existing files during extraction") preservePerms := flag.Bool("preserve-perms", true, "Preserve file permissions during extraction") exclude := flag.String("exclude", "", "Comma-separated list of patterns to exclude") include := flag.String("include", "", "Comma-separated list of patterns to include") verify := flag.Bool("verify", false, "Verify archive integrity after compression") remoteURL := flag.String("url", "", "Remote URL to download and extract archive from") showVersion := flag.Bool("version", false, "Show version information") help := flag.Bool("help", false, "Show help information") flag.Parse() if *showVersion { fmt.Println(version.FullVersion()) return true } if *help { printHelp() return true } if flag.NFlag() == 0 { return false } if *remoteURL != "" { if *output == "" { fmt.Println("❌ Error: --output is required when using --url") os.Exit(1) } if !fetcher.IsValidArchiveURL(*remoteURL) { fmt.Println("⚠️ Warning: URL does not appear to point to a supported archive format") } if err := fetcher.FetchAndExtract(*remoteURL, *output, *overwrite, *preservePerms); err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } fmt.Println("✨ Remote archive fetched and extracted successfully!") return true } if *compress != "" { if *output == "" { fmt.Println("❌ Error: --output is required for compression") os.Exit(1) } archType := parseArchiveType(*archiveType) if archType == models.RAR { fmt.Println("❌ Error: RAR compression is not supported (proprietary format)") os.Exit(1) } config := &models.CompressConfig{ SourcePath: *compress, OutputPath: *output, ArchiveType: archType, CompressionLevel: *level, VerifyIntegrity: *verify, } if *exclude != "" { config.ExcludePaths = strings.Split(*exclude, ",") } if *include != "" { config.IncludePaths = strings.Split(*include, ",") } fmt.Printf("📦 Compressing %s to %s (%s)...\n", *compress, *output, archType) if err := archiver.Compress(config); err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } fmt.Println("✨ Compression completed successfully!") return true } if *extract != "" { if *output == "" { fmt.Println("❌ Error: --output is required for extraction") os.Exit(1) } // Detect archive type if not specified or set to auto archType := parseArchiveType(*archiveType) if archType == models.AUTO || *archiveType == "" { detectedType, err := archiver.DetectArchiveType(*extract) if err != nil { fmt.Printf("❌ Error detecting archive type: %v\n", err) os.Exit(1) } archType = detectedType fmt.Printf("🔍 Detected archive type: %s\n", archType) } config := &models.ExtractConfig{ ArchivePath: *extract, DestPath: *output, ArchiveType: archType, OverwriteAll: *overwrite, PreservePerms: *preservePerms, } fmt.Printf("📂 Extracting %s to %s...\n", *extract, *output) if err := archiver.Extract(config); err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } fmt.Println("✨ Extraction completed successfully!") return true } // Handle analysis if *analyze != "" { info, err := archiver.Analyze(*analyze) if err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } fmt.Println("\n📊 Archive Analysis") fmt.Println("==================") fmt.Printf("Type: %s\n", info.Type) fmt.Printf("File Count: %d\n", info.FileCount) fmt.Printf("Total Size: %d bytes\n", info.TotalSize) fmt.Printf("Compressed Size: %d bytes\n", info.CompressedSize) if info.CompressionRatio > 0 { fmt.Printf("Compression Ratio: %.2f%%\n", info.CompressionRatio*100) } if info.Checksum != "" { fmt.Printf("Checksum (SHA256): %s\n", info.Checksum) } fmt.Println("\n📁 Files:") for i, file := range info.Files { if i >= 20 { fmt.Printf("... and %d more files\n", len(info.Files)-20) break } fmt.Printf(" - %s (%d bytes)\n", file.Name, file.Size) } return true } fmt.Println("❌ Error: No valid operation specified. Use --help for usage information.") os.Exit(1) return true } func parseArchiveType(typeStr string) models.ArchiveType { switch strings.ToLower(typeStr) { case "zip": return models.ZIP case "tar": return models.TAR case "tar.gz", "targz", "tgz": return models.TARGZ case "gzip", "gz": return models.GZIP case "rar": return models.RAR case "auto": return models.AUTO default: return models.ZIP } } func printHelp() { fmt.Println(version.FullVersion()) fmt.Println("\n🗜️ A modern TUI/CLI archiving tool with support for multiple formats") fmt.Println("\nUSAGE:") fmt.Println(" Interactive mode (default):") fmt.Println(" zipprine") fmt.Println("\n Command-line mode:") fmt.Println(" zipprine [OPTIONS]") fmt.Println("\nOPTIONS:") fmt.Println(" --compress Compress files/folders at the specified path") fmt.Println(" --extract Extract archive at the specified path") fmt.Println(" --analyze Analyze archive at the specified path") fmt.Println(" --output Output path for compression or extraction") fmt.Println(" --type Archive type: zip, tar, tar.gz, gzip, rar (default: zip)") fmt.Println(" --level <1-9> Compression level: 1=fast, 6=balanced, 9=best (default: 6)") fmt.Println(" --overwrite Overwrite existing files during extraction") fmt.Println(" --preserve-perms Preserve file permissions (default: true)") fmt.Println(" --exclude Comma-separated patterns to exclude") fmt.Println(" --include Comma-separated patterns to include") fmt.Println(" --verify Verify archive integrity after compression") fmt.Println(" --url Download and extract archive from remote URL") fmt.Println(" --version Show version information") fmt.Println(" --help Show this help message") fmt.Println("\nEXAMPLES:") fmt.Println(" # Compress a directory") fmt.Println(" zipprine --compress /path/to/source --output archive.zip --type zip") fmt.Println("\n # Extract an archive") fmt.Println(" zipprine --extract archive.tar.gz --output /path/to/dest") fmt.Println("\n # Analyze an archive") fmt.Println(" zipprine --analyze archive.zip") fmt.Println("\n # Download and extract from URL") fmt.Println(" zipprine --url https://example.com/archive.zip --output /path/to/dest") fmt.Println("\n # Compress with exclusions") fmt.Println(" zipprine --compress /project --output project.tar.gz --type tar.gz --exclude '*.log,*.tmp'") fmt.Println("\nSUPPORTED FORMATS:") fmt.Println(" Compression: ZIP, TAR, TAR.GZ, GZIP") fmt.Println(" Extraction: ZIP, TAR, TAR.GZ, GZIP, RAR") fmt.Println("\nNOTE:") fmt.Println(" RAR compression is not supported due to proprietary format.") fmt.Println(" RAR extraction is supported for reading existing RAR archives.") }