file-store/pkg/ifs/walk_dir.go

96 lines
1.9 KiB
Go

package ifs
import (
"errors"
"io"
"io/fs"
"path"
)
var SkipDir = fs.SkipDir
func WalkDir(fsys FS, root string, fn fs.WalkDirFunc) error {
info, err := fs.Stat(fsys, root)
if err != nil {
err = fn(root, nil, err)
} else {
err = walkDir(fsys, root, &statDirEntry{info}, fn)
}
if err == SkipDir {
return nil
}
return err
}
type ReadDirFn func(fs.DirEntry) error
func ReadDir(fsys fs.FS, name string, fn ReadDirFn) error {
file, err := fsys.Open(name)
if err != nil {
return err
}
defer file.Close()
dir, ok := file.(fs.ReadDirFile)
if !ok {
return &fs.PathError{Op: "readdir", Path: name, Err: errors.New("not implemented")}
}
for {
list, err := dir.ReadDir(100)
if err != nil && err != io.EOF {
return err
}
for _, dirent := range list {
if err := fn(dirent); err != nil {
return err
}
}
if err == io.EOF {
break
}
}
return nil
}
func walkDir(fsys fs.FS, name string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error {
if err := walkDirFn(name, d, nil); err != nil || !d.IsDir() {
if err == SkipDir && d.IsDir() {
// Successfully skipped directory.
err = nil
}
return err
}
if err := ReadDir(fsys, name, func(d1 fs.DirEntry) error {
name1 := path.Join(name, d1.Name())
if err := walkDir(fsys, name1, d1, walkDirFn); err != nil {
if err == SkipDir {
return nil
}
return err
}
return nil
}); err != nil {
// Second call, to report ReadDir error.
err = walkDirFn(name, d, err)
if err != nil {
if err == SkipDir && d.IsDir() {
err = nil
}
return err
}
}
return nil
}
type statDirEntry struct {
info fs.FileInfo
}
func (d *statDirEntry) Name() string { return d.info.Name() }
func (d *statDirEntry) IsDir() bool { return d.info.IsDir() }
func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() }
func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil }