github.com/charlievieth/fastwalk@v1.0.3/adapters.go (about)

     1  package fastwalk
     2  
     3  import (
     4  	"io/fs"
     5  	"os"
     6  	"path/filepath"
     7  )
     8  
     9  func isDir(path string, d fs.DirEntry) bool {
    10  	if d.IsDir() {
    11  		return true
    12  	}
    13  	if d.Type()&os.ModeSymlink != 0 {
    14  		if fi, err := StatDirEntry(path, d); err == nil {
    15  			return fi.IsDir()
    16  		}
    17  	}
    18  	return false
    19  }
    20  
    21  // IgnoreDuplicateDirs wraps fs.WalkDirFunc walkFn to make it follow symbolic
    22  // links and ignore duplicate directories (if a symlink points to a directory
    23  // that has already been traversed it is skipped). The walkFn is called for
    24  // for skipped directories, but the directory is not traversed (this is
    25  // required for error handling).
    26  //
    27  // The Config.Follow setting has no effect on the behavior of Walk when
    28  // this wrapper is used.
    29  //
    30  // In most use cases, the returned fs.WalkDirFunc should not be reused between
    31  // in another call to Walk. If it is reused, any previously visited file will
    32  // be skipped.
    33  //
    34  // NOTE: The order of traversal is undefined. Given an "example" directory
    35  // like the one below where "dir" is a directory and "smydir1" and "smydir2"
    36  // are links to it, only one of "dir", "smydir1", or "smydir2" will be
    37  // traversed, but which one is undefined.
    38  //
    39  //	example
    40  //	├── dir
    41  //	├── smydir1 -> dir
    42  //	└── smydir2 -> dir
    43  func IgnoreDuplicateDirs(walkFn fs.WalkDirFunc) fs.WalkDirFunc {
    44  	filter := NewEntryFilter()
    45  	return func(path string, d fs.DirEntry, err error) error {
    46  		// Call walkFn before checking the entry filter so that we
    47  		// don't record directories that are skipped with SkipDir.
    48  		err = walkFn(path, d, err)
    49  		if err != nil {
    50  			if err != filepath.SkipDir && isDir(path, d) {
    51  				filter.Entry(path, d)
    52  			}
    53  			return err
    54  		}
    55  		if isDir(path, d) {
    56  			if filter.Entry(path, d) {
    57  				return filepath.SkipDir
    58  			}
    59  			if d.Type() == os.ModeSymlink {
    60  				return ErrTraverseLink
    61  			}
    62  		}
    63  		return nil
    64  	}
    65  }
    66  
    67  // IgnoreDuplicateFiles wraps walkFn so that symlinks are followed and duplicate
    68  // files are ignored. If a symlink resolves to a file that has already been
    69  // visited it will be skipped.
    70  //
    71  // In most use cases, the returned fs.WalkDirFunc should not be reused between
    72  // in another call to Walk. If it is reused, any previously visited file will
    73  // be skipped.
    74  //
    75  // This can significantly slow Walk as os.Stat() is called for each path
    76  // (on Windows, os.Stat() is only needed for symlinks).
    77  func IgnoreDuplicateFiles(walkFn fs.WalkDirFunc) fs.WalkDirFunc {
    78  	filter := NewEntryFilter()
    79  	return func(path string, d fs.DirEntry, err error) error {
    80  		// Skip all duplicate files, directories, and links
    81  		if filter.Entry(path, d) {
    82  			if isDir(path, d) {
    83  				return filepath.SkipDir
    84  			}
    85  			return nil
    86  		}
    87  		err = walkFn(path, d, err)
    88  		if err == nil && d.Type() == os.ModeSymlink && isDir(path, d) {
    89  			err = ErrTraverseLink
    90  		}
    91  		return err
    92  	}
    93  }
    94  
    95  // IgnorePermissionErrors wraps walkFn so that permission errors are ignored.
    96  // The returned fs.WalkDirFunc may be reused.
    97  func IgnorePermissionErrors(walkFn fs.WalkDirFunc) fs.WalkDirFunc {
    98  	return func(path string, d fs.DirEntry, err error) error {
    99  		if err != nil && os.IsPermission(err) {
   100  			return nil
   101  		}
   102  		return walkFn(path, d, err)
   103  	}
   104  }