package walker import ( "fmt" "io/fs" "os" "path/filepath" "strings" "git.flo.fo/FLO/dirmd/internal/config" "git.flo.fo/FLO/dirmd/internal/filter" ) type Entry struct { RelPath string Size int64 } type Result struct { Entries []Entry AbsRoot string SingleFile bool OmitTest bool } func Run(cfg *config.Config) (*Result, error) { if cfg.SingleFile { return processSingleFile(cfg) } return walkDirectory(cfg) } func processSingleFile(cfg *config.Config) (*Result, error) { info, err := os.Stat(cfg.AbsRoot) if err != nil { return nil, fmt.Errorf("cannot access %s: %w", cfg.AbsRoot, err) } if info.IsDir() { return walkDirectory(cfg) } flt := &filter.Filter{ MaxSize: cfg.MaxSize, Extensions: cfg.Extensions, Ignores: cfg.Ignores, OmitTest: cfg.OmitTest, FrontendOnly: cfg.FrontendOnly, OmitMd: cfg.OmitMd, } if flt.ShouldSkipFile(cfg.AbsRoot, filepath.Base(cfg.AbsRoot), info) { return nil, fmt.Errorf("file %s filtered out", cfg.AbsRoot) } return &Result{ Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}}, AbsRoot: filepath.Dir(cfg.AbsRoot), SingleFile: true, OmitTest: cfg.OmitTest, }, nil } func walkDirectory(cfg *config.Config) (*Result, error) { flt := &filter.Filter{ MaxSize: cfg.MaxSize, Extensions: cfg.Extensions, Ignores: cfg.Ignores, OmitTest: cfg.OmitTest, FrontendOnly: cfg.FrontendOnly, OmitMd: cfg.OmitMd, } result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest} err := filepath.WalkDir(cfg.AbsRoot, func(path string, d fs.DirEntry, err error) error { if err != nil { fmt.Fprintf(os.Stderr, "warning: %v\n", err) return nil } rel, err := filepath.Rel(cfg.AbsRoot, path) if err != nil { return nil } depth := strings.Count(rel, string(filepath.Separator)) if rel != "." { depth++ } if depth > cfg.MaxDepth { if d.IsDir() { return filepath.SkipDir } return nil } if filter.IsSymlink(path) { if d.IsDir() { return filepath.SkipDir } return nil } base := filepath.Base(path) if d.IsDir() { if rel != "." && flt.ShouldSkipDir(base) { return filepath.SkipDir } return nil } info, err := d.Info() if err != nil { fmt.Fprintf(os.Stderr, "warning: cannot stat %s: %v\n", rel, err) return nil } if flt.ShouldSkipFile(path, base, info) { return nil } result.Entries = append(result.Entries, Entry{RelPath: rel, Size: info.Size()}) return nil }) return result, err }