diff --git a/internal/dirs/dirs.go b/internal/dirs/dirs.go index 379064e..19707d4 100644 --- a/internal/dirs/dirs.go +++ b/internal/dirs/dirs.go @@ -1,3 +1,4 @@ +// Package dirs provides functions sanitize directory names. package dirs import ( @@ -15,11 +16,11 @@ var ( // UnExpand returns dir after expanding some directory shortcuts // -// $HOME -> ~ +// $HOME -> ~ // -// $PWD -> . +// $PWD -> . // -// workdir -> / +// workdir -> / func UnExpand(dir, workdir string) (outdir string) { if dir != "" { outdir = cleanDir(dir, pwd) diff --git a/internal/filemode/filemode.go b/internal/filemode/filemode.go index 81900d1..d3af219 100644 --- a/internal/filemode/filemode.go +++ b/internal/filemode/filemode.go @@ -1,3 +1,4 @@ +// Package filemode does things io/fs doesn't do package filemode import ( @@ -7,19 +8,21 @@ import ( // Parse parses a string of 3 or 4 numbers as a *NIX filesystem permission. // -// "0777" or "777" -> fs.FileMode(0777) +// "0777" or "777" -> fs.FileMode(0777) // -// "0644" or "644" -> fs.FileMode(0644) -func Parse(in string) (fs.FileMode, error) { - if in == "" { +// "0644" or "644" -> fs.FileMode(0644) +func Parse(input string) (fs.FileMode, error) { + const simplemodelen = 3 + if input == "" { return fs.FileMode(0), nil } - if len(in) == 3 { - in = "0" + in + if len(input) == simplemodelen { + input = "0" + input } - md, e := strconv.ParseUint(in, 8, 64) + md, e := strconv.ParseUint(input, 8, 64) if e != nil { return 0, e } + return fs.FileMode(md), nil } diff --git a/internal/files/disk.go b/internal/files/disk.go index f1a7747..26fd8b8 100644 --- a/internal/files/disk.go +++ b/internal/files/disk.go @@ -52,14 +52,14 @@ func NewDisk(path string) (DiskFile, error) { } name := filepath.Base(abs) - base_path := filepath.Dir(abs) + basePath := filepath.Dir(abs) log.Debugf("%s (base:%s) (size:%s) (modified:%s) exists", - name, base_path, humanize.Bytes(uint64(info.Size())), info.ModTime()) + name, basePath, humanize.Bytes(uint64(info.Size())), info.ModTime()) return DiskFile{ name: name, - path: filepath.Join(base_path, name), + path: filepath.Join(basePath, name), filesize: info.Size(), modified: info.ModTime(), isdir: info.IsDir(), @@ -67,14 +67,14 @@ func NewDisk(path string) (DiskFile, error) { }, nil } -func FindDisk(dir string, recursive bool, f *filter.Filter) (files Files, err error) { +func FindDisk(dir string, recursive bool, fltr *filter.Filter) (Files, error) { + var files Files dir = filepath.Clean(dir) if dir == "." || dir == "" { - var d string - if d, err = os.Getwd(); err != nil { - return + if pwd, err := os.Getwd(); err != nil { + dir = filepath.Clean(dir) } else { - dir = d + dir = pwd } } @@ -83,54 +83,55 @@ func FindDisk(dir string, recursive bool, f *filter.Filter) (files Files, err er recursively = " recursively" } - log.Debugf("gonna find files%s in %s matching %s", recursively, dir, f) + log.Debugf("gonna find files%s in %s matching %s", recursively, dir, fltr) if recursive { - files = append(files, walk_dir(dir, f)...) + files = append(files, walkDir(dir, fltr)...) } else { - files = append(files, read_dir(dir, f)...) + files = append(files, readDir(dir, fltr)...) } - return + return files, nil } -// is_in_hidden_dir checks `path` and parent directories -// of `path` up to `base` for a hidden parent -func is_in_hidden_dir(base, path string) bool { - me := path +// isInHiddenDir checks `path` and parent directories +// of `path` up to `base` for a hidden parent. +func isInHiddenDir(base, path string) bool { + current := path for { - me = filepath.Clean(me) - if me == base { + current = filepath.Clean(current) + if current == base { break } - if strings.HasPrefix(filepath.Base(me), ".") { + if strings.HasPrefix(filepath.Base(current), ".") { return true } - me += string(os.PathSeparator) + ".." + current += string(os.PathSeparator) + ".." } return false } -func walk_dir(dir string, f *filter.Filter) (files Files) { - err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { +func walkDir(dir string, fltr *filter.Filter) Files { + var files Files + err := filepath.WalkDir(dir, func(path string, dirEntry fs.DirEntry, err error) error { if dir == path { return nil } - if is_in_hidden_dir(dir, path) && f.IgnoreHidden() { + if isInHiddenDir(dir, path) && fltr.IgnoreHidden() { return nil } - p, e := filepath.Abs(path) + actualPath, e := filepath.Abs(path) if e != nil { return err } - name := d.Name() - info, _ := d.Info() - if f.Match(info) { + name := dirEntry.Name() + info, _ := dirEntry.Info() + if fltr.Match(info) { files = append(files, DiskFile{ - path: p, + path: actualPath, name: name, filesize: info.Size(), modified: info.ModTime(), @@ -144,10 +145,11 @@ func walk_dir(dir string, f *filter.Filter) (files Files) { log.Errorf("error walking directory %s: %s", dir, err) return Files{} } - return + return files } -func read_dir(dir string, f *filter.Filter) (files Files) { +func readDir(dir string, fltr *filter.Filter) Files { + var files Files fs, err := os.ReadDir(dir) if err != nil { return Files{} @@ -166,7 +168,7 @@ func read_dir(dir string, f *filter.Filter) (files Files) { path := filepath.Dir(filepath.Join(dir, name)) - if f.Match(info) { + if fltr.Match(info) { files = append(files, DiskFile{ name: name, path: filepath.Join(path, name), @@ -177,5 +179,5 @@ func read_dir(dir string, f *filter.Filter) (files Files) { }) } } - return + return files } diff --git a/internal/files/files.go b/internal/files/files.go index d86179b..13f70a0 100644 --- a/internal/files/files.go +++ b/internal/files/files.go @@ -103,17 +103,17 @@ func SortDirectoriesLast(a, b File) int { } func doNameSort(a, b File) int { - an := strings.ToLower(a.Name()) - bn := strings.ToLower(b.Name()) + aname := strings.ToLower(a.Name()) + bname := strings.ToLower(b.Name()) // check if filename is a number - abase := strings.Replace(an, filepath.Ext(an), "", 1) - bbase := strings.Replace(bn, filepath.Ext(bn), "", 1) + abase := strings.Replace(aname, filepath.Ext(aname), "", 1) + bbase := strings.Replace(bname, filepath.Ext(bname), "", 1) ai, aerr := strconv.Atoi(abase) bi, berr := strconv.Atoi(bbase) if aerr == nil && berr == nil { return cmp.Compare(ai, bi) } - return cmp.Compare(an, bn) + return cmp.Compare(aname, bname) } func getSortingSize(f File) int64 { diff --git a/internal/files/trash.go b/internal/files/trash.go index f08bfa8..1cea4e5 100644 --- a/internal/files/trash.go +++ b/internal/files/trash.go @@ -20,13 +20,15 @@ import ( ) const ( - random_str_length int = 8 - trash_info_ext string = ".trashinfo" - trash_info_sec string = "Trash Info" - trash_info_path string = "Path" - trash_info_date string = "DeletionDate" - trash_info_date_fmt string = "2006-01-02T15:04:05" - trash_info_template string = `[Trash Info] + executePerm = fs.FileMode(0755) + noExecuteUserPerm = fs.FileMode(0600) + randomStrLength int = 8 + trashInfoExt string = ".trashinfo" + trashInfoSec string = "Trash Info" + trashInfoPath string = "Path" + trashInfoDate string = "DeletionDate" + trashInfoDateFmt string = "2006-01-02T15:04:05" + trashInfoTemplate string = `[Trash Info] Path={path} DeletionDate={date} ` @@ -59,15 +61,16 @@ func (t TrashInfo) String() string { return t.name + t.path + t.ogpath + t.trashinfo } -func FindTrash(trashdir, ogdir string, f *filter.Filter) (files Files, outerr error) { - outerr = filepath.WalkDir(trashdir, func(path string, d fs.DirEntry, err error) error { +func FindTrash(trashdir, ogdir string, fltr *filter.Filter) (Files, error) { + var files Files + outerr := filepath.WalkDir(trashdir, func(path string, dirEntry fs.DirEntry, err error) error { if err != nil { log.Debugf("what happened?? what is %s?", err) return err } // ignore self, directories, and non trashinfo files - if path == trashdir || d.IsDir() || filepath.Ext(path) != trash_info_ext { + if path == trashdir || dirEntry.IsDir() || filepath.Ext(path) != trashInfoExt { return nil } @@ -76,19 +79,19 @@ func FindTrash(trashdir, ogdir string, f *filter.Filter) (files Files, outerr er if err != nil { return err } - if s := c.Section(trash_info_sec); s != nil { - basepath := s.Key(trash_info_path).String() + if section := c.Section(trashInfoSec); section != nil { + basepath := section.Key(trashInfoPath).String() filename := filepath.Base(basepath) - trashedpath := strings.Replace(strings.Replace(path, "info", "files", 1), trash_info_ext, "", 1) + trashedpath := strings.Replace(strings.Replace(path, "info", "files", 1), trashInfoExt, "", 1) info, err := os.Lstat(trashedpath) if err != nil { log.Errorf("error reading %s: %s", trashedpath, err) return nil } - s := s.Key(trash_info_date).Value() - date, err := time.ParseInLocation(trash_info_date_fmt, s, time.Local) + s := section.Key(trashInfoDate).Value() + date, err := time.ParseInLocation(trashInfoDateFmt, s, time.Local) if err != nil { return err } @@ -97,7 +100,7 @@ func FindTrash(trashdir, ogdir string, f *filter.Filter) (files Files, outerr er return nil } - if f.Match(info) { + if fltr.Match(info) { files = append(files, TrashInfo{ name: filename, path: trashedpath, @@ -108,14 +111,13 @@ func FindTrash(trashdir, ogdir string, f *filter.Filter) (files Files, outerr er filesize: info.Size(), }) } - } return nil }) if outerr != nil { return Files{}, outerr } - return + return files, nil } func Restore(files Files) (restored int, err error) { @@ -125,8 +127,8 @@ func Restore(files Files) (restored int, err error) { return restored, fmt.Errorf("bad file?? %s", maybeFile.Name()) } - var outpath string = dirs.UnEscape(file.ogpath) var cancel bool + outpath := dirs.UnEscape(file.ogpath) log.Infof("restoring %s back to %s\n", file.name, outpath) if _, e := os.Lstat(outpath); e == nil { outpath, cancel = prompt.NewPath(outpath) @@ -138,7 +140,7 @@ func Restore(files Files) (restored int, err error) { basedir := filepath.Dir(outpath) if _, e := os.Stat(basedir); e != nil { - if err = os.MkdirAll(basedir, fs.FileMode(0755)); err != nil { + if err = os.MkdirAll(basedir, executePerm); err != nil { return restored, err } } @@ -161,11 +163,11 @@ func ConfirmRestore(confirm bool, fs Files) error { log.Info("doing the thing") restored, err := Restore(fs) if err != nil { - return fmt.Errorf("restored %d files before error %s", restored, err) + return fmt.Errorf("restored %d files before error %w", restored, err) } - fmt.Printf("restored %d files\n", restored) + fmt.Fprintf(os.Stdout, "restored %d files\n", restored) } else { - fmt.Printf("not doing anything\n") + fmt.Fprintf(os.Stdout, "not doing anything\n") } return nil } @@ -177,7 +179,6 @@ func Remove(files Files) (removed int, err error) { return removed, fmt.Errorf("bad file?? %s", maybeFile.Name()) } - log.Infof("removing %s permanently forever!!!", file.name) if err = os.Remove(file.path); err != nil { if i, e := os.Lstat(file.path); e == nil && i.IsDir() { err = os.RemoveAll(file.path) @@ -199,38 +200,37 @@ func Remove(files Files) (removed int, err error) { func ConfirmClean(confirm bool, fs Files) error { if prompt.YesNo(fmt.Sprintf("remove %d selected files permanently from the trash?", len(fs))) && (!confirm || prompt.YesNo(fmt.Sprintf("really remove all these %d selected files permanently from the trash forever??", len(fs)))) { - log.Info("gonna remove some files forever") removed, err := Remove(fs) if err != nil { - return fmt.Errorf("removed %d files before error %s", removed, err) + return fmt.Errorf("removed %d files before error %w", removed, err) } - fmt.Printf("removed %d files\n", removed) + fmt.Fprintf(os.Stdout, "removed %d files\n", removed) } else { - fmt.Printf("not doing anything\n") + fmt.Fprintf(os.Stdout, "not doing anything\n") } return nil } func TrashFile(trashDir, name string) error { - trashinfo_filename, out_path := ensureUniqueName(filepath.Base(name), trashDir) + trashinfoFilename, outPath := ensureUniqueName(filepath.Base(name), trashDir) // TODO: write across filesystems - if err := os.Rename(name, out_path); err != nil { + if err := os.Rename(name, outPath); err != nil { if strings.Contains(err.Error(), "invalid cross-device link") { return fmt.Errorf("not trashing file '%s': On different filesystem from trash directory", name) } return err } - trash_info, err := formatter.Format(trash_info_template, formatter.Named{ + trashInfo, err := formatter.Format(trashInfoTemplate, formatter.Named{ "path": name, - "date": time.Now().Format(trash_info_date_fmt), + "date": time.Now().Format(trashInfoDateFmt), }) if err != nil { return err } - if err := os.WriteFile(trashinfo_filename, []byte(trash_info), fs.FileMode(0600)); err != nil { + if err := os.WriteFile(trashinfoFilename, []byte(trashInfo), noExecuteUserPerm); err != nil { return err } return nil @@ -258,15 +258,15 @@ func ConfirmTrash(confirm bool, fs Files, trashDir string) error { if err != nil { return err } - var f string + var files string if trashed == 1 { - f = "file" + files = "file" } else { - f = "files" + files = "files" } - fmt.Printf("trashed %d %s\n", trashed, f) + fmt.Fprintf(os.Stdout, "trashed %d %s\n", trashed, files) } else { - fmt.Printf("not doing anything\n") + fmt.Fprintf(os.Stdout, "not doing anything\n") return nil } return nil @@ -291,7 +291,7 @@ func ensureUniqueName(filename, trashDir string) (string, string) { infodir = filepath.Join(trashDir, "info") ) - info := filepath.Join(infodir, filename+trash_info_ext) + info := filepath.Join(infodir, filename+trashInfoExt) if _, err := os.Stat(info); os.IsNotExist(err) { // doesn't exist, so use it path := filepath.Join(filedir, filename) @@ -302,12 +302,12 @@ func ensureUniqueName(filename, trashDir string) (string, string) { var tries int for { tries++ - rando := randomFilename(random_str_length) - new_name := filepath.Join(infodir, filename+rando+trash_info_ext) - if _, err := os.Stat(new_name); os.IsNotExist(err) { + rando := randomFilename(randomStrLength) + newName := filepath.Join(infodir, filename+rando+trashInfoExt) + if _, err := os.Stat(newName); os.IsNotExist(err) { path := filepath.Join(filedir, filename+rando) log.Debugf("settled on random name %s%s on the %s try", filename, rando, humanize.Ordinal(tries)) - return new_name, path + return newName, path } } } diff --git a/internal/filter/filter.go b/internal/filter/filter.go index d91ee15..e3a8e09 100644 --- a/internal/filter/filter.go +++ b/internal/filter/filter.go @@ -1,4 +1,4 @@ -// Package filter filters files based on specific critera +// Package filter filters files based on specific criteria package filter import ( @@ -61,7 +61,7 @@ func (f *Filter) Match(info fs.FileInfo) bool { // on or before/after, not both if !f.on.IsZero() { - if !same_day(f.on, modified) { + if !sameDay(f.on, modified) { log.Debugf("%s: %s isn't on %s, bye!", filename, modified, f.on) return false } @@ -76,7 +76,7 @@ func (f *Filter) Match(info fs.FileInfo) bool { } } - if f.has_regex() && !f.matcher.MatchString(filename) { + if f.hasRegex() && !f.matcher.MatchString(filename) { log.Debugf("%s doesn't match `%s`, bye!", filename, f.matcher.String()) return false } @@ -88,7 +88,7 @@ func (f *Filter) Match(info fs.FileInfo) bool { } } - if f.has_unregex() && f.unmatcher.MatchString(filename) { + if f.hasUnregex() && f.unmatcher.MatchString(filename) { log.Debugf("%s matches `%s`, bye!", filename, f.unmatcher.String()) return false } @@ -155,14 +155,14 @@ func (f *Filter) SetUnPattern(unpattern string) error { } func (f *Filter) Blank() bool { - t := time.Time{} - return !f.has_regex() && - !f.has_unregex() && + blank := time.Time{} + return !f.hasRegex() && + !f.hasUnregex() && f.glob == "" && f.unglob == "" && - f.after.Equal(t) && - f.before.Equal(t) && - f.on.Equal(t) && + f.after.Equal(blank) && + f.before.Equal(blank) && + f.on.Equal(blank) && len(f.filenames) == 0 && !f.ignorehidden && !f.filesonly && @@ -173,31 +173,31 @@ func (f *Filter) Blank() bool { } func (f *Filter) String() string { - var m, unm string + var match, unmatch string if f.matcher != nil { - m = f.matcher.String() + match = f.matcher.String() } if f.unmatcher != nil { - unm = f.unmatcher.String() + unmatch = f.unmatcher.String() } return fmt.Sprintf("on:'%s' before:'%s' after:'%s' glob:'%s' regex:'%s' unglob:'%s' "+ "unregex:'%s' filenames:'%v' filesonly:'%t' dirsonly:'%t' ignorehidden:'%t' "+ "minsize:'%d' maxsize:'%d' mode:'%s'", f.on, f.before, f.after, - f.glob, m, f.unglob, unm, + f.glob, match, f.unglob, unmatch, f.filenames, f.filesonly, f.dirsonly, f.ignorehidden, f.minsize, f.maxsize, f.mode, ) } -func (f *Filter) has_regex() bool { +func (f *Filter) hasRegex() bool { if f.matcher == nil { return false } return f.matcher.String() != "" } -func (f *Filter) has_unregex() bool { +func (f *Filter) hasUnregex() bool { if f.unmatcher == nil { return false } @@ -210,7 +210,7 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, now = time.Now() ) - f := &Filter{ + filter := &Filter{ glob: glob, unglob: unglob, filesonly: filesonly, @@ -219,14 +219,14 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, mode: mode, } - f.AddFileNames(names...) + filter.AddFileNames(names...) if on != "" { o, err := anytime.Parse(on, now) if err != nil { return &Filter{}, err } - f.on = o + filter.on = o } if after != "" { @@ -234,7 +234,7 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, if err != nil { return &Filter{}, err } - f.after = a + filter.after = a } if before != "" { @@ -242,14 +242,14 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, if err != nil { return &Filter{}, err } - f.before = b + filter.before = b } - err = f.SetPattern(pattern) + err = filter.SetPattern(pattern) if err != nil { return nil, err } - err = f.SetUnPattern(unpattern) + err = filter.SetUnPattern(unpattern) if err != nil { return nil, err } @@ -259,7 +259,7 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, if e != nil { log.Errorf("invalid input size '%s'", minsize) } - f.minsize = int64(m) + filter.minsize = int64(m) } if maxsize != "" { @@ -267,13 +267,13 @@ func New(on, before, after, glob, pattern, unglob, unpattern string, filesonly, if e != nil { log.Errorf("invalid input size '%s'", maxsize) } - f.maxsize = int64(m) + filter.maxsize = int64(m) } - return f, nil + return filter, nil } -func same_day(a, b time.Time) bool { +func sameDay(a, b time.Time) bool { ay, am, ad := a.Date() by, bm, bd := b.Date() return ay == by && am == bm && ad == bd diff --git a/internal/filter/filter_test.go b/internal/filter/filter_test.go index b290b2b..3846f1d 100644 --- a/internal/filter/filter_test.go +++ b/internal/filter/filter_test.go @@ -66,13 +66,14 @@ func (s singletest) String() string { } func testmatch(t *testing.T, testers []testholder) { + t.Helper() // I don't think this is a helper function but w/e const testnamefmt string = "file %s modified on %s" var ( - f *filter.Filter - err error + fltr *filter.Filter + err error ) for _, tester := range testers { - f, err = filter.New( + fltr, err = filter.New( tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.unglob, tester.unpattern, tester.filesonly, tester.dirsonly, tester.ignorehidden, tester.minsize, tester.maxsize, tester.mode, @@ -84,7 +85,7 @@ func testmatch(t *testing.T, testers []testholder) { for _, tst := range tester.good { t.Run(fmt.Sprintf(testnamefmt+"_good", tst.filename, tst.modified), func(t *testing.T) { - if !f.Match(tst) { + if !fltr.Match(tst) { t.Fatalf("(%s) didn't match (%s) but should have", tst, tester) } }) @@ -92,7 +93,7 @@ func testmatch(t *testing.T, testers []testholder) { for _, tst := range tester.bad { t.Run(fmt.Sprintf(testnamefmt+"_bad", tst.filename, tst.modified), func(t *testing.T) { - if f.Match(tst) { + if fltr.Match(tst) { t.Fatalf("(%s) matched (%s) but shouldn't have", tst, tester) } }) @@ -116,10 +117,10 @@ func timeonly(dir bool, times ...time.Time) []singletest { return out } -func sizeonly(dir bool, sizes ...int64) []singletest { +func sizeonly(sizes ...int64) []singletest { out := make([]singletest, 0, len(sizes)) for _, size := range sizes { - out = append(out, singletest{filename: "blank", modified: time.Time{}, isdir: dir, size: size, mode: 0000}) + out = append(out, singletest{filename: "blank", modified: time.Time{}, isdir: false, size: size, mode: 0000}) } return out } @@ -301,7 +302,7 @@ func TestFilterGlob(t *testing.T) { }, { glob: "t?st", - good: nameonly(false, "test", "tast", "tfst", "tnst"), + good: nameonly(false, "test", "tist", "tfst", "tnst"), bad: nameonly(false, "best", "fast", "most", "past"), }, }) @@ -342,7 +343,7 @@ func TestFilterUnGlob(t *testing.T) { { unglob: "t?st", good: nameonly(false, "best", "fast", "most", "past"), - bad: nameonly(false, "test", "tast", "tfst", "tnst"), + bad: nameonly(false, "test", "tist", "tfst", "tnst"), }, }) } @@ -410,13 +411,13 @@ func TestFilesize(t *testing.T) { testmatch(t, []testholder{ { minsize: "9001B", - good: sizeonly(false, 10000, 9002, 424242, math.MaxInt64), - bad: sizeonly(false, 9000, math.MinInt64, 0, -9001), + good: sizeonly(10000, 9002, 424242, math.MaxInt64), + bad: sizeonly(9000, math.MinInt64, 0, -9001), }, { maxsize: "9001B", - good: sizeonly(false, 9000, math.MinInt64, 0, -9001), - bad: sizeonly(false, 10000, 9002, 424242, math.MaxInt64), + good: sizeonly(9000, math.MinInt64, 0, -9001), + bad: sizeonly(10000, 9002, 424242, math.MaxInt64), }, }) } @@ -580,25 +581,25 @@ func TestFilterMultipleParameters(t *testing.T) { } func TestFilterBlank(t *testing.T) { - var f *filter.Filter + var fltr *filter.Filter t.Run("new", func(t *testing.T) { - f, _ = filter.New("", "", "", "", "", "", "", false, false, false, "0", "0", 0) - if !f.Blank() { - t.Fatalf("filter isn't blank? %s", f) + fltr, _ = filter.New("", "", "", "", "", "", "", false, false, false, "0", "0", 0) + if !fltr.Blank() { + t.Fatalf("filter isn't blank? %s", fltr) } }) t.Run("blank", func(t *testing.T) { - f = &filter.Filter{} - if !f.Blank() { - t.Fatalf("filter isn't blank? %s", f) + fltr = &filter.Filter{} + if !fltr.Blank() { + t.Fatalf("filter isn't blank? %s", fltr) } }) } func TestFilterNotBlank(t *testing.T) { var ( - f *filter.Filter + fltr *filter.Filter testers = []testholder{ { pattern: "[Ttest]", @@ -642,14 +643,14 @@ func TestFilterNotBlank(t *testing.T) { for _, tester := range testers { t.Run("notblank"+tester.String(), func(t *testing.T) { - f, _ = filter.New( + fltr, _ = filter.New( tester.on, tester.before, tester.after, tester.glob, tester.pattern, tester.unglob, tester.unpattern, tester.filesonly, tester.dirsonly, tester.ignorehidden, tester.minsize, tester.maxsize, tester.mode, tester.filenames..., ) - if f.Blank() { - t.Fatalf("filter is blank?? %s", f) + if fltr.Blank() { + t.Fatalf("filter is blank?? %s", fltr) } }) } diff --git a/internal/interactive/interactive.go b/internal/interactive/interactive.go index 6b95279..c251358 100644 --- a/internal/interactive/interactive.go +++ b/internal/interactive/interactive.go @@ -1,3 +1,4 @@ +// Package interactive implements a charm-powered table to display files. package interactive import ( @@ -64,7 +65,7 @@ type model struct { files files.Files } -func newModel(fs []files.File, width, height int, readonly, preselected, once bool, workdir string, mode modes.Mode) model { +func newModel(fls []files.File, width, height int, readonly, preselected, once bool, workdir string, mode modes.Mode) model { var ( // TODO: figure this out dynamically based on longest of each fwidth int = int(math.Round(float64(width-woffset) * 0.46)) @@ -72,9 +73,9 @@ func newModel(fs []files.File, width, height int, readonly, preselected, once bo dwidth int = int(math.Round(float64(width-woffset) * 0.15)) swidth int = int(math.Round(float64(width-woffset) * 0.12)) cwidth int = int(math.Round(float64(width-woffset) * 0.02)) - theight int = min(height-hoffset, len(fs)) + theight int = min(height-hoffset, len(fls)) - m = model{ + mdl = model{ keys: defaultKeyMap(), readonly: readonly, once: once, @@ -83,15 +84,15 @@ func newModel(fs []files.File, width, height int, readonly, preselected, once bo mode: mode, selected: map[string]bool{}, selectsize: 0, - files: fs, + files: fls, } ) if workdir != "" { - m.workdir = filepath.Clean(workdir) + mdl.workdir = filepath.Clean(workdir) } - rows := m.freshRows(preselected) + rows := mdl.freshRows(preselected) columns := []table.Column{ {Title: "filename", Width: fwidth}, @@ -99,18 +100,18 @@ func newModel(fs []files.File, width, height int, readonly, preselected, once bo {Title: "modified", Width: dwidth}, {Title: "size", Width: swidth}, } - if !m.readonly { + if !mdl.readonly { columns = append(columns, table.Column{Title: uncheck, Width: cwidth}) } else { columns[0].Width += cwidth } - m.table = createTable(columns, rows, theight) + mdl.table = createTable(columns, rows, theight) - m.sorting = sorting.Name - m.sort() + mdl.sorting = sorting.Name + mdl.sort() - return m + return mdl } type keyMap struct { @@ -235,7 +236,7 @@ func (m model) View() string { func (m model) showHelp() string { // TODO: maybe use bubbletea built in help - var keys []string = []string{ + var keys = []string{ fmt.Sprintf("%s %s (%s)", darktext.Render(m.keys.sort.Help().Key), darkertext.Render(m.keys.sort.Help().Desc), m.sorting.String()), fmt.Sprintf("%s %s", darktext.Render(m.keys.quit.Help().Key), darkertext.Render(m.keys.quit.Help().Desc)), } @@ -254,50 +255,50 @@ func (m model) showHelp() string { func (m model) header() string { var ( - right, left string - spacer_width int - keys = []string{ + right, left string + spacerWidth int + keys = []string{ fmt.Sprintf("%s %s", darktext.Render(m.keys.rstr.Help().Key), darkertext.Render(m.keys.rstr.Help().Desc)), fmt.Sprintf("%s %s", darktext.Render(m.keys.clen.Help().Key), darkertext.Render(m.keys.clen.Help().Desc)), } - select_keys = []string{ + selectKeys = []string{ fmt.Sprintf("%s %s", darktext.Render(m.keys.todo.Help().Key), darkertext.Render(m.keys.todo.Help().Desc)), fmt.Sprintf("%s %s", darktext.Render(m.keys.nada.Help().Key), darkertext.Render(m.keys.nada.Help().Desc)), fmt.Sprintf("%s %s", darktext.Render(m.keys.invr.Help().Key), darkertext.Render(m.keys.invr.Help().Desc)), } - dot = darkesttext.Render("•") - wide_dot = darkesttext.Render(" • ") + dot = darkesttext.Render("•") + wideDot = darkesttext.Render(" • ") ) right = " " // to offset from the table border switch m.mode { case modes.Interactive: - right += strings.Join(keys, wide_dot) + right += strings.Join(keys, wideDot) default: right += m.mode.String() if m.workdir != "" { right += fmt.Sprintf(" in %s", dirs.UnExpand(m.workdir, "")) } } - right += fmt.Sprintf(" %s %s", dot, strings.Join(select_keys, wide_dot)) + right += fmt.Sprintf(" %s %s", dot, strings.Join(selectKeys, wideDot)) left = fmt.Sprintf("%d/%d %s %s", len(m.selected), len(m.table.Rows()), dot, humanize.Bytes(uint64(m.selectsize))) // offset of 2 again because of table border - spacer_width = m.termwidth - lipgloss.Width(right) - lipgloss.Width(left) - 2 - if spacer_width <= 0 { - spacer_width = 1 // always at least one space + spacerWidth = m.termwidth - lipgloss.Width(right) - lipgloss.Width(left) - 2 + if spacerWidth <= 0 { + spacerWidth = 1 // always at least one space } - return fmt.Sprintf("%s%s%s", right, strings.Repeat(" ", spacer_width), left) + return fmt.Sprintf("%s%s%s", right, strings.Repeat(" ", spacerWidth), left) } func (m model) footer() string { return regulartext.Render(m.showHelp()) } -func (m model) quit(unselect_all bool) (model, tea.Cmd) { - if unselect_all { +func (m model) quit(unselectAll bool) (model, tea.Cmd) { + if unselectAll { m.unselectAll() } else { m.onlySelected() @@ -337,17 +338,17 @@ func (m model) selectedFiles() (outfile files.Files) { } */ func (m *model) freshRows(preselected bool) (rows []table.Row) { - for _, f := range m.files { - r := newRow(f, m.workdir) + for _, file := range m.files { + row := newRow(file, m.workdir) if !m.readonly { - r = append(r, getCheck(preselected)) + row = append(row, getCheck(preselected)) } if preselected { - m.selected[f.String()] = true - m.selectsize += f.Filesize() + m.selected[file.String()] = true + m.selectsize += file.Filesize() } - rows = append(rows, r) + rows = append(rows, row) } return } @@ -364,7 +365,7 @@ func (m *model) onlySelected() { m.table.SetRows(rows) } -// updateRow updates row of `index` with `row` +// updateRow updates row of provided index with provided row. func (m *model) updateRow(index int, selected bool) { rows := m.table.Rows() row := rows[index] @@ -380,17 +381,17 @@ func (m *model) updateRow(index int, selected bool) { } func (m *model) updateRows(selected bool) { - var newrows []table.Row + var newrows = []table.Row{} for _, row := range m.table.Rows() { - r := table.Row{ + newRow := table.Row{ row[0], row[1], row[2], row[3], getCheck(selected), } - newrows = append(newrows, r) + newrows = append(newrows, newRow) } m.table.SetRows(newrows) } @@ -483,7 +484,7 @@ func (m *model) invertSelection() { func (m *model) sort() { slices.SortStableFunc(m.files, m.sorting.Sorter()) - var rows []table.Row + var rows = []table.Row{} for _, file := range m.files { r := newRow(file, m.workdir) if !m.readonly { @@ -495,32 +496,32 @@ func (m *model) sort() { m.table.SetRows(rows) } -func Select(fs files.Files, width, height int, readonly, preselected, once bool, workdir string, mode modes.Mode) (files.Files, modes.Mode, error) { - mdl := newModel(fs, width, height, readonly, preselected, once, workdir, mode) +func Select(fls files.Files, width, height int, readonly, preselected, once bool, workdir string, mode modes.Mode) (files.Files, modes.Mode, error) { + mdl := newModel(fls, width, height, readonly, preselected, once, workdir, mode) endmodel, err := tea.NewProgram(mdl).Run() if err != nil { - return fs, 0, err + return fls, 0, err } m, ok := endmodel.(model) if !ok { - return fs, 0, fmt.Errorf("model isn't the right type?? what has happened") + return fls, 0, fmt.Errorf("model isn't the right type?? what has happened") } return m.selectedFiles(), m.mode, nil } func newRow(file files.File, workdir string) table.Row { - var t, b string - t = humanize.Time(file.Date()) + var time, bar string + time = humanize.Time(file.Date()) if file.IsDir() { - b = strings.Repeat("─", 3) + bar = strings.Repeat("─", 3) } else { - b = humanize.Bytes(uint64(file.Filesize())) + bar = humanize.Bytes(uint64(file.Filesize())) } return table.Row{ dirs.UnEscape(file.Name()), dirs.UnExpand(filepath.Dir(file.Path()), workdir), - t, - b, + time, + bar, } } @@ -534,43 +535,43 @@ func getCheck(selected bool) (ourcheck string) { } func createTable(columns []table.Column, rows []table.Row, height int) table.Model { - t := table.New( + tbl := table.New( table.WithColumns(columns), table.WithRows(rows), table.WithFocused(true), table.WithHeight(height), ) - t.KeyMap = fixTableKeymap() - t.SetStyles(makeStyle()) - return t + tbl.KeyMap = fixTableKeymap() + tbl.SetStyles(makeStyle()) + return tbl } func fixTableKeymap() table.KeyMap { - t := table.DefaultKeyMap() + tbl := table.DefaultKeyMap() // remove spacebar from default page down keybind, but keep the rest - t.PageDown.SetKeys( - slices.DeleteFunc(t.PageDown.Keys(), func(s string) bool { + tbl.PageDown.SetKeys( + slices.DeleteFunc(tbl.PageDown.Keys(), func(s string) bool { return s == space })..., ) - return t + return tbl } func makeStyle() table.Styles { - s := table.DefaultStyles() - s.Header = s.Header. + style := table.DefaultStyles() + style.Header = style.Header. BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color(black)). BorderBottom(true). Bold(false) - s.Selected = s.Selected. + style.Selected = style.Selected. Foreground(lipgloss.Color(white)). Background(lipgloss.Color(hoveritembg)). Bold(false) - return s + return style } func makeUnselectedStyle() table.Styles { diff --git a/internal/interactive/modes/modes.go b/internal/interactive/modes/modes.go index f79679a..89df740 100644 --- a/internal/interactive/modes/modes.go +++ b/internal/interactive/modes/modes.go @@ -1,3 +1,4 @@ +// Package modes implements Mode type for interactive table. package modes type Mode int diff --git a/internal/interactive/sorting/sorting.go b/internal/interactive/sorting/sorting.go index 37ce001..44e4eeb 100644 --- a/internal/interactive/sorting/sorting.go +++ b/internal/interactive/sorting/sorting.go @@ -1,3 +1,4 @@ +// Package sorting implements Sorting type for interactive table. package sorting import "git.burning.moe/celediel/gt/internal/files" diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 5824bae..78f864b 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -1,3 +1,4 @@ +// Package prompt implements prompt functions for runes and strings. package prompt import ( @@ -28,16 +29,16 @@ func AskRune(prompt, options string) byte { } }() - fmt.Printf("%s [%s]: ", prompt, options) + fmt.Fprintf(os.Stdout, "%s [%s]: ", prompt, options) // read one byte from stdin - b := make([]byte, 1) - _, err = os.Stdin.Read(b) + one := make([]byte, 1) + _, err = os.Stdin.Read(one) if err != nil { return 0 } - return bytes.ToLower(b)[0] + return bytes.ToLower(one)[0] } func NewPath(path string) (string, bool) { diff --git a/main.go b/main.go index 8747ae7..a970161 100644 --- a/main.go +++ b/main.go @@ -21,9 +21,10 @@ import ( ) const ( - appname string = "gt" - appdesc string = "xdg trash cli" - appversion string = "v0.0.1" + appname string = "gt" + appdesc string = "xdg trash cli" + appversion string = "v0.0.1" + executePerm = fs.FileMode(0755) ) var ( @@ -43,12 +44,12 @@ var ( trashDir = filepath.Join(xdg.DataHome, "Trash") - beforeAll = func(ctx *cli.Context) (err error) { + beforeAll = func(_ *cli.Context) error { // setup log log.SetReportTimestamp(true) log.SetTimeFormat(time.TimeOnly) - if l, e := log.ParseLevel(loglvl); e == nil { - log.SetLevel(l) + if level, err := log.ParseLevel(loglvl); err == nil { + log.SetLevel(level) // Some extra info for debug level if log.GetLevel() == log.DebugLevel { log.SetReportCaller(true) @@ -58,35 +59,35 @@ var ( } // read the term height and width for tables - w, h, e := term.GetSize(int(os.Stdout.Fd())) + width, height, e := term.GetSize(int(os.Stdout.Fd())) if e != nil { - w = 80 - h = 24 + width = 80 + height = 24 } - termwidth = w - termheight = h + termwidth = width + termheight = height // ensure trash directories exist if _, e := os.Stat(trashDir); os.IsNotExist(e) { - if err := os.Mkdir(trashDir, fs.FileMode(0755)); err != nil { + if err := os.Mkdir(trashDir, executePerm); err != nil { return err } } if _, e := os.Stat(filepath.Join(trashDir, "info")); os.IsNotExist(e) { - if err := os.Mkdir(filepath.Join(trashDir, "info"), fs.FileMode(0755)); err != nil { + if err := os.Mkdir(filepath.Join(trashDir, "info"), executePerm); err != nil { return err } } if _, e := os.Stat(filepath.Join(trashDir, "files")); os.IsNotExist(e) { - if err := os.Mkdir(filepath.Join(trashDir, "files"), fs.FileMode(0755)); err != nil { + if err := os.Mkdir(filepath.Join(trashDir, "files"), executePerm); err != nil { return err } } - return + return nil } - // action launches interactive mode if run without args, or trashes files as args + // action launches interactive mode if run without args, or trashes files as args. action = func(ctx *cli.Context) error { var ( err error @@ -103,18 +104,7 @@ var ( return err } - if len(ctx.Args().Slice()) != 0 { - // args, so try to trash files - var files_to_trash files.Files - for _, arg := range ctx.Args().Slice() { - file, e := files.NewDisk(arg) - if e != nil { - log.Fatalf("cannot trash '%s': No such file or directory", arg) - } - files_to_trash = append(files_to_trash, file) - } - return files.ConfirmTrash(askconfirm, files_to_trash, trashDir) - } else { + if len(ctx.Args().Slice()) == 0 { // no ags, so do interactive mode var ( infiles files.Files @@ -133,7 +123,7 @@ var ( } else { msg = "no files to show" } - fmt.Println(msg) + fmt.Fprint(os.Stdout, msg) return nil } selected, mode, err = interactive.Select(infiles, termwidth, termheight, false, false, false, workdir, modes.Interactive) @@ -162,6 +152,17 @@ var ( } return nil } + + // args, so try to trash files + var filesToTrash files.Files + for _, arg := range ctx.Args().Slice() { + file, e := files.NewDisk(arg) + if e != nil { + log.Fatalf("cannot trash '%s': No such file or directory", arg) + } + filesToTrash = append(filesToTrash, file) + } + return files.ConfirmTrash(askconfirm, filesToTrash, trashDir) } beforeCommands = func(ctx *cli.Context) (err error) { @@ -189,7 +190,7 @@ var ( return } - after = func(ctx *cli.Context) error { + after = func(_ *cli.Context) error { return nil } @@ -200,7 +201,7 @@ var ( Flags: slices.Concat(trashFlags, filterFlags), Before: beforeTrash, Action: func(ctx *cli.Context) error { - var files_to_trash files.Files + var filesToTrash files.Files var selectall bool for _, arg := range ctx.Args().Slice() { file, e := files.NewDisk(arg) @@ -209,25 +210,25 @@ var ( fltr.AddFileName(arg) continue } - files_to_trash = append(files_to_trash, file) + filesToTrash = append(filesToTrash, file) selectall = true } // if none of the args were files, then process find files based on filter - if len(files_to_trash) == 0 { + if len(filesToTrash) == 0 { fls, err := files.FindDisk(workdir, recursive, fltr) if err != nil { return err } if len(fls) == 0 { - fmt.Println("no files to trash") + fmt.Fprintf(os.Stdout, "no files to trash") return nil } - files_to_trash = append(files_to_trash, fls...) + filesToTrash = append(filesToTrash, fls...) selectall = !fltr.Blank() } - selected, _, err := interactive.Select(files_to_trash, termwidth, termheight, false, selectall, false, workdir, modes.Trashing) + selected, _, err := interactive.Select(filesToTrash, termwidth, termheight, false, selectall, false, workdir, modes.Trashing) if err != nil { return err } @@ -246,14 +247,14 @@ var ( Usage: "List trashed files", Flags: slices.Concat(listFlags, alreadyintrashFlags, filterFlags), Before: beforeCommands, - Action: func(ctx *cli.Context) error { + Action: func(_ *cli.Context) error { log.Debugf("searching in directory %s for files", trashDir) // look for files fls, err := files.FindTrash(trashDir, ogdir, fltr) var msg string - log.Debugf("filter '%s' is blark? %t in %s", fltr, fltr.Blank(), ogdir) + log.Debugf("filter '%s' is blank? %t in %s", fltr, fltr.Blank(), ogdir) if fltr.Blank() && ogdir == "" { msg = "trash is empty" } else { @@ -261,7 +262,7 @@ var ( } if len(fls) == 0 { - fmt.Println(msg) + fmt.Fprint(os.Stdout, msg) return nil } else if err != nil { return err @@ -280,13 +281,13 @@ var ( Usage: "Restore a trashed file or files", Flags: slices.Concat(cleanRestoreFlags, alreadyintrashFlags, filterFlags), Before: beforeCommands, - Action: func(ctx *cli.Context) error { + Action: func(_ *cli.Context) error { log.Debugf("searching in directory %s for files", trashDir) // look for files fls, err := files.FindTrash(trashDir, ogdir, fltr) if len(fls) == 0 { - fmt.Println("no files to restore") + fmt.Fprintf(os.Stdout, "no files to restore") return nil } else if err != nil { return err @@ -311,10 +312,10 @@ var ( Usage: "Clean files from trash", Flags: slices.Concat(cleanRestoreFlags, alreadyintrashFlags, filterFlags), Before: beforeCommands, - Action: func(ctx *cli.Context) error { + Action: func(_ *cli.Context) error { fls, err := files.FindTrash(trashDir, ogdir, fltr) if len(fls) == 0 { - fmt.Println("no files to clean") + fmt.Fprintf(os.Stdout, "no files to clean") return nil } else if err != nil { return err