From 92f684235b2dba5e28a901284678b828df756ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Tue, 8 Oct 2024 20:17:47 -0600 Subject: [PATCH 01/11] still figuring it out --- fileinfo/fileInfo.go | 39 +++++++++++++++++++++++++++++++++++++++ fileinfo/fileInfo_test.go | 12 ++++++++++++ main.go | 4 +--- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index ab7608e..b7b3ede 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -2,6 +2,7 @@ package fileinfo import ( "errors" + "io/fs" "os" "path/filepath" "slices" @@ -16,6 +17,7 @@ type FileInfo struct { Name string FileType string Size int + Children []string } var errBadDescriptor = errors.New("bad file descriptor") @@ -126,3 +128,40 @@ func GetRootInfo(root string) ([]FileInfo, error) { return infoList, err } + +func GenerateFileMap(root string) (map[string]FileInfo, error) { + fileMap := map[string]FileInfo{root: {Name: root, FileType: "dir", Size: 0}} + f := getMapFillerFunc(fileMap) + + if walkErr := filepath.WalkDir(root, f); walkErr != nil { + return nil, walkErr + } + + return fileMap, nil +} + +func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, err error) error { + return func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + parent := filepath.Dir(path) + if fi, exists := m[parent]; exists { + fi.Children = append(fi.Children, path) + m[parent] = fi + } else { + m[parent] = FileInfo{Name: parent, FileType: "dir", Size: 0, Children: []string{path}} + } + + if d.IsDir() { + m[path] = FileInfo{Name: path, FileType: "dir", Size: 0} + } else { + info, err := d.Info() + if err != nil { + return err + } + m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())} + } + return nil + } +} diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index 8ee98f2..c2bdbb2 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -22,3 +22,15 @@ func TestGetRootInfo(t *testing.T) { t.Errorf("slices are not equal. Expected %v, but got %v", expected, infoList) } } + +func TestGenerateFileMap(t *testing.T) { + m, err := GenerateFileMap("testfiles") + if err != nil { + t.Error(err) + } + for k, v := range m { + t.Logf("%v = %#v\n", k, v) + } + + t.Error("erring on purpose") +} diff --git a/main.go b/main.go index ac6e383..56a7361 100644 --- a/main.go +++ b/main.go @@ -140,7 +140,7 @@ func deleteCmd(dir string) tea.Cmd { } func getFileInfoCmd(dir string) tea.Cmd { - f := func() tea.Msg { + return func() tea.Msg { var res fileInfoResponse data, err := fileinfo.GetRootInfo(dir) if err != nil { @@ -151,8 +151,6 @@ func getFileInfoCmd(dir string) tea.Cmd { res.data = data return res } - - return f } func getInitialTable() table.Model { From 98f48ac12ffbef16bceead2fc2dac1e06898b630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 01:07:26 -0600 Subject: [PATCH 02/11] functions for cache seem to be working --- fileinfo/fileInfo.go | 28 ++++++++++++++++++++ fileinfo/fileInfo_test.go | 54 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index b7b3ede..00a3512 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -137,6 +137,8 @@ func GenerateFileMap(root string) (map[string]FileInfo, error) { return nil, walkErr } + updateDirSizes(fileMap, root) + return fileMap, nil } @@ -165,3 +167,29 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er return nil } } + +func updateDirSizes(m map[string]FileInfo, root string) { + fi := m[root] + if fi.FileType == "file" { + return + } + for _, c := range fi.Children { + updateDirSizes(m, c) + fi.Size += m[c].Size + } + m[root] = fi +} + +func GetSortedDirs(m map[string]FileInfo, root string) []FileInfo { + list := []FileInfo{} + + for _, c := range m[root].Children { + list = append(list, m[c]) + } + + slices.SortFunc(list, func(a, b FileInfo) int { + return b.Size - a.Size + }) + + return list +} diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index c2bdbb2..36353c2 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -1,6 +1,7 @@ package fileinfo import ( + "reflect" "slices" "testing" ) @@ -28,9 +29,56 @@ func TestGenerateFileMap(t *testing.T) { if err != nil { t.Error(err) } - for k, v := range m { - t.Logf("%v = %#v\n", k, v) + expected := map[string]FileInfo{ + ".": {Name: ".", FileType: "dir", Size: 0, Children: []string{"testfiles"}}, + "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/dir1", "testfiles/file1.txt", "testfiles/file2.txt"}}, + "testfiles/dir1": {Name: "testfiles/dir1", FileType: "dir", Size: 10, Children: []string{"testfiles/dir1/dir2", "testfiles/dir1/file3.txt"}}, + "testfiles/dir1/dir2": {Name: "testfiles/dir1/dir2", FileType: "dir", Size: 7, Children: []string{"testfiles/dir1/dir2/file4.txt"}}, + "testfiles/dir1/dir2/file4.txt": {Name: "testfiles/dir1/dir2/file4.txt", FileType: "file", Size: 7, Children: []string(nil)}, + "testfiles/dir1/file3.txt": {Name: "testfiles/dir1/file3.txt", FileType: "file", Size: 3, Children: []string(nil)}, + "testfiles/file1.txt": {Name: "testfiles/file1.txt", FileType: "file", Size: 1, Children: []string(nil)}, + "testfiles/file2.txt": {Name: "testfiles/file2.txt", FileType: "file", Size: 2, Children: []string(nil)}, } - t.Error("erring on purpose") + if !reflect.DeepEqual(m, expected) { + t.Errorf("expected %v, but got %v", expected, m) + } +} + +func TestGetSortedDirs(t *testing.T) { + m, err := GenerateFileMap("testfiles") + if err != nil { + t.Error(err) + } + + list := GetSortedDirs(m, "testfiles") + expected := []FileInfo{{Name: "testfiles/dir1", FileType: "dir", Size: 10}, {Name: "testfiles/file2.txt", FileType: "file", Size: 2}, {Name: "testfiles/file1.txt", FileType: "file", Size: 1}} + + if slices.CompareFunc(expected, list, func(a, b FileInfo) int { + if a.FileType != b.FileType || a.Name != b.Name || a.Size != b.Size { + return -1 + } + return 0 + }) != 0 { + t.Errorf("slices are not equal. Expected %v, but got %v", expected, list) + } +} + +func BenchmarkGetRootInfo(b *testing.B) { + for range b.N { + _, err := GetRootInfo("testfiles") + if err != nil { + b.Error(err) + } + } +} +func BenchmarkGetSortedDirs(b *testing.B) { + for range b.N { + m, err := GenerateFileMap("testfiles") + if err != nil { + b.Error(err) + } + + _ = GetSortedDirs(m, "testfiles") + } } From a2e62bc319cbc156df7dbee5485037c6d60dc8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 14:46:03 -0600 Subject: [PATCH 03/11] updated to reuse map --- fileinfo/fileInfo.go | 22 +++++++++++++++++----- fileinfo/fileInfo_test.go | 23 ++++++++++++----------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index 00a3512..f6e0665 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -18,6 +18,7 @@ type FileInfo struct { FileType string Size int Children []string + Checked bool } var errBadDescriptor = errors.New("bad file descriptor") @@ -129,17 +130,19 @@ func GetRootInfo(root string) ([]FileInfo, error) { return infoList, err } -func GenerateFileMap(root string) (map[string]FileInfo, error) { - fileMap := map[string]FileInfo{root: {Name: root, FileType: "dir", Size: 0}} - f := getMapFillerFunc(fileMap) +func GenerateFileMap(m map[string]FileInfo, root string) (map[string]FileInfo, error) { + if m == nil { + m = map[string]FileInfo{} + } + f := getMapFillerFunc(m) if walkErr := filepath.WalkDir(root, f); walkErr != nil { return nil, walkErr } - updateDirSizes(fileMap, root) + updateDirSizes(m, root) - return fileMap, nil + return m, nil } func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, err error) error { @@ -155,6 +158,10 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er m[parent] = FileInfo{Name: parent, FileType: "dir", Size: 0, Children: []string{path}} } + if fi, exists := m[path]; exists && fi.Checked { + return nil + } + if d.IsDir() { m[path] = FileInfo{Name: path, FileType: "dir", Size: 0} } else { @@ -171,12 +178,17 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er func updateDirSizes(m map[string]FileInfo, root string) { fi := m[root] if fi.FileType == "file" { + fi.Checked = true + m[root] = fi + } + if fi.Checked { return } for _, c := range fi.Children { updateDirSizes(m, c) fi.Size += m[c].Size } + fi.Checked = true m[root] = fi } diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index 36353c2..bb6c5b1 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -25,19 +25,19 @@ func TestGetRootInfo(t *testing.T) { } func TestGenerateFileMap(t *testing.T) { - m, err := GenerateFileMap("testfiles") + m, err := GenerateFileMap(nil, "testfiles") if err != nil { t.Error(err) } expected := map[string]FileInfo{ - ".": {Name: ".", FileType: "dir", Size: 0, Children: []string{"testfiles"}}, - "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/dir1", "testfiles/file1.txt", "testfiles/file2.txt"}}, - "testfiles/dir1": {Name: "testfiles/dir1", FileType: "dir", Size: 10, Children: []string{"testfiles/dir1/dir2", "testfiles/dir1/file3.txt"}}, - "testfiles/dir1/dir2": {Name: "testfiles/dir1/dir2", FileType: "dir", Size: 7, Children: []string{"testfiles/dir1/dir2/file4.txt"}}, - "testfiles/dir1/dir2/file4.txt": {Name: "testfiles/dir1/dir2/file4.txt", FileType: "file", Size: 7, Children: []string(nil)}, - "testfiles/dir1/file3.txt": {Name: "testfiles/dir1/file3.txt", FileType: "file", Size: 3, Children: []string(nil)}, - "testfiles/file1.txt": {Name: "testfiles/file1.txt", FileType: "file", Size: 1, Children: []string(nil)}, - "testfiles/file2.txt": {Name: "testfiles/file2.txt", FileType: "file", Size: 2, Children: []string(nil)}, + ".": {Name: ".", FileType: "dir", Size: 0, Children: []string{"testfiles"}, Checked: false}, + "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/dir1", "testfiles/file1.txt", "testfiles/file2.txt"}, Checked: true}, + "testfiles/dir1": {Name: "testfiles/dir1", FileType: "dir", Size: 10, Children: []string{"testfiles/dir1/dir2", "testfiles/dir1/file3.txt"}, Checked: true}, + "testfiles/dir1/dir2": {Name: "testfiles/dir1/dir2", FileType: "dir", Size: 7, Children: []string{"testfiles/dir1/dir2/file4.txt"}, Checked: true}, + "testfiles/dir1/dir2/file4.txt": {Name: "testfiles/dir1/dir2/file4.txt", FileType: "file", Size: 7, Children: []string(nil), Checked: true}, + "testfiles/dir1/file3.txt": {Name: "testfiles/dir1/file3.txt", FileType: "file", Size: 3, Children: []string(nil), Checked: true}, + "testfiles/file1.txt": {Name: "testfiles/file1.txt", FileType: "file", Size: 1, Children: []string(nil), Checked: true}, + "testfiles/file2.txt": {Name: "testfiles/file2.txt", FileType: "file", Size: 2, Children: []string(nil), Checked: true}, } if !reflect.DeepEqual(m, expected) { @@ -46,7 +46,7 @@ func TestGenerateFileMap(t *testing.T) { } func TestGetSortedDirs(t *testing.T) { - m, err := GenerateFileMap("testfiles") + m, err := GenerateFileMap(nil, "testfiles") if err != nil { t.Error(err) } @@ -72,9 +72,10 @@ func BenchmarkGetRootInfo(b *testing.B) { } } } + func BenchmarkGetSortedDirs(b *testing.B) { for range b.N { - m, err := GenerateFileMap("testfiles") + m, err := GenerateFileMap(nil, "testfiles") if err != nil { b.Error(err) } From 5c6b94b6e5f618c3b3de544d7a7ba9c56c2dc2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 18:03:51 -0600 Subject: [PATCH 04/11] updated main.go to use new functions --- fileinfo/fileInfo.go | 13 ++++++++++++- fileinfo/fileInfo_test.go | 19 +++++++++++++++++++ main.go | 35 +++++++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index f6e0665..b69829f 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -151,7 +151,7 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er return err } parent := filepath.Dir(path) - if fi, exists := m[parent]; exists { + if fi, exists := m[parent]; exists && !fi.Checked { fi.Children = append(fi.Children, path) m[parent] = fi } else { @@ -205,3 +205,14 @@ func GetSortedDirs(m map[string]FileInfo, root string) []FileInfo { return list } + +func CleanChildren(m map[string]FileInfo, dir string) { + delete(m, dir) + + for path, fi := range m { + fi.Children = slices.DeleteFunc(fi.Children, func(item string) bool { + return item == dir + }) + m[path] = fi + } +} diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index bb6c5b1..66c18f3 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -64,6 +64,25 @@ func TestGetSortedDirs(t *testing.T) { } } +func TestCleanChildren(t *testing.T) { + m := map[string]FileInfo{ + "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/dir1", "testfiles/file1.txt", "testfiles/file2.txt"}, Checked: true}, + "testfiles/dir1": {Name: "testfiles/dir1", FileType: "dir", Size: 10, Children: []string{"testfiles/dir1/dir2", "testfiles/dir1/file3.txt"}, Checked: true}, + } + + expected := map[string]FileInfo{ + "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/file1.txt", "testfiles/file2.txt"}, Checked: true}, + } + + CleanChildren(m, "testfiles/dir1") + t.Log(m) + + if !reflect.DeepEqual(expected, m) { + t.Errorf("expected %v, got %v", expected, m) + } + +} + func BenchmarkGetRootInfo(b *testing.B) { for range b.N { _, err := GetRootInfo("testfiles") diff --git a/main.go b/main.go index 56a7361..5c9a124 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( type model struct { currentDir string + fileMap map[string]fileinfo.FileInfo loading bool deleting bool deleteDir string @@ -23,8 +24,9 @@ type model struct { } type fileInfoResponse struct { - data []fileinfo.FileInfo - err error + data []fileinfo.FileInfo + fileMap map[string]fileinfo.FileInfo + err error } type deleteResponse struct { @@ -36,10 +38,11 @@ var baseStyle = lipgloss.NewStyle(). BorderForeground(lipgloss.Color("240")) func (m model) Init() tea.Cmd { - return getFileInfoCmd(m.currentDir) + return getFileInfoCmd(nil, m.currentDir) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + log.Printf("current dir: %v, fileMap: %#v", m.currentDir, m.fileMap) switch msg := msg.(type) { case fileInfoResponse: m.loading = false @@ -48,6 +51,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.err = fir.err log.Fatalf("something went wrong: %v", m.err) } + m.fileMap = fir.fileMap m.table.SetRows(fileinfo.FileInfosToRow(fir.data)) case deleteResponse: @@ -59,7 +63,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.loading = true - return m, getFileInfoCmd(m.currentDir) + return m, getFileInfoCmd(m.fileMap, m.currentDir) case tea.KeyMsg: switch msg.String() { @@ -69,12 +73,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { sr := m.table.SelectedRow() if !m.loading && !m.deleting && len(sr) != 0 && sr[1] == "dir" { m = m.updateCurrentDir(m.table.SelectedRow()[0], false) - return m, getFileInfoCmd(m.currentDir) + return m, getFileInfoCmd(m.fileMap, m.currentDir) } case "left", "h", "backspace": if !m.loading && !m.deleting { m = m.updateCurrentDir(filepath.Dir(m.currentDir), true) - return m, getFileInfoCmd(m.currentDir) + return m, getFileInfoCmd(m.fileMap, m.currentDir) } case "d": sr := m.table.SelectedRow() @@ -82,7 +86,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.loading && !m.deleting && len(sr) != 0 { m.deleting = true m.deleteDir = filepath.Join(m.currentDir, sr[0]) - return m, deleteCmd(m.deleteDir) + return m, deleteCmd(m.fileMap, m.deleteDir) } } @@ -124,7 +128,7 @@ func (m model) updateCurrentDir(dir string, replace bool) model { return m } -func deleteCmd(dir string) tea.Cmd { +func deleteCmd(m map[string]fileinfo.FileInfo, dir string) tea.Cmd { f := func() tea.Msg { var res deleteResponse @@ -133,20 +137,24 @@ func deleteCmd(dir string) tea.Cmd { res.err = err } + fileinfo.CleanChildren(m, dir) + return res } return f } -func getFileInfoCmd(dir string) tea.Cmd { +func getFileInfoCmd(m map[string]fileinfo.FileInfo, dir string) tea.Cmd { return func() tea.Msg { var res fileInfoResponse - data, err := fileinfo.GetRootInfo(dir) + fm, err := fileinfo.GenerateFileMap(m, dir) if err != nil { res.err = err return res } + data := fileinfo.GetSortedDirs(fm, dir) + res.fileMap = fm res.data = data return res @@ -194,6 +202,13 @@ func main() { table: getInitialTable(), } + f, err := tea.LogToFile("debug.log", "debug") + if err != nil { + fmt.Println("fatal:", err) + os.Exit(1) + } + defer f.Close() + p := tea.NewProgram(m) if _, err := p.Run(); err != nil { log.Fatalf("something went wrong %v\n", err) From 3ca1e6fc6967536643407550c45f3ddaf5623d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 18:43:40 -0600 Subject: [PATCH 05/11] updated to skip dirs and files without permission --- fileinfo/fileInfo.go | 6 ++++++ main.go | 13 ++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index b69829f..a262fea 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -148,6 +148,9 @@ func GenerateFileMap(m map[string]FileInfo, root string) (map[string]FileInfo, e func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, err error) error { return func(path string, d fs.DirEntry, err error) error { if err != nil { + if errors.Is(err, fs.ErrPermission) && d.IsDir() { + return filepath.SkipDir + } return err } parent := filepath.Dir(path) @@ -167,6 +170,9 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er } else { info, err := d.Info() if err != nil { + if errors.Is(err, fs.ErrPermission) { + return nil + } return err } m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())} diff --git a/main.go b/main.go index 5c9a124..213eb4a 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,6 @@ func (m model) Init() tea.Cmd { } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - log.Printf("current dir: %v, fileMap: %#v", m.currentDir, m.fileMap) switch msg := msg.(type) { case fileInfoResponse: m.loading = false @@ -202,12 +201,12 @@ func main() { table: getInitialTable(), } - f, err := tea.LogToFile("debug.log", "debug") - if err != nil { - fmt.Println("fatal:", err) - os.Exit(1) - } - defer f.Close() + // f, err := tea.LogToFile("debug.log", "debug") + // if err != nil { + // fmt.Println("fatal:", err) + // os.Exit(1) + // } + // defer f.Close() p := tea.NewProgram(m) if _, err := p.Run(); err != nil { From 5db075dae73132bfc78fea814f9a841a6e7e8fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 18:55:49 -0600 Subject: [PATCH 06/11] updated readme --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index b19bdbd..cce6588 100644 --- a/readme.md +++ b/readme.md @@ -6,5 +6,6 @@ A simple CLI tool to view and delete files and directories ## TODO -- add error message +- better handling of possible errors - add toggle to show/hide controls +- function to reload cache From de6fa73fca6b34eec51869cb26844920b81f2f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 19:59:56 -0600 Subject: [PATCH 07/11] exclude files or directories with non existent or invalid argument errors --- fileinfo/fileInfo.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index a262fea..969ea18 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -148,7 +148,7 @@ func GenerateFileMap(m map[string]FileInfo, root string) (map[string]FileInfo, e func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, err error) error { return func(path string, d fs.DirEntry, err error) error { if err != nil { - if errors.Is(err, fs.ErrPermission) && d.IsDir() { + if d.IsDir() { return filepath.SkipDir } return err @@ -173,6 +173,9 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er if errors.Is(err, fs.ErrPermission) { return nil } + if errors.Is(err, fs.ErrNotExist) { + return nil + } return err } m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())} From b1ee5b56ea8710edae07b2ec7391799745598be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 9 Oct 2024 22:52:12 -0600 Subject: [PATCH 08/11] cache seems to be working todo: more test --- fileinfo/fileInfo.go | 11 +++++++---- fileinfo/fileInfo_test.go | 1 - main.go | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index 969ea18..6581105 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -153,16 +153,19 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er } return err } + parent := filepath.Dir(path) - if fi, exists := m[parent]; exists && !fi.Checked { - fi.Children = append(fi.Children, path) - m[parent] = fi + if fi, exists := m[parent]; exists { + if !fi.Checked { + fi.Children = append(fi.Children, path) + m[parent] = fi + } } else { m[parent] = FileInfo{Name: parent, FileType: "dir", Size: 0, Children: []string{path}} } if fi, exists := m[path]; exists && fi.Checked { - return nil + return filepath.SkipDir } if d.IsDir() { diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index 66c18f3..5846f91 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -80,7 +80,6 @@ func TestCleanChildren(t *testing.T) { if !reflect.DeepEqual(expected, m) { t.Errorf("expected %v, got %v", expected, m) } - } func BenchmarkGetRootInfo(b *testing.B) { diff --git a/main.go b/main.go index 213eb4a..2a30458 100644 --- a/main.go +++ b/main.go @@ -201,7 +201,7 @@ func main() { table: getInitialTable(), } - // f, err := tea.LogToFile("debug.log", "debug") + // f, err := tea.LogToFile("/home/nyan/Desktop/experiments/godu/debug.log", "debug") // if err != nil { // fmt.Println("fatal:", err) // os.Exit(1) From c88833fb74cb07f402438e36716fbbe72ea7becf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Thu, 10 Oct 2024 01:02:37 -0600 Subject: [PATCH 09/11] minor improvements for legibility --- fileinfo/fileInfo.go | 27 +++++++++++++-------------- fileinfo/fileInfo_test.go | 1 - 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/fileinfo/fileInfo.go b/fileinfo/fileInfo.go index 6581105..a5baad9 100644 --- a/fileinfo/fileInfo.go +++ b/fileinfo/fileInfo.go @@ -160,29 +160,28 @@ func getMapFillerFunc(m map[string]FileInfo) func(path string, d fs.DirEntry, er fi.Children = append(fi.Children, path) m[parent] = fi } - } else { - m[parent] = FileInfo{Name: parent, FileType: "dir", Size: 0, Children: []string{path}} } - if fi, exists := m[path]; exists && fi.Checked { + if _, exists := m[path]; exists { return filepath.SkipDir } if d.IsDir() { m[path] = FileInfo{Name: path, FileType: "dir", Size: 0} - } else { - info, err := d.Info() - if err != nil { - if errors.Is(err, fs.ErrPermission) { - return nil - } - if errors.Is(err, fs.ErrNotExist) { - return nil - } - return err + return nil + } + + info, err := d.Info() + if err != nil { + if errors.Is(err, fs.ErrPermission) { + return nil } - m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())} + if errors.Is(err, fs.ErrNotExist) { + return nil + } + return err } + m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())} return nil } } diff --git a/fileinfo/fileInfo_test.go b/fileinfo/fileInfo_test.go index 5846f91..837deb7 100644 --- a/fileinfo/fileInfo_test.go +++ b/fileinfo/fileInfo_test.go @@ -30,7 +30,6 @@ func TestGenerateFileMap(t *testing.T) { t.Error(err) } expected := map[string]FileInfo{ - ".": {Name: ".", FileType: "dir", Size: 0, Children: []string{"testfiles"}, Checked: false}, "testfiles": {Name: "testfiles", FileType: "dir", Size: 13, Children: []string{"testfiles/dir1", "testfiles/file1.txt", "testfiles/file2.txt"}, Checked: true}, "testfiles/dir1": {Name: "testfiles/dir1", FileType: "dir", Size: 10, Children: []string{"testfiles/dir1/dir2", "testfiles/dir1/file3.txt"}, Checked: true}, "testfiles/dir1/dir2": {Name: "testfiles/dir1/dir2", FileType: "dir", Size: 7, Children: []string{"testfiles/dir1/dir2/file4.txt"}, Checked: true}, From 6f3125590532f6b6f7fdef6e46ed3ec6a76c83bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 16 Oct 2024 14:09:54 -0600 Subject: [PATCH 10/11] updated dependencies --- go.mod | 24 ++++++++++++------------ go.sum | 55 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index d938520..a4fe31c 100644 --- a/go.mod +++ b/go.mod @@ -3,25 +3,25 @@ module github.com/frogfreg/godu go 1.23.2 require ( - github.com/charmbracelet/bubbles v0.16.1 - github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.7.1 + github.com/charmbracelet/bubbles v0.20.0 + github.com/charmbracelet/bubbletea v1.1.1 + github.com/charmbracelet/lipgloss v0.13.0 ) require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.6.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.3.8 // indirect ) diff --git a/go.sum b/go.sum index 1f101c1..395e492 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,43 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= -github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= -github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= -github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= -github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= -github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= +github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= +github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= +github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= From 4522ac84fe49a1b12e527de207c8f0e0af48b534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Fern=C3=A1ndez?= Date: Wed, 16 Oct 2024 14:12:41 -0600 Subject: [PATCH 11/11] updated readme todo --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index cce6588..2ecf81a 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,8 @@ A simple CLI tool to view and delete files and directories ## TODO +- cleanup of unused functions - better handling of possible errors - add toggle to show/hide controls +- possible optimizations - function to reload cache