Skip to content

Commit e6675e3

Browse files
committed
Add Walk function and tests for it.
1 parent 2c3e227 commit e6675e3

File tree

2 files changed

+400
-0
lines changed

2 files changed

+400
-0
lines changed

walk.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package vfs
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"sort"
7+
)
8+
9+
// Walk walks the file tree rooted at root, calling walkFunc for each file or
10+
// directory in the tree, including root. All errors that arise visiting files
11+
// and directories are filtered by walkFn. The files are walked in lexical
12+
// order, which makes the output deterministic but means that for very
13+
// large directories Walk can be inefficient.
14+
// Walk does not follow symbolic links.
15+
func Walk(fs Filesystem, root string, walkFunc filepath.WalkFunc) error {
16+
info, err := fs.Lstat(root)
17+
if err != nil {
18+
err = walkFunc(root, nil, err)
19+
} else {
20+
err = walk(fs, root, info, walkFunc)
21+
}
22+
if err == filepath.SkipDir {
23+
return nil
24+
}
25+
return err
26+
}
27+
28+
// readDirNames reads the directory named by dirname and returns
29+
// a sorted list of directory entries.
30+
func readDirNames(fs Filesystem, dirname string) ([]string, error) {
31+
infos, err := fs.ReadDir(dirname)
32+
if err != nil {
33+
return nil, err
34+
}
35+
names := make([]string, 0, len(infos))
36+
for _, info := range infos {
37+
names = append(names, info.Name())
38+
}
39+
sort.Strings(names)
40+
return names, nil
41+
}
42+
43+
// walk recursively descends path, calling walkFunc.
44+
func walk(fs Filesystem, path string, info os.FileInfo, walkFunc filepath.WalkFunc) error {
45+
if !info.IsDir() {
46+
return walkFunc(path, info, nil)
47+
}
48+
49+
names, err := readDirNames(fs, path)
50+
err1 := walkFunc(path, info, err)
51+
// If err != nil, walk can't walk into this directory.
52+
// err1 != nil means walkFn want walk to skip this directory or stop walking.
53+
// Therefore, if one of err and err1 isn't nil, walk will return.
54+
if err != nil || err1 != nil {
55+
// The caller's behavior is controlled by the return value, which is decided
56+
// by walkFn. walkFn may ignore err and return nil.
57+
// If walkFn returns SkipDir, it will be handled by the caller.
58+
// So walk should return whatever walkFn returns.
59+
return err1
60+
}
61+
62+
for _, name := range names {
63+
filename := filepath.Join(path, name)
64+
fileInfo, err := fs.Lstat(filename)
65+
if err != nil {
66+
if err := walkFunc(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
67+
return err
68+
}
69+
} else {
70+
err = walk(fs, filename, fileInfo, walkFunc)
71+
if err != nil {
72+
if !fileInfo.IsDir() || err != filepath.SkipDir {
73+
return err
74+
}
75+
}
76+
}
77+
}
78+
return nil
79+
}

0 commit comments

Comments
 (0)