- 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)
203 lines
4.5 KiB
Go
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
|
|
}
|