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