zutils/cmd/dir.go
selamanapps aeae34365c feat: add zutils v0.2.0 with professional table formatting
- Add directory information with Unicode table borders
- Add network information with professional formatting
- Add version command
- Add comprehensive documentation (README.md, DEVELOPMENT.md)
- Improve table output with proper borders and alignment
- Add project structure (cmd/, pkg/ directories)
2026-05-02 00:05:44 +03:00

203 lines
4.5 KiB
Go

package cmd
import (
"fmt"
"os"
"path/filepath"
"sort"
"time"
"github.com/zemenawi/zutils/pkg/colors"
"github.com/zemenawi/zutils/pkg/formatter"
)
type FileItem struct {
Path string
Size int64
}
type DirStats struct {
TotalSize int64
FileCount int
DirCount int
LargestFiles []FileItem
LargestDirs []FileItem
LastModified time.Time
FileExtensions map[string]int
}
func DirInfoCommand(args []string) error {
if len(args) < 1 {
return fmt.Errorf("please specify a directory path")
}
dirPath := args[0]
stats, err := analyzeDirectory(dirPath)
if err != nil {
return fmt.Errorf("error analyzing directory: %w", err)
}
size := formatter.FormatSize(stats.TotalSize)
modTime := stats.LastModified.Format(time.RFC1123)
PrintBoxHeader("DIRECTORY INFORMATION", colors.Purple)
fmt.Printf("%s%s📁 Path:%s %s\n", colors.Blue, colors.Bold, colors.Reset, dirPath)
fmt.Printf("%s%s📏 Total Size:%s %s\n", colors.Blue, colors.Bold, colors.Reset, size)
fmt.Printf("%s%s📊 Files:%s %d\n", colors.Blue, colors.Bold, colors.Reset, stats.FileCount)
fmt.Printf("%s%s📂 Directories:%s %d\n", colors.Blue, colors.Bold, colors.Reset, stats.DirCount)
fmt.Println()
// Print largest files table
if len(stats.LargestFiles) > 0 {
PrintSectionHeader("LARGEST FILES")
headers := []string{"#", "File Name", "Size"}
data := make([][]string, 0, len(stats.LargestFiles))
for i, file := range stats.LargestFiles {
name := filepath.Base(file.Path)
row := []string{
fmt.Sprintf("%d", i+1),
name,
formatter.FormatSize(file.Size),
}
data = append(data, row)
}
printSimpleTable(headers, data)
}
// Print largest directories table
if len(stats.LargestDirs) > 0 {
PrintSectionHeader("LARGEST DIRS")
headers := []string{"#", "Directory Name", "Size"}
data := make([][]string, 0, len(stats.LargestDirs))
for i, dir := range stats.LargestDirs {
name := filepath.Base(dir.Path)
row := []string{
fmt.Sprintf("%d", i+1),
name,
formatter.FormatSize(dir.Size),
}
data = append(data, row)
}
printSimpleTable(headers, data)
}
// Print file type distribution table
if len(stats.FileExtensions) > 0 {
PrintSectionHeader("FILE TYPE DISTRIBUTION")
type extCount struct {
ext string
count int
}
var sortedExts []extCount
for ext, count := range stats.FileExtensions {
sortedExts = append(sortedExts, extCount{ext, count})
}
sort.Slice(sortedExts, func(i, j int) bool {
return sortedExts[i].count > sortedExts[j].count
})
maxShow := 10
if len(sortedExts) < maxShow {
maxShow = len(sortedExts)
}
headers := []string{"#", "Extension", "File Count", "Percentage"}
data := make([][]string, 0, maxShow)
for i := 0; i < maxShow; i++ {
ext := sortedExts[i].ext
if ext == "" {
ext = "(no extension)"
}
percentage := float64(sortedExts[i].count) / float64(stats.FileCount) * 100
row := []string{
fmt.Sprintf("%d", i+1),
ext,
fmt.Sprintf("%d", sortedExts[i].count),
fmt.Sprintf("%.1f%%", percentage),
}
data = append(data, row)
}
printSimpleTable(headers, data)
}
PrintSectionHeader("DIRECTORY DETAILS")
fmt.Printf("%s%s📅 Last Modified:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, modTime)
fmt.Println()
return nil
}
func printSimpleTable(headers []string, data [][]string) {
formatter.PrintTable(headers, data, colors.Cyan)
fmt.Println()
}
func analyzeDirectory(path string) (*DirStats, error) {
stats := &DirStats{
LargestFiles: make([]FileItem, 0),
LargestDirs: make([]FileItem, 0),
FileExtensions: make(map[string]int),
}
err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filePath == path {
return nil
}
if info.IsDir() {
stats.DirCount++
stats.LargestDirs = appendSorted(stats.LargestDirs, FileItem{
Path: filePath,
Size: info.Size(),
}, 5)
} else {
stats.FileCount++
stats.TotalSize += info.Size()
ext := filepath.Ext(filePath)
stats.FileExtensions[ext]++
stats.LargestFiles = appendSorted(stats.LargestFiles, FileItem{
Path: filePath,
Size: info.Size(),
}, 5)
if info.ModTime().After(stats.LastModified) {
stats.LastModified = info.ModTime()
}
}
return nil
})
return stats, err
}
func appendSorted(items []FileItem, newItem FileItem, maxSize int) []FileItem {
items = append(items, newItem)
sort.Slice(items, func(i, j int) bool {
return items[i].Size > items[j].Size
})
if len(items) > maxSize {
items = items[:maxSize]
}
return items
}