96 lines
1.9 KiB
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 }
|