Skip to content

Commit

Permalink
Merge pull request #2 from frogfreg/cache
Browse files Browse the repository at this point in the history
Add caching for explored directories
  • Loading branch information
frogfreg authored Oct 16, 2024
2 parents 5b8de7a + 4522ac8 commit 5691d6e
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 52 deletions.
101 changes: 101 additions & 0 deletions fileinfo/fileInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fileinfo

import (
"errors"
"io/fs"
"os"
"path/filepath"
"slices"
Expand All @@ -16,6 +17,8 @@ type FileInfo struct {
Name string
FileType string
Size int
Children []string
Checked bool
}

var errBadDescriptor = errors.New("bad file descriptor")
Expand Down Expand Up @@ -126,3 +129,101 @@ func GetRootInfo(root string) ([]FileInfo, error) {

return infoList, err
}

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(m, root)

return m, 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 {
if d.IsDir() {
return filepath.SkipDir
}
return err
}

parent := filepath.Dir(path)
if fi, exists := m[parent]; exists {
if !fi.Checked {
fi.Children = append(fi.Children, path)
m[parent] = fi
}
}

if _, exists := m[path]; exists {
return filepath.SkipDir
}

if d.IsDir() {
m[path] = FileInfo{Name: path, FileType: "dir", Size: 0}
return nil
}

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
}
m[path] = FileInfo{Name: path, FileType: "file", Size: int(info.Size())}
return nil
}
}

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
}

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
}

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
}
}
78 changes: 78 additions & 0 deletions fileinfo/fileInfo_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fileinfo

import (
"reflect"
"slices"
"testing"
)
Expand All @@ -22,3 +23,80 @@ 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(nil, "testfiles")
if err != nil {
t.Error(err)
}
expected := 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},
"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) {
t.Errorf("expected %v, but got %v", expected, m)
}
}

func TestGetSortedDirs(t *testing.T) {
m, err := GenerateFileMap(nil, "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 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")
if err != nil {
b.Error(err)
}
}
}

func BenchmarkGetSortedDirs(b *testing.B) {
for range b.N {
m, err := GenerateFileMap(nil, "testfiles")
if err != nil {
b.Error(err)
}

_ = GetSortedDirs(m, "testfiles")
}
}
24 changes: 12 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
55 changes: 29 additions & 26 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Loading

0 comments on commit 5691d6e

Please sign in to comment.