...

Source file src/io/fs/walk.go

Documentation: io/fs

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fs
     6  
     7  import (
     8  	"errors"
     9  	"path"
    10  )
    11  
    12  // SkipDir is used as a return value from WalkDirFuncs to indicate that
    13  // the directory named in the call is to be skipped. It is not returned
    14  // as an error by any function.
    15  var SkipDir = errors.New("skip this directory")
    16  
    17  // WalkDirFunc is the type of the function called by WalkDir to visit
    18  // each file or directory.
    19  //
    20  // The path argument contains the argument to WalkDir as a prefix.
    21  // That is, if WalkDir is called with root argument "dir" and finds a file
    22  // named "a" in that directory, the walk function will be called with
    23  // argument "dir/a".
    24  //
    25  // The d argument is the fs.DirEntry for the named path.
    26  //
    27  // The error result returned by the function controls how WalkDir
    28  // continues. If the function returns the special value SkipDir, WalkDir
    29  // skips the current directory (path if d.IsDir() is true, otherwise
    30  // path's parent directory). Otherwise, if the function returns a non-nil
    31  // error, WalkDir stops entirely and returns that error.
    32  //
    33  // The err argument reports an error related to path, signaling that
    34  // WalkDir will not walk into that directory. The function can decide how
    35  // to handle that error; as described earlier, returning the error will
    36  // cause WalkDir to stop walking the entire tree.
    37  //
    38  // WalkDir calls the function with a non-nil err argument in two cases.
    39  //
    40  // First, if the initial fs.Stat on the root directory fails, WalkDir
    41  // calls the function with path set to root, d set to nil, and err set to
    42  // the error from fs.Stat.
    43  //
    44  // Second, if a directory's ReadDir method fails, WalkDir calls the
    45  // function with path set to the directory's path, d set to an
    46  // fs.DirEntry describing the directory, and err set to the error from
    47  // ReadDir. In this second case, the function is called twice with the
    48  // path of the directory: the first call is before the directory read is
    49  // attempted and has err set to nil, giving the function a chance to
    50  // return SkipDir and avoid the ReadDir entirely. The second call is
    51  // after a failed ReadDir and reports the error from ReadDir.
    52  // (If ReadDir succeeds, there is no second call.)
    53  //
    54  // The differences between WalkDirFunc compared to filepath.WalkFunc are:
    55  //
    56  //   - The second argument has type fs.DirEntry instead of fs.FileInfo.
    57  //   - The function is called before reading a directory, to allow SkipDir
    58  //     to bypass the directory read entirely.
    59  //   - If a directory read fails, the function is called a second time
    60  //     for that directory to report the error.
    61  type WalkDirFunc func(path string, d DirEntry, err error) error
    62  
    63  // walkDir recursively descends path, calling walkDirFn.
    64  func walkDir(fsys FS, name string, d DirEntry, walkDirFn WalkDirFunc) error {
    65  	if err := walkDirFn(name, d, nil); err != nil || !d.IsDir() {
    66  		if err == SkipDir && d.IsDir() {
    67  			// Successfully skipped directory.
    68  			err = nil
    69  		}
    70  		return err
    71  	}
    72  
    73  	dirs, err := ReadDir(fsys, name)
    74  	if err != nil {
    75  		// Second call, to report ReadDir error.
    76  		err = walkDirFn(name, d, err)
    77  		if err != nil {
    78  			if err == SkipDir && d.IsDir() {
    79  				err = nil
    80  			}
    81  			return err
    82  		}
    83  	}
    84  
    85  	for _, d1 := range dirs {
    86  		name1 := path.Join(name, d1.Name())
    87  		if err := walkDir(fsys, name1, d1, walkDirFn); err != nil {
    88  			if err == SkipDir {
    89  				break
    90  			}
    91  			return err
    92  		}
    93  	}
    94  	return nil
    95  }
    96  
    97  // WalkDir walks the file tree rooted at root, calling fn for each file or
    98  // directory in the tree, including root.
    99  //
   100  // All errors that arise visiting files and directories are filtered by fn:
   101  // see the fs.WalkDirFunc documentation for details.
   102  //
   103  // The files are walked in lexical order, which makes the output deterministic
   104  // but requires WalkDir to read an entire directory into memory before proceeding
   105  // to walk that directory.
   106  //
   107  // WalkDir does not follow symbolic links found in directories,
   108  // but if root itself is a symbolic link, its target will be walked.
   109  func WalkDir(fsys FS, root string, fn WalkDirFunc) error {
   110  	info, err := Stat(fsys, root)
   111  	if err != nil {
   112  		err = fn(root, nil, err)
   113  	} else {
   114  		err = walkDir(fsys, root, &statDirEntry{info}, fn)
   115  	}
   116  	if err == SkipDir {
   117  		return nil
   118  	}
   119  	return err
   120  }
   121  
   122  type statDirEntry struct {
   123  	info FileInfo
   124  }
   125  
   126  func (d *statDirEntry) Name() string            { return d.info.Name() }
   127  func (d *statDirEntry) IsDir() bool             { return d.info.IsDir() }
   128  func (d *statDirEntry) Type() FileMode          { return d.info.Mode().Type() }
   129  func (d *statDirEntry) Info() (FileInfo, error) { return d.info, nil }
   130  

View as plain text