github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/os/fsi/common/walk.go (about)

     1  // Package common - contains convenience functions,
     2  // built on top of fsi interfaces;
     3  // thus independent of implementations.
     4  // Sadly we cannot attach methods to interfaces.
     5  // Generic filesystem utils should be implemented here.
     6  // find, compact, export ...
     7  package common
     8  
     9  import (
    10  	"errors"
    11  	"os"
    12  	pth "path"
    13  
    14  	"github.com/pbberlin/tools/os/fsi"
    15  )
    16  
    17  // SkipDir is an "error", which a walk-function can
    18  // return, in order to signal, that walk should not traverse into this dir.
    19  var SkipDir = errors.New("skip this directory")
    20  
    21  type WalkFunc func(path string, info os.FileInfo, err error) error
    22  
    23  var cntr = 0
    24  
    25  // walk recursively descends path, calling walkFn.
    26  func walk(fs fsi.FileSystem, path string, info os.FileInfo, walkFn WalkFunc) error {
    27  
    28  	// cntr++
    29  	// if cntr > 20 {
    30  	// 	return fmt.Errorf("too many recursions")
    31  	// }
    32  
    33  	err := walkFn(path, info, nil)
    34  	if err != nil {
    35  		if info.IsDir() && err == SkipDir {
    36  			return nil
    37  		}
    38  		return err
    39  	}
    40  
    41  	if !info.IsDir() {
    42  		return nil
    43  	}
    44  
    45  	fis, err := fs.ReadDir(path)
    46  	// fnd := ""
    47  	// for i := 0; i < len(fis); i++ {
    48  	// 	fnd += fis[i].Name() + ", "
    49  	// }
    50  	// log.Printf("readdir of %-26v  => %v, %v", path, len(fis), fnd)
    51  	if err != nil && err != fsi.EmptyQueryResult {
    52  		return walkFn(path, info, err)
    53  	}
    54  
    55  	//
    56  	for _, fi := range fis {
    57  		filename := pth.Join(path, pth.Base(fi.Name()))
    58  
    59  		fileInfo, err := fs.Lstat(filename)
    60  		if err != nil {
    61  			if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
    62  				return err
    63  			}
    64  		} else {
    65  			err = walk(fs, filename, fileInfo, walkFn)
    66  			if err != nil {
    67  				if !fileInfo.IsDir() || err != SkipDir {
    68  					return err
    69  				}
    70  			}
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  // Walk walks the file tree rooted at root, calling walkFn for each file or
    77  // directory in the tree, including root.
    78  //
    79  // It requires only the fsi.FileSystem interface, and is therefore implementation independent.
    80  //
    81  // It is similar to filepath.Walk(root string, walkFunc)
    82  //
    83  // Directories are crawled in order of fs.ReadDir()
    84  // Walk crawls directories first, files second.
    85  //
    86  // Errors that arise visiting directories can be filtered by walkFn.
    87  //
    88  // Walk does not follow symbolic links.
    89  func Walk(fs fsi.FileSystem, root string, walkFn WalkFunc) error {
    90  	info, err := fs.Lstat(root)
    91  	if err != nil {
    92  		// log.Printf("walk start error %10v %v", root, err)
    93  		return walkFn(root, nil, err)
    94  	}
    95  	// log.Printf("walk start fnd %v", info.Name())
    96  	return walk(fs, root, info, walkFn)
    97  }