package cmd import ( "bufio" "fmt" "os" "path/filepath" "strings" "time" "github.com/zemenawi/zutils/pkg/colors" "github.com/zemenawi/zutils/pkg/formatter" ) func FileInfoCommand(args []string) error { if len(args) < 1 { return fmt.Errorf("please specify a file path") } filePath := args[0] file, err := os.Open(filePath) if err != nil { return fmt.Errorf("error opening file: %w", err) } defer file.Close() fileInfo, err := file.Stat() if err != nil { return fmt.Errorf("error getting file info: %w", err) } if fileInfo.IsDir() { return fmt.Errorf("'%s' is a directory, use 'z info dir ' instead", filePath) } // Count content lines, words, chars := countFileContent(file) // Reset file pointer file.Seek(0, 0) // Get file info fileType := getFileType(filePath) tokens := int(float64(words) / 0.75) size := formatter.FormatSize(fileInfo.Size()) perms := FormatPermissions(fileInfo.Mode()) modTime := fileInfo.ModTime().Format(time.RFC1123) // Print output PrintBoxHeader("FILE INFORMATION", colors.Cyan) fmt.Printf("%s%s📁 Path:%s %s\n", colors.Blue, colors.Bold, colors.Reset, filePath) fmt.Printf("%s%s📄 Name:%s %s\n", colors.Blue, colors.Bold, colors.Reset, filepath.Base(filePath)) fmt.Printf("%s%s📏 Size:%s %s\n", colors.Blue, colors.Bold, colors.Reset, size) fmt.Printf("%s%s🔖 Type:%s %s\n", colors.Blue, colors.Bold, colors.Reset, fileType) fmt.Println() PrintSectionHeader("CONTENT STATISTICS") fmt.Printf("%s%s📊 Lines:%s %d\n", colors.Green, colors.Bold, colors.Reset, lines) fmt.Printf("%s%s📝 Words:%s %d\n", colors.Green, colors.Bold, colors.Reset, words) fmt.Printf("%s%s🔢 Characters:%s %d\n", colors.Green, colors.Bold, colors.Reset, chars) fmt.Printf("%s%s🤖 Tokens (est):%s %d\n", colors.Green, colors.Bold, colors.Reset, tokens) fmt.Println() PrintSectionHeader("FILE DETAILS") fmt.Printf("%s%s🔒 Permissions:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, perms) fmt.Printf("%s └─ %s%s\n", colors.Gray, colors.Reset, GetPermissionHuman(fileInfo.Mode())) fmt.Printf("%s%s📅 Modified:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, modTime) fmt.Printf("%s%s📂 Extension:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, getFileExtension(filePath)) fmt.Println() return nil } func countFileContent(file *os.File) (lines, words, chars int) { scanner := bufio.NewScanner(file) lines = 0 words = 0 chars = 0 for scanner.Scan() { lines++ line := scanner.Text() chars += len(line) words += len(strings.Fields(line)) } return lines, words, chars } func getFileType(path string) string { ext := strings.ToLower(filepath.Ext(path)) switch ext { case ".go": return "Go Source" case ".js": return "JavaScript" case ".ts": return "TypeScript" case ".py": return "Python Source" case ".java": return "Java Source" case ".c": return "C Source" case ".cpp": return "C++ Source" case ".rs": return "Rust Source" case ".json": return "JSON Data" case ".yaml", ".yml": return "YAML Data" case ".xml": return "XML Data" case ".html": return "HTML Document" case ".css": return "CSS Stylesheet" case ".md": return "Markdown" case ".txt": return "Text File" case ".pdf": return "PDF Document" case ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg": return "Image File" case ".mp3", ".wav", ".ogg": return "Audio File" case ".mp4", ".avi", ".mkv": return "Video File" case ".zip", ".tar", ".gz", ".7z", ".rar": return "Archive File" case ".exe", ".bin": return "Binary File" default: return "Unknown Type" } } func getFileExtension(path string) string { ext := filepath.Ext(path) if ext == "" { return "none" } return ext } type Permission struct { Symbol string Human string } func InterpretPermissions(mode os.FileMode) []Permission { var perms []Permission isDir := mode.IsDir() isLink := mode&os.ModeSymlink != 0 bits := []uint32{ uint32(mode >> 6) & 0x7, uint32(mode >> 3) & 0x7, uint32(mode) & 0x7, } who := []string{"Owner", "Group", "Other"} readable := []string{"read", "write", "execute"} for i, whoStr := range who { for j := 0; j < 3; j++ { bitVal := (bits[i] >> uint(2-j)) & 1 symbol := "-" human := "no " + readable[j] if bitVal == 1 { if j == 2 { if isDir { symbol = "x" human = whoStr + " can enter directory" } else if isLink { symbol = "x" human = whoStr + " can follow link" } else { symbol = "x" human = whoStr + " can execute" } } else if j == 1 { symbol = "w" human = whoStr + " can write" } else { symbol = "r" human = whoStr + " can read" } } perms = append(perms, Permission{Symbol: symbol, Human: human}) } } if isDir { perms = append([]Permission{{Symbol: "d", Human: "Directory"}}, perms...) } else if isLink { perms = append([]Permission{{Symbol: "l", Human: "Symbolic Link"}}, perms...) } else { perms = append([]Permission{{Symbol: "-", Human: "Regular File"}}, perms...) } return perms } func FormatPermissions(mode os.FileMode) string { perms := InterpretPermissions(mode) var result string for _, p := range perms { result += p.Symbol } return result } func GetPermissionHuman(mode os.FileMode) string { perms := InterpretPermissions(mode) var parts []string var currentWho string for i, p := range perms { if i == 0 { currentWho = p.Human continue } whoIdx := (i - 1) / 3 who := []string{"Owner", "Group", "Other"}[whoIdx] if p.Symbol != "-" { parts = append(parts, who+" "+p.Human[len(who)+1:]) } } if len(parts) == 0 { return "No permissions" } return currentWho + ", " + strings.Join(parts, ", ") }