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 }