Skip to content

Commit e0f9da9

Browse files
committed
feat: add error colors and refactor size utilities
1 parent a26b382 commit e0f9da9

8 files changed

Lines changed: 128 additions & 71 deletions

File tree

cmd/root.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package cmd
22

33
import (
4+
"fmt"
5+
"os"
6+
47
"github.com/fatih/color"
58
"github.com/spf13/cobra"
69
)
@@ -40,4 +43,10 @@ func init() {
4043
rootCmd.PersistentFlags().IntVar(&days, "days", 14, "Only process node_modules not modified in N days")
4144
rootCmd.PersistentFlags().BoolVar(&includeHidden, "include-hidden", false, "Include hidden directories (starting with .)")
4245
rootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "Minimal output (summary only)")
46+
47+
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
48+
fmt.Fprintf(os.Stderr, "%s %s\n", red("Error:"), err)
49+
_ = cmd.Usage()
50+
return nil
51+
})
4352
}

internal/scanner/scanner.go

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/fatih/color"
1112
"github.com/vukan322/nuke-node_modules/internal/util"
1213
)
1314

15+
var (
16+
yellow = color.New(color.FgYellow).SprintFunc()
17+
red = color.New(color.FgRed).SprintFunc()
18+
)
19+
1420
type Scanner struct {
1521
rootPath string
1622
days int
@@ -45,7 +51,7 @@ func (s *Scanner) Scan() (*ScanResult, error) {
4551
err = filepath.WalkDir(s.rootPath, func(path string, d fs.DirEntry, err error) error {
4652
if err != nil {
4753
if s.verbose {
48-
fmt.Fprintf(os.Stderr, "Warning: cannot access %s: %v\n", path, err)
54+
fmt.Fprintf(os.Stderr, "%s %s: %v\n", yellow("Warning:"), path, err)
4955
}
5056
return nil
5157
}
@@ -56,7 +62,7 @@ func (s *Scanner) Scan() (*ScanResult, error) {
5662

5763
if d.Type()&os.ModeSymlink != 0 {
5864
if s.verbose {
59-
fmt.Fprintf(os.Stderr, "Skipping symlink: %s\n", path)
65+
fmt.Fprintf(os.Stderr, "%s %s\n", yellow("Skipping symlink:"), path)
6066
}
6167
return fs.SkipDir
6268
}
@@ -71,21 +77,21 @@ func (s *Scanner) Scan() (*ScanResult, error) {
7177
info, err := d.Info()
7278
if err != nil {
7379
if s.verbose {
74-
fmt.Fprintf(os.Stderr, "Warning: cannot get info for %s: %v\n", path, err)
80+
fmt.Fprintf(os.Stderr, "%s cannot get info for %s: %v\n", yellow("Warning:"), path, err)
7581
}
7682
return nil
7783
}
7884

7985
if getDevice(info) != s.rootDev {
8086
if s.verbose {
81-
fmt.Fprintf(os.Stderr, "Skipping different filesystem: %s\n", path)
87+
fmt.Fprintf(os.Stderr, "%s %s\n", yellow("Skipping different filesystem:"), path)
8288
}
8389
return fs.SkipDir
8490
}
8591

8692
if d.Name() == "node_modules" {
8793
if info.ModTime().Before(s.cutoffTime) {
88-
size := calculateSize(path)
94+
size := util.CalculateSize(path)
8995
folder := FolderInfo{
9096
Path: path,
9197
Size: size,
@@ -133,7 +139,7 @@ func (s *Scanner) Delete(result *ScanResult) (*ScanResult, error) {
133139
if err != nil {
134140
failures = append(failures, fmt.Sprintf("%s: %v", folder.Path, err))
135141
if s.verbose {
136-
fmt.Fprintf(os.Stderr, "Failed to delete %s: %v\n", folder.Path, err)
142+
fmt.Fprintf(os.Stderr, "%s %s: %v\n", red("Failed to delete"), folder.Path, err)
137143
}
138144
continue
139145
}
@@ -153,20 +159,3 @@ func (s *Scanner) Delete(result *ScanResult) (*ScanResult, error) {
153159

154160
return deleted, nil
155161
}
156-
157-
func calculateSize(path string) int64 {
158-
var size int64
159-
_ = filepath.WalkDir(path, func(_ string, d fs.DirEntry, err error) error {
160-
if err != nil {
161-
return nil
162-
}
163-
if !d.IsDir() {
164-
info, err := d.Info()
165-
if err == nil {
166-
size += info.Size()
167-
}
168-
}
169-
return nil
170-
})
171-
return size
172-
}

internal/scanner/scanner_test.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,30 @@ func TestScan_MultipleNodeModules(t *testing.T) {
161161
project1 := filepath.Join(tmpDir, "project1")
162162
project2 := filepath.Join(tmpDir, "project2")
163163

164-
os.Mkdir(project1, 0755)
165-
os.Mkdir(project2, 0755)
164+
if err := os.Mkdir(project1, 0755); err != nil {
165+
t.Fatal(err)
166+
}
167+
if err := os.Mkdir(project2, 0755); err != nil {
168+
t.Fatal(err)
169+
}
166170

167171
nm1 := filepath.Join(project1, "node_modules")
168172
nm2 := filepath.Join(project2, "node_modules")
169173

170-
os.Mkdir(nm1, 0755)
171-
os.Mkdir(nm2, 0755)
174+
if err := os.Mkdir(nm1, 0755); err != nil {
175+
t.Fatal(err)
176+
}
177+
if err := os.Mkdir(nm2, 0755); err != nil {
178+
t.Fatal(err)
179+
}
172180

173181
oldTime := time.Now().AddDate(0, 0, -30)
174-
os.Chtimes(nm1, oldTime, oldTime)
175-
os.Chtimes(nm2, oldTime, oldTime)
182+
if err := os.Chtimes(nm1, oldTime, oldTime); err != nil {
183+
t.Fatal(err)
184+
}
185+
if err := os.Chtimes(nm2, oldTime, oldTime); err != nil {
186+
t.Fatal(err)
187+
}
176188

177189
s := New(tmpDir, 14, false, false)
178190
result, err := s.Scan()
@@ -200,7 +212,9 @@ func TestDelete_RemovesNodeModules(t *testing.T) {
200212
}
201213

202214
oldTime := time.Now().AddDate(0, 0, -30)
203-
os.Chtimes(nmPath, oldTime, oldTime)
215+
if err := os.Chtimes(nmPath, oldTime, oldTime); err != nil {
216+
t.Fatal(err)
217+
}
204218

205219
s := New(tmpDir, 0, false, false)
206220
scanResult, _ := s.Scan()

internal/ui/output.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
var (
1212
green = color.New(color.FgGreen).SprintFunc()
13-
red = color.New(color.FgRed).SprintFunc()
1413
yellow = color.New(color.FgYellow).SprintFunc()
1514
cyan = color.New(color.FgCyan).SprintFunc()
1615
)

internal/util/format.go

Lines changed: 0 additions & 16 deletions
This file was deleted.

internal/util/format_test.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

internal/util/size.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package util
2+
3+
import (
4+
"fmt"
5+
"io/fs"
6+
"path/filepath"
7+
)
8+
9+
func FormatSize(bytes int64) string {
10+
const unit = 1024
11+
if bytes < unit {
12+
return fmt.Sprintf("%d B", bytes)
13+
}
14+
div, exp := int64(unit), 0
15+
for n := bytes / unit; n >= unit; n /= unit {
16+
div *= unit
17+
exp++
18+
}
19+
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
20+
}
21+
22+
func CalculateSize(path string) int64 {
23+
var size int64
24+
_ = filepath.WalkDir(path, func(_ string, d fs.DirEntry, err error) error {
25+
if err != nil {
26+
return nil
27+
}
28+
if !d.IsDir() {
29+
info, err := d.Info()
30+
if err == nil {
31+
size += info.Size()
32+
}
33+
}
34+
return nil
35+
})
36+
return size
37+
}

internal/util/size_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package util
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestFormatSize(t *testing.T) {
10+
tests := []struct {
11+
bytes int64
12+
expected string
13+
}{
14+
{0, "0 B"},
15+
{1023, "1023 B"},
16+
{1024, "1.0 KB"},
17+
{1536, "1.5 KB"},
18+
{1048576, "1.0 MB"},
19+
{1073741824, "1.0 GB"},
20+
}
21+
22+
for _, tt := range tests {
23+
result := FormatSize(tt.bytes)
24+
if result != tt.expected {
25+
t.Errorf("FormatSize(%d) = %s, want %s", tt.bytes, result, tt.expected)
26+
}
27+
}
28+
}
29+
30+
func TestCalculateSize(t *testing.T) {
31+
tmpDir := t.TempDir()
32+
33+
file1 := filepath.Join(tmpDir, "file1.txt")
34+
if err := os.WriteFile(file1, make([]byte, 1024), 0644); err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
file2 := filepath.Join(tmpDir, "file2.txt")
39+
if err := os.WriteFile(file2, make([]byte, 512), 0644); err != nil {
40+
t.Fatal(err)
41+
}
42+
43+
size := CalculateSize(tmpDir)
44+
expected := int64(1024 + 512)
45+
46+
if size != expected {
47+
t.Errorf("CalculateSize() = %d, want %d", size, expected)
48+
}
49+
}

0 commit comments

Comments
 (0)