zutils/cmd/file.go

241 lines
5.7 KiB
Go
Raw Permalink Normal View History

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 <path>' 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, ", ")
}