// Copyright 2024 Matthew Rich . All rights reserved. package fs import ( "io/fs" "log/slog" "path/filepath" "fmt" ) type DirEntry fs.DirEntry type FS fs.FS type Selector[Item comparable] func(r Item) bool type WalkFunc func(fsys FS, path string, file DirEntry) (err error) type WalkDir struct { root *WorkingDir subDirsStack []*WorkingDir `yaml:"-" json:"-"` fn WalkFunc } type WorkingDir struct { path string fsys FS } func NewWalkDir(fsys FS, path string, fn WalkFunc) *WalkDir { w := &WalkDir{ subDirsStack: make([]*WorkingDir, 0, 100), fn: fn, } w.root = &WorkingDir{ path: path, fsys: fsys } w.subDirsStack = append(w.subDirsStack, w.root) return w } func (w *WalkDir) WalkDirectory(fsys FS, path string, filter Selector[DirEntry]) (err error) { var files []fs.DirEntry if files, err = fs.ReadDir(fsys, "."); err == nil { for _,file := range files { if filter != nil && filter(file) { continue } slog.Info("fs.WalkDir.WalkDirectory()", "file", file, "path", path, "file", file.Name()) entryPath := filepath.Join(path, file.Name()) if err = w.fn(fsys, entryPath, file); err != nil { slog.Info("fs.WalkDir.WalkDirectory() ERROR", "file", entryPath, "error", err) return } if file.IsDir() { var dir FS if dir, err = fs.Sub(fsys, file.Name()); err != nil { slog.Info("fs.WalkDir.WalkDirectory() SubDir ERROR", "dir", dir, "error", err) return } else { w.subDirsStack = append(w.subDirsStack, &WorkingDir{ path: entryPath, fsys: dir }) } } } } else { err = fmt.Errorf("fs.ReadDir failed on path %s: %w", path, err) } return } func (w *WalkDir) Walk(filter Selector[DirEntry]) (err error) { for len(w.subDirsStack) != 0 { var dirPath *WorkingDir dirPath, w.subDirsStack = w.subDirsStack[len(w.subDirsStack) - 1], w.subDirsStack[:len(w.subDirsStack) - 1] if err = w.WalkDirectory(dirPath.fsys, dirPath.path, filter); err != nil { return } } return }