| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package fs |
| |
| import ( |
| "errors" |
| pathpkg "path" |
| ) |
| |
| // SkipDir is used as a return value from WalkFuncs to indicate that |
| // the directory named in the call is to be skipped. It is not returned |
| // as an error by any function. |
| var SkipDir = errors.New("skip this directory") |
| |
| // WalkFunc is the type of the function called for each file or directory |
| // visited by Walk. The path argument contains the argument to Walk as a |
| // prefix; that is, if Walk is called with "dir", which is a directory |
| // containing the file "a", the walk function will be called with argument |
| // "dir/a". The info argument is the FileInfo for the named path. |
| // |
| // If there was a problem walking to the file or directory named by path, the |
| // incoming error will describe the problem and the function can decide how |
| // to handle that error (and Walk will not descend into that directory). In the |
| // case of an error, the info argument will be nil. If an error is returned, |
| // processing stops. The sole exception is when the function returns the special |
| // value SkipDir. If the function returns SkipDir when invoked on a directory, |
| // Walk skips the directory's contents entirely. If the function returns SkipDir |
| // when invoked on a non-directory file, Walk skips the remaining files in the |
| // containing directory. |
| type WalkFunc func(path string, info FileInfo, err error) error |
| |
| // walk recursively descends path, calling walkFn. |
| func walk(fsys FS, path string, info FileInfo, walkFn WalkFunc) error { |
| if !info.IsDir() { |
| return walkFn(path, info, nil) |
| } |
| |
| infos, err := ReadDir(fsys, path) |
| err1 := walkFn(path, info, err) |
| // If err != nil, walk can't walk into this directory. |
| // err1 != nil means walkFn want walk to skip this directory or stop walking. |
| // Therefore, if one of err and err1 isn't nil, walk will return. |
| if err != nil || err1 != nil { |
| // The caller's behavior is controlled by the return value, which is decided |
| // by walkFn. walkFn may ignore err and return nil. |
| // If walkFn returns SkipDir, it will be handled by the caller. |
| // So walk should return whatever walkFn returns. |
| return err1 |
| } |
| |
| for _, info := range infos { |
| filename := pathpkg.Join(path, info.Name()) |
| err = walk(fsys, filename, info, walkFn) |
| if err != nil { |
| if !info.IsDir() || err != SkipDir { |
| return err |
| } |
| } |
| } |
| return nil |
| } |
| |
| // Walk walks the file tree rooted at root, calling walkFn for each file or |
| // directory in the tree, including root. All errors that arise visiting files |
| // and directories are filtered by walkFn. The files are walked in lexical |
| // order, which makes the output deterministic but means that for very |
| // large directories Walk can be inefficient. |
| func Walk(fsys FS, root string, walkFn WalkFunc) error { |
| info, err := Stat(fsys, root) |
| if err != nil { |
| err = walkFn(root, nil, err) |
| } else { |
| err = walk(fsys, root, info, walkFn) |
| } |
| if err == SkipDir { |
| return nil |
| } |
| return err |
| } |