github.com/trdyer/afero@v1.1.1/path.go (about)

     1  // Copyright ©2015 The Go Authors
     2  // Copyright ©2015 Steve Francia <spf@spf13.com>
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package afero
    17  
    18  import (
    19  	"os"
    20  	"path/filepath"
    21  	"sort"
    22  )
    23  
    24  // readDirNames reads the directory named by dirname and returns
    25  // a sorted list of directory entries.
    26  // adapted from https://golang.org/src/path/filepath/path.go
    27  func readDirNames(fs Fs, dirname string) ([]string, error) {
    28  	f, err := fs.Open(dirname)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	names, err := f.Readdirnames(-1)
    33  	f.Close()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	sort.Strings(names)
    38  	return names, nil
    39  }
    40  
    41  // walk recursively descends path, calling walkFn
    42  // adapted from https://golang.org/src/path/filepath/path.go
    43  func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
    44  	err := walkFn(path, info, nil)
    45  	if err != nil {
    46  		if info.IsDir() && err == filepath.SkipDir {
    47  			return nil
    48  		}
    49  		return err
    50  	}
    51  
    52  	if !info.IsDir() {
    53  		return nil
    54  	}
    55  
    56  	names, err := readDirNames(fs, path)
    57  	if err != nil {
    58  		return walkFn(path, info, err)
    59  	}
    60  
    61  	for _, name := range names {
    62  		filename := filepath.Join(path, name)
    63  		fileInfo, err := lstatIfPossible(fs, filename)
    64  		if err != nil {
    65  			if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
    66  				return err
    67  			}
    68  		} else {
    69  			err = walk(fs, filename, fileInfo, walkFn)
    70  			if err != nil {
    71  				if !fileInfo.IsDir() || err != filepath.SkipDir {
    72  					return err
    73  				}
    74  			}
    75  		}
    76  	}
    77  	return nil
    78  }
    79  
    80  // if the filesystem supports it, use Lstat, else use fs.Stat
    81  func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) {
    82  	if lfs, ok := fs.(Lstater); ok {
    83  		fi, _, err := lfs.LstatIfPossible(path)
    84  		return fi, err
    85  	}
    86  	return fs.Stat(path)
    87  }
    88  
    89  // Walk walks the file tree rooted at root, calling walkFn for each file or
    90  // directory in the tree, including root. All errors that arise visiting files
    91  // and directories are filtered by walkFn. The files are walked in lexical
    92  // order, which makes the output deterministic but means that for very
    93  // large directories Walk can be inefficient.
    94  // Walk does not follow symbolic links.
    95  
    96  func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
    97  	return Walk(a.Fs, root, walkFn)
    98  }
    99  
   100  func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
   101  	info, err := lstatIfPossible(fs, root)
   102  	if err != nil {
   103  		return walkFn(root, nil, err)
   104  	}
   105  	return walk(fs, root, info, walkFn)
   106  }