diff options
-rw-r--r-- | cmd/gdu/app/app.go | 4 | ||||
-rw-r--r-- | internal/common/analyze.go | 21 | ||||
-rw-r--r-- | internal/common/ignore.go | 4 | ||||
-rw-r--r-- | internal/common/ui.go | 4 | ||||
-rw-r--r-- | internal/testanalyze/analyze.go | 12 | ||||
-rw-r--r-- | pkg/analyze/dir.go | 48 | ||||
-rw-r--r-- | pkg/analyze/dir_test.go | 23 | ||||
-rw-r--r-- | pkg/analyze/encode_test.go | 5 | ||||
-rw-r--r-- | pkg/analyze/file.go | 177 | ||||
-rw-r--r-- | pkg/analyze/file_test.go | 29 | ||||
-rw-r--r-- | pkg/analyze/sort_test.go | 25 | ||||
-rw-r--r-- | pkg/fs/file.go | 104 | ||||
-rw-r--r-- | report/export.go | 11 | ||||
-rw-r--r-- | report/import.go | 4 | ||||
-rw-r--r-- | stdout/stdout.go | 21 | ||||
-rw-r--r-- | tui/actions.go | 25 | ||||
-rw-r--r-- | tui/actions_test.go | 29 | ||||
-rw-r--r-- | tui/format.go | 4 | ||||
-rw-r--r-- | tui/keys.go | 6 | ||||
-rw-r--r-- | tui/keys_test.go | 47 | ||||
-rw-r--r-- | tui/progress.go | 3 | ||||
-rw-r--r-- | tui/show.go | 6 | ||||
-rw-r--r-- | tui/sort.go | 22 | ||||
-rw-r--r-- | tui/tui.go | 23 | ||||
-rw-r--r-- | tui/tui_test.go | 11 |
25 files changed, 363 insertions, 305 deletions
diff --git a/cmd/gdu/app/app.go b/cmd/gdu/app/app.go index f5a747b..7e67732 100644 --- a/cmd/gdu/app/app.go +++ b/cmd/gdu/app/app.go @@ -16,8 +16,8 @@ import ( "github.com/dundee/gdu/v5/build" "github.com/dundee/gdu/v5/internal/common" - "github.com/dundee/gdu/v5/pkg/analyze" "github.com/dundee/gdu/v5/pkg/device" + gfs "github.com/dundee/gdu/v5/pkg/fs" "github.com/dundee/gdu/v5/report" "github.com/dundee/gdu/v5/stdout" "github.com/dundee/gdu/v5/tui" @@ -28,7 +28,7 @@ import ( // UI is common interface for both terminal UI and text output type UI interface { ListDevices(getter device.DevicesInfoGetter) error - AnalyzePath(path string, parentDir *analyze.Dir) error + AnalyzePath(path string, parentDir gfs.Item) error ReadAnalysis(input io.Reader) error SetIgnoreDirPaths(paths []string) SetIgnoreDirPatterns(paths []string) error diff --git a/internal/common/analyze.go b/internal/common/analyze.go new file mode 100644 index 0000000..0894c46 --- /dev/null +++ b/internal/common/analyze.go @@ -0,0 +1,21 @@ +package common + +import "github.com/dundee/gdu/v5/pkg/fs" + +// CurrentProgress struct +type CurrentProgress struct { + CurrentItemName string + ItemCount int + TotalSize int64 +} + +// ShouldDirBeIgnored whether path should be ignored +type ShouldDirBeIgnored func(name, path string) bool + +// Analyzer is type for dir analyzing function +type Analyzer interface { + AnalyzeDir(path string, ignore ShouldDirBeIgnored) fs.Item + GetProgressChan() chan CurrentProgress + GetDoneChan() chan struct{} + ResetProgress() +} diff --git a/internal/common/ignore.go b/internal/common/ignore.go index f09d1a5..2ae1458 100644 --- a/internal/common/ignore.go +++ b/internal/common/ignore.go @@ -7,8 +7,6 @@ import ( "strings" log "github.com/sirupsen/logrus" - - "github.com/dundee/gdu/v5/pkg/analyze" ) // CreateIgnorePattern creates one pattern from all path patterns @@ -103,7 +101,7 @@ func (ui *UI) IsHiddenDir(name, path string) bool { } // CreateIgnoreFunc returns function for detecting if dir should be ignored -func (ui *UI) CreateIgnoreFunc() analyze.ShouldDirBeIgnored { +func (ui *UI) CreateIgnoreFunc() ShouldDirBeIgnored { switch { case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns == nil && !ui.IgnoreHidden: return ui.ShouldDirBeIgnored diff --git a/internal/common/ui.go b/internal/common/ui.go index 18b7113..9f19596 100644 --- a/internal/common/ui.go +++ b/internal/common/ui.go @@ -3,13 +3,11 @@ package common import ( "regexp" "strconv" - - "github.com/dundee/gdu/v5/pkg/analyze" ) // UI struct type UI struct { - Analyzer analyze.Analyzer + Analyzer Analyzer IgnoreDirPaths map[string]struct{} IgnoreDirPathPatterns *regexp.Regexp IgnoreHidden bool diff --git a/internal/testanalyze/analyze.go b/internal/testanalyze/analyze.go index 76e01df..b0429c1 100644 --- a/internal/testanalyze/analyze.go +++ b/internal/testanalyze/analyze.go @@ -4,14 +4,16 @@ import ( "errors" "time" + "github.com/dundee/gdu/v5/internal/common" "github.com/dundee/gdu/v5/pkg/analyze" + "github.com/dundee/gdu/v5/pkg/fs" ) // MockedAnalyzer returns dir with files with different size exponents type MockedAnalyzer struct{} // AnalyzeDir returns dir with files with different size exponents -func (a *MockedAnalyzer) AnalyzeDir(path string, ignore analyze.ShouldDirBeIgnored) *analyze.Dir { +func (a *MockedAnalyzer) AnalyzeDir(path string, ignore common.ShouldDirBeIgnored) fs.Item { dir := &analyze.Dir{ File: &analyze.File{ Name: "test_dir", @@ -56,14 +58,14 @@ func (a *MockedAnalyzer) AnalyzeDir(path string, ignore analyze.ShouldDirBeIgnor Mtime: time.Date(2021, 8, 27, 22, 23, 24, 0, time.UTC), Parent: dir, } - dir.Files = analyze.Files{dir2, dir3, dir4, file} + dir.Files = fs.Files{dir2, dir3, dir4, file} return dir } // GetProgressChan returns always Done -func (a *MockedAnalyzer) GetProgressChan() chan analyze.CurrentProgress { - return make(chan analyze.CurrentProgress) +func (a *MockedAnalyzer) GetProgressChan() chan common.CurrentProgress { + return make(chan common.CurrentProgress) } // GetDoneChan returns always Done @@ -77,6 +79,6 @@ func (a *MockedAnalyzer) GetDoneChan() chan struct{} { func (a *MockedAnalyzer) ResetProgress() {} // RemoveItemFromDirWithErr returns error -func RemoveItemFromDirWithErr(dir *analyze.Dir, file analyze.Item) error { +func RemoveItemFromDirWithErr(dir fs.Item, file fs.Item) error { return errors.New("Failed") } diff --git a/pkg/analyze/dir.go b/pkg/analyze/dir.go index f91ddda..e6c9467 100644 --- a/pkg/analyze/dir.go +++ b/pkg/analyze/dir.go @@ -5,55 +5,39 @@ import ( "path/filepath" "runtime" + "github.com/dundee/gdu/v5/internal/common" + "github.com/dundee/gdu/v5/pkg/fs" log "github.com/sirupsen/logrus" ) -// CurrentProgress struct -type CurrentProgress struct { - CurrentItemName string - ItemCount int - TotalSize int64 -} - var concurrencyLimit = make(chan struct{}, 3*runtime.GOMAXPROCS(0)) -// ShouldDirBeIgnored whether path should be ignored -type ShouldDirBeIgnored func(name, path string) bool - -// Analyzer is type for dir analyzing function -type Analyzer interface { - AnalyzeDir(path string, ignore ShouldDirBeIgnored) *Dir - GetProgressChan() chan CurrentProgress - GetDoneChan() chan struct{} - ResetProgress() -} - // ParallelAnalyzer implements Analyzer type ParallelAnalyzer struct { - progress *CurrentProgress - progressChan chan CurrentProgress - progressOutChan chan CurrentProgress + progress *common.CurrentProgress + progressChan chan common.CurrentProgress + progressOutChan chan common.CurrentProgress doneChan chan struct{} wait *WaitGroup - ignoreDir ShouldDirBeIgnored + ignoreDir common.ShouldDirBeIgnored } // CreateAnalyzer returns Analyzer -func CreateAnalyzer() Analyzer { +func CreateAnalyzer() common.Analyzer { return &ParallelAnalyzer{ - progress: &CurrentProgress{ + progress: &common.CurrentProgress{ ItemCount: 0, TotalSize: int64(0), }, - progressChan: make(chan CurrentProgress, 1), - progressOutChan: make(chan CurrentProgress, 1), + progressChan: make(chan common.CurrentProgress, 1), + progressOutChan: make(chan common.CurrentProgress, 1), doneChan: make(chan struct{}, 1), wait: (&WaitGroup{}).Init(), } } // GetProgressChan returns channel for getting progress -func (a *ParallelAnalyzer) GetProgressChan() chan CurrentProgress { +func (a *ParallelAnalyzer) GetProgressChan() chan common.CurrentProgress { return a.progressOutChan } @@ -70,7 +54,7 @@ func (a *ParallelAnalyzer) ResetProgress() { } // AnalyzeDir analyzes given path -func (a *ParallelAnalyzer) AnalyzeDir(path string, ignore ShouldDirBeIgnored) *Dir { +func (a *ParallelAnalyzer) AnalyzeDir(path string, ignore common.ShouldDirBeIgnored) fs.Item { a.ignoreDir = ignore go a.updateProgress() @@ -108,7 +92,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { Flag: getDirFlag(err, len(files)), }, ItemCount: 1, - Files: make([]Item, 0, len(files)), + Files: make(fs.Files, 0, len(files)), } setDirPlatformSpecificAttrs(dir, path) @@ -145,7 +129,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { totalSize += info.Size() - dir.Files.Append(file) + dir.AddFile(file) } } @@ -154,13 +138,13 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { for i := 0; i < dirCount; i++ { sub = <-subDirChan - dir.Files.Append(sub) + dir.AddFile(sub) } a.wait.Done() }() - a.progressChan <- CurrentProgress{path, len(files), totalSize} + a.progressChan <- common.CurrentProgress{path, len(files), totalSize} return dir } diff --git a/pkg/analyze/dir_test.go b/pkg/analyze/dir_test.go index 874de8c..a4b15ca 100644 --- a/pkg/analyze/dir_test.go +++ b/pkg/analyze/dir_test.go @@ -8,6 +8,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/dundee/gdu/v5/internal/testdir" + "github.com/dundee/gdu/v5/pkg/fs" "github.com/stretchr/testify/assert" ) @@ -20,14 +21,14 @@ func TestAnalyzeDir(t *testing.T) { defer fin() analyzer := CreateAnalyzer() - dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) + dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }).(*Dir) progress := <-analyzer.GetProgressChan() assert.GreaterOrEqual(t, progress.TotalSize, int64(0)) analyzer.ResetProgress() <-analyzer.GetDoneChan() - dir.UpdateStats(make(HardLinkedItems)) + dir.UpdateStats(make(fs.HardLinkedItems)) // test dir info assert.Equal(t, "test_dir", dir.Name) @@ -47,14 +48,14 @@ func TestAnalyzeDir(t *testing.T) { assert.Equal(t, int64(5), dir.Files[0].(*Dir).Files[1].(*Dir).Files[0].GetSize()) // test parent link - assert.Equal(t, "test_dir", dir.Files[0].(*Dir).Files[1].(*Dir).Files[0].GetParent().GetParent().GetParent().Name) + assert.Equal(t, "test_dir", dir.Files[0].(*Dir).Files[1].(*Dir).Files[0].GetParent().GetParent().GetParent().GetName()) } func TestIgnoreDir(t *testing.T) { fin := testdir.CreateTestDir() defer fin() - dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return true }) + dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return true }).(*Dir) assert.Equal(t, "test_dir", dir.Name) assert.Equal(t, 1, dir.ItemCount) @@ -71,9 +72,9 @@ func TestFlags(t *testing.T) { assert.Nil(t, err) analyzer := CreateAnalyzer() - dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) + dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }).(*Dir) <-analyzer.GetDoneChan() - dir.UpdateStats(make(HardLinkedItems)) + dir.UpdateStats(make(fs.HardLinkedItems)) sort.Sort(dir.Files) @@ -97,9 +98,9 @@ func TestHardlink(t *testing.T) { assert.Nil(t, err) analyzer := CreateAnalyzer() - dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) + dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }).(*Dir) <-analyzer.GetDoneChan() - dir.UpdateStats(make(HardLinkedItems)) + dir.UpdateStats(make(fs.HardLinkedItems)) assert.Equal(t, int64(7+4096*3), dir.Size) // file2 and file3 are counted just once for size assert.Equal(t, 6, dir.ItemCount) // but twice for item count @@ -122,9 +123,9 @@ func TestErr(t *testing.T) { }() analyzer := CreateAnalyzer() - dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) + dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }).(*Dir) <-analyzer.GetDoneChan() - dir.UpdateStats(make(HardLinkedItems)) + dir.UpdateStats(make(fs.HardLinkedItems)) assert.Equal(t, "test_dir", dir.GetName()) assert.Equal(t, 2, dir.ItemCount) @@ -143,5 +144,5 @@ func BenchmarkAnalyzeDir(b *testing.B) { analyzer := CreateAnalyzer() dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false }) <-analyzer.GetDoneChan() - dir.UpdateStats(make(HardLinkedItems)) + dir.UpdateStats(make(fs.HardLinkedItems)) } diff --git a/pkg/analyze/encode_test.go b/pkg/analyze/encode_test.go index 6989bcd..819ccb4 100644 --- a/pkg/analyze/encode_test.go +++ b/pkg/analyze/encode_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/dundee/gdu/v5/pkg/fs" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) @@ -53,8 +54,8 @@ func TestEncode(t *testing.T) { Mli: 1234, Flag: 'H', } - dir.Files = Files{subdir} - subdir.Files = Files{file, file2, file3} + dir.Files = fs.Files{subdir} + subdir.Files = fs.Files{file, file2, file3} var buff bytes.Buffer err := dir.EncodeJSON(&buff, true) diff --git a/pkg/analyze/file.go b/pkg/analyze/file.go index 209ce2c..a80a458 100644 --- a/pkg/analyze/file.go +++ b/pkg/analyze/file.go @@ -1,36 +1,17 @@ package analyze import ( - "io" "os" "path/filepath" "time" -) -// HardLinkedItems maps inode number to array of all hard linked items -type HardLinkedItems map[uint64]Files - -// Item is fs item (file or dir) -type Item interface { - GetPath() string - GetName() string - GetFlag() rune - IsDir() bool - GetSize() int64 - GetType() string - GetUsage() int64 - GetMtime() time.Time - GetItemCount() int - GetParent() *Dir - GetMultiLinkedInode() uint64 - EncodeJSON(writer io.Writer, topLevel bool) error - getItemStats(linkedItems HardLinkedItems) (int, int64, int64) -} + "github.com/dundee/gdu/v5/pkg/fs" +) // File struct type File struct { Mtime time.Time - Parent *Dir + Parent fs.Item Name string Size int64 Usage int64 @@ -49,10 +30,15 @@ func (f *File) IsDir() bool { } // GetParent retruns parent dir -func (f *File) GetParent() *Dir { +func (f *File) GetParent() fs.Item { return f.Parent } +// SetParent sets parent dir +func (f *File) SetParent(parent fs.Item) { + f.Parent = parent +} + // GetPath retruns absolute Get of the file func (f *File) GetPath() string { return filepath.Join(f.Parent.GetPath(), f.Name) @@ -97,7 +83,7 @@ func (f *File) GetMultiLinkedInode() uint64 { return f.Mli } -func (f *File) alreadyCounted(linkedItems HardLinkedItems) bool { +func (f *File) alreadyCounted(linkedItems fs.HardLinkedItems) bool { mli := f.Mli counted := false if mli > 0 { @@ -110,21 +96,55 @@ func (f *File) alreadyCounted(linkedItems HardLinkedItems) bool { return counted } -func (f *File) getItemStats(linkedItems HardLinkedItems) (int, int64, int64) { +// GetItemStats returns 1 as count of items, apparent usage and real usage of this file +func (f *File) GetItemStats(linkedItems fs.HardLinkedItems) (int, int64, int64) { if f.alreadyCounted(linkedItems) { return 1, 0, 0 } return 1, f.GetSize(), f.GetUsage() } +// UpdateStats does nothing on file +func (f *File) UpdateStats(linkedItems fs.HardLinkedItems) {} + +// GetFiles returns all files in directory +func (f *File) GetFiles() fs.Files { + return fs.Files{} +} + +// SetFiles panics on file +func (f *File) SetFiles(files fs.Files) { + panic("SetFiles should not be called on file") +} + +// AddFile panics on file +func (f *File) AddFile(item fs.Item) { + panic("AddFile should not be called on file") +} + // Dir struct type Dir struct { *File BasePath string - Files Files + Files fs.Files ItemCount int } +// AddFile add item fo files +func (f *Dir) AddFile(item fs.Item) { + f.Files = append(f.Files, item) +} + +// GetFiles returns all files in directory +func (f *Dir) GetFiles() fs.Files { + return f.Files +} + +// SetFiles sets files in directory +func (f *Dir) SetFiles(files fs.Files) { + f.Files = files +} + // GetType returns name type of item func (f *Dir) GetType() string { return "Directory" @@ -148,18 +168,19 @@ func (f *Dir) GetPath() string { return filepath.Join(f.Parent.GetPath(), f.Name) } -func (f *Dir) getItemStats(linkedItems HardLinkedItems) (int, int64, int64) { +// GetItemStats returns item count, apparent usage and real usage of this dir +func (f *Dir) GetItemStats(linkedItems fs.HardLinkedItems) (int, int64, int64) { f.UpdateStats(linkedItems) return f.ItemCount, f.GetSize(), f.GetUsage() } // UpdateStats recursively updates size and item count -func (f *Dir) UpdateStats(linkedItems HardLinkedItems) { +func (f *Dir) UpdateStats(linkedItems fs.HardLinkedItems) { totalSize := int64(4096) totalUsage := int64(4096) var itemCount int for _, entry := range f.Files { - count, size, usage := entry.getItemStats(linkedItems) + count, size, usage := entry.GetItemStats(linkedItems) totalSize += size totalUsage += usage itemCount += count @@ -180,96 +201,16 @@ func (f *Dir) UpdateStats(linkedItems HardLinkedItems) { f.Usage = totalUsage } -// Files - slice of pointers to File -type Files []Item - -// Append addes one item to Files -func (f *Files) Append(file Item) { - slice := *f - slice = append(slice, file) - *f = slice -} - -// IndexOf searches File in Files and returns its index -func (f Files) IndexOf(file Item) (int, bool) { - for i, item := range f { - if item == file { - return i, true - } - } - return 0, false -} - -// FindByName searches name in Files and returns its index -func (f Files) FindByName(name string) (int, bool) { - for i, item := range f { - if item.GetName() == name { - return i, true - } - } - return 0, false -} - -// Remove removes File from Files -func (f Files) Remove(file Item) Files { - index, ok := f.IndexOf(file) - if !ok { - return f - } - return append(f[:index], f[index+1:]...) -} - -// RemoveByName removes File from Files -func (f Files) RemoveByName(name string) Files { - index, ok := f.FindByName(name) - if !ok { - return f - } - return append(f[:index], f[index+1:]...) -} - -func (f Files) Len() int { return len(f) } -func (f Files) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f Files) Less(i, j int) bool { return f[i].GetUsage() > f[j].GetUsage() } - -// ByApparentSize sorts files by apparent size -type ByApparentSize Files - -func (f ByApparentSize) Len() int { return len(f) } -func (f ByApparentSize) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f ByApparentSize) Less(i, j int) bool { return f[i].GetSize() > f[j].GetSize() } - -// ByItemCount sorts files by item count -type ByItemCount Files - -func (f ByItemCount) Len() int { return len(f) } -func (f ByItemCount) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f ByItemCount) Less(i, j int) bool { return f[i].GetItemCount() > f[j].GetItemCount() } - -// ByName sorts files by name -type ByName Files - -func (f ByName) Len() int { return len(f) } -func (f ByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f ByName) Less(i, j int) bool { return f[i].GetName() > f[j].GetName() } - -// ByMtime sorts files by name -type ByMtime Files - -func (f ByMtime) Len() int { return len(f) } -func (f ByMtime) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f ByMtime) Less(i, j int) bool { return f[i].GetMtime().After(f[j].GetMtime()) } - // RemoveItemFromDir removes item from dir -func RemoveItemFromDir(dir *Dir, item Item) error { +func RemoveItemFromDir(dir fs.Item, item fs.Item) error { err := os.RemoveAll(item.GetPath()) if err != nil { return err } - dir.Files = dir.Files.Remove(item) + dir.SetFiles(dir.GetFiles().Remove(item)) - cur := dir + cur := dir.(*Dir) for { cur.ItemCount -= item.GetItemCount() cur.Size -= item.GetSize() @@ -278,19 +219,19 @@ func RemoveItemFromDir(dir *Dir, item Item) error { if cur.Parent == nil { break } - cur = cur.Parent + cur = cur.Parent.(*Dir) } return nil } // EmptyFileFromDir empty file from dir -func EmptyFileFromDir(dir *Dir, file Item) error { +func EmptyFileFromDir(dir fs.Item, file fs.Item) error { err := os.Truncate(file.GetPath(), 0) if err != nil { return err } - cur := dir + cur := dir.(*Dir) for { cur.Size -= file.GetSize() cur.Usage -= file.GetUsage() @@ -298,17 +239,17 @@ func EmptyFileFromDir(dir *Dir, file Item) error { if cur.Parent == nil { break } - cur = cur.Parent + cur = cur.Parent.(*Dir) } - dir.Files = dir.Files.Remove(file) + dir.SetFiles(dir.GetFiles().Remove(file)) newFile := &File{ Name: file.GetName(), Flag: file.GetFlag(), Size: 0, Parent: dir, } - dir.Files.Append(newFile) + dir.AddFile(newFile) return nil } diff --git a/pkg/analyze/file_test.go b/pkg/analyze/file_test.go index 48cdc51..d07d841 100644 --- a/pkg/analyze/file_test.go +++ b/pkg/analyze/file_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/dundee/gdu/v5/internal/testdir" + "github.com/dundee/gdu/v5/pkg/fs" "github.com/stretchr/testify/assert" ) @@ -22,7 +23,7 @@ func TestIsDir(t *testing.T) { Size: 2, Parent: &dir, } - dir.Files = Files{file} + dir.Files = fs.Files{file} assert.True(t, dir.IsDir()) assert.False(t, file.IsDir()) @@ -48,7 +49,7 @@ func TestGetType(t *testing.T) { Parent: &dir, Flag: '@', } - dir.Files = Files{file, file2} + dir.Files = fs.Files{file, file2} assert.Equal(t, "Directory", dir.GetType()) assert.Equal(t, "File", file.GetType()) @@ -74,7 +75,7 @@ func TestFind(t *testing.T) { Size: 3, Parent: &dir, } - dir.Files = Files{file, file2} + dir.Files = fs.Files{file, file2} i, _ := dir.Files.IndexOf(file) assert.Equal(t, 0, i) @@ -101,7 +102,7 @@ func TestRemove(t *testing.T) { Size: 3, Parent: &dir, } - dir.Files = Files{file, file2} + dir.Files = fs.Files{file, file2} dir.Files = dir.Files.Remove(file) @@ -131,7 +132,7 @@ func TestRemoveByName(t *testing.T) { Usage: 4, Parent: &dir, } - dir.Files = Files{file, file2} + dir.Files = fs.Files{file, file2} dir.Files = dir.Files.RemoveByName("yyy") @@ -160,7 +161,7 @@ func TestRemoveNotInDir(t *testing.T) { Size: 3, Usage: 4, } - dir.Files = Files{file} + dir.Files = fs.Files{file} _, ok := dir.Files.IndexOf(file2) assert.Equal(t, false, ok) @@ -191,7 +192,7 @@ func TestRemoveByNameNotInDir(t *testing.T) { Size: 3, Usage: 4, } - dir.Files = Files{file} + dir.Files = fs.Files{file} _, ok := dir.Files.IndexOf(file2) assert.Equal(t, false, ok) @@ -227,8 +228,8 @@ func TestRemoveFile(t *testing.T) { Usage: 4, Parent: subdir, } - dir.Files = Files{subdir} - subdir.Files = Files{file} + dir.Files = fs.Files{subdir} + subdir.Files = fs.Files{file} err := RemoveItemFromDir(subdir, file) assert.Nil(t, err) @@ -300,8 +301,8 @@ func TestTruncateFile(t *testing.T) { Usage: 4, Parent: subdir, } - dir.Files = Files{subdir} - subdir.Files = Files{file} + dir.Files = fs.Files{subdir} + subdir.Files = fs.Files{file} err := EmptyFileFromDir(subdir, file) @@ -341,8 +342,8 @@ func TestTruncateFileWithErr(t *testing.T) { Usage: 4, Parent: subdir, } - dir.Files = Files{subdir} - subdir.Files = Files{file} + dir.Files = fs.Files{subdir} + subdir.Files = fs.Files{file} err := EmptyFileFromDir(subdir, file) @@ -371,7 +372,7 @@ func TestUpdateStats(t *testing.T) { Mtime: time.Date(2021, 8, 19, 0, 42, 0, 0, time.UTC), Parent: &dir, } - dir.Files = Files{file, file2} + dir.Files = fs.Files{file, file2} dir.UpdateStats(nil) diff --git a/pkg/analyze/sort_test.go b/pkg/analyze/sort_test.go index e828628..a7d208f 100644 --- a/pkg/analyze/sort_test.go +++ b/pkg/analyze/sort_test.go @@ -5,11 +5,12 @@ import ( "testing" "time" + "github.com/dundee/gdu/v5/pkg/fs" "github.com/stretchr/testify/assert" ) func TestSortByUsage(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Usage: 1, }, @@ -29,7 +30,7 @@ func TestSortByUsage(t *testing.T) { } func TestSortByUsageAsc(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Size: 1, }, @@ -49,7 +50,7 @@ func TestSortByUsageAsc(t *testing.T) { } func TestSortBySize(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Size: 1, }, @@ -61,7 +62,7 @@ func TestSortBySize(t *testing.T) { }, } - sort.Sort(ByApparentSize(files)) + sort.Sort(fs.ByApparentSize(files)) assert.Equal(t, int64(3), files[0].GetSize()) assert.Equal(t, int64(2), files[1].GetSize()) @@ -69,7 +70,7 @@ func TestSortBySize(t *testing.T) { } func TestSortBySizeAsc(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Size: 1, }, @@ -81,7 +82,7 @@ func TestSortBySizeAsc(t *testing.T) { }, } - sort.Sort(sort.Reverse(ByApparentSize(files))) + sort.Sort(sort.Reverse(fs.ByApparentSize(files))) assert.Equal(t, int64(1), files[0].GetSize()) assert.Equal(t, int64(2), files[1].GetSize()) @@ -89,7 +90,7 @@ func TestSortBySizeAsc(t *testing.T) { } func TestSortByItemCount(t *testing.T) { - files := Files{ + files := fs.Files{ &Dir{ ItemCount: 1, }, @@ -101,7 +102,7 @@ func TestSortByItemCount(t *testing.T) { }, } - sort.Sort(ByItemCount(files)) + sort.Sort(fs.ByItemCount(files)) assert.Equal(t, 3, files[0].GetItemCount()) assert.Equal(t, 2, files[1].GetItemCount()) @@ -109,7 +110,7 @@ func TestSortByItemCount(t *testing.T) { } func TestSortByName(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Name: "aa", }, @@ -121,7 +122,7 @@ func TestSortByName(t *testing.T) { }, } - sort.Sort(ByName(files)) + sort.Sort(fs.ByName(files)) assert.Equal(t, "cc", files[0].GetName()) assert.Equal(t, "bb", files[1].GetName()) @@ -129,7 +130,7 @@ func TestSortByName(t *testing.T) { } func TestSortByMtime(t *testing.T) { - files := Files{ + files := fs.Files{ &File{ Mtime: time.Date(2021, 8, 19, 0, 40, 0, 0, time.UTC), }, @@ -141,7 +142,7 @@ func TestSortByMtime(t *testing.T) { }, } - sort.Sort(ByMtime(files)) + sort.Sort(fs.ByMtime(files)) assert.Equal(t, 42, files[0].GetMtime().Minute()) assert.Equal(t, 41, files[1].GetMtime().Minute()) diff --git a/pkg/fs/file.go b/pkg/fs/file.go new file mode 100644 index 0000000..71e85c2 --- /dev/null +++ b/pkg/fs/file.go @@ -0,0 +1,104 @@ +package fs + +import ( + "io" + "time" +) + +// Item is a FS item (file or dir) +type Item interface { + GetPath() string + GetName() string + GetFlag() rune + IsDir() bool + GetSize() int64 + GetType() string + GetUsage() int64 + GetMtime() time.Time + GetItemCount() int + GetParent() Item + SetParent(Item) + GetMultiLinkedInode() uint64 + EncodeJSON(writer io.Writer, topLevel bool) error + GetItemStats(linkedItems HardLinkedItems) (int, int64, int64) + UpdateStats(linkedItems HardLinkedItems) + AddFile(Item) + GetFiles() Files + SetFiles(Files) +} + +// Files - slice of pointers to File +type Files []Item + +// HardLinkedItems maps inode number to array of all hard linked items +type HardLinkedItems map[uint64]Files + +// IndexOf searches File in Files and returns its index +func (f Files) IndexOf(file Item) (int, bool) { + for i, item := range f { + if item == file { + return i, true + } + } + return 0, false +} + +// FindByName searches name in Files and returns its index +func (f Files) FindByName(name string) (int, bool) { + for i, item := range f { + if item.GetName() == name { + return i, true + } + } + return 0, false +} + +// Remove removes File from Files +func (f Files) Remove(file Item) Files { + index, ok := f.IndexOf(file) + if !ok { + return f + } + return append(f[:index], f[index+1:]...) +} + +// RemoveByName |