...

Source file src/github.com/shurcooL/httpfs/vfsutil/walk.go

Documentation: github.com/shurcooL/httpfs/vfsutil

     1  package vfsutil
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"os"
     7  	pathpkg "path"
     8  	"path/filepath"
     9  	"sort"
    10  )
    11  
    12  // Walk walks the filesystem rooted at root, calling walkFn for each file or
    13  // directory in the filesystem, including root. All errors that arise visiting files
    14  // and directories are filtered by walkFn. The files are walked in lexical
    15  // order.
    16  func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
    17  	info, err := Stat(fs, root)
    18  	if err != nil {
    19  		return walkFn(root, nil, err)
    20  	}
    21  	return walk(fs, root, info, walkFn)
    22  }
    23  
    24  // readDirNames reads the directory named by dirname and returns
    25  // a sorted list of directory entries.
    26  func readDirNames(fs http.FileSystem, dirname string) ([]string, error) {
    27  	fis, err := ReadDir(fs, dirname)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	names := make([]string, len(fis))
    32  	for i := range fis {
    33  		names[i] = fis[i].Name()
    34  	}
    35  	sort.Strings(names)
    36  	return names, nil
    37  }
    38  
    39  // walk recursively descends path, calling walkFn.
    40  func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
    41  	err := walkFn(path, info, nil)
    42  	if err != nil {
    43  		if info.IsDir() && err == filepath.SkipDir {
    44  			return nil
    45  		}
    46  		return err
    47  	}
    48  
    49  	if !info.IsDir() {
    50  		return nil
    51  	}
    52  
    53  	names, err := readDirNames(fs, path)
    54  	if err != nil {
    55  		return walkFn(path, info, err)
    56  	}
    57  
    58  	for _, name := range names {
    59  		filename := pathpkg.Join(path, name)
    60  		fileInfo, err := Stat(fs, filename)
    61  		if err != nil {
    62  			if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
    63  				return err
    64  			}
    65  		} else {
    66  			err = walk(fs, filename, fileInfo, walkFn)
    67  			if err != nil {
    68  				if !fileInfo.IsDir() || err != filepath.SkipDir {
    69  					return err
    70  				}
    71  			}
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  // WalkFilesFunc is the type of the function called for each file or directory visited by WalkFiles.
    78  // It's like filepath.WalkFunc, except it provides an additional ReadSeeker parameter for file being visited.
    79  type WalkFilesFunc func(path string, info os.FileInfo, rs io.ReadSeeker, err error) error
    80  
    81  // WalkFiles walks the filesystem rooted at root, calling walkFn for each file or
    82  // directory in the filesystem, including root. In addition to FileInfo, it passes an
    83  // ReadSeeker to walkFn for each file it visits.
    84  func WalkFiles(fs http.FileSystem, root string, walkFn WalkFilesFunc) error {
    85  	file, info, err := openStat(fs, root)
    86  	if err != nil {
    87  		return walkFn(root, nil, nil, err)
    88  	}
    89  	return walkFiles(fs, root, info, file, walkFn)
    90  }
    91  
    92  // walkFiles recursively descends path, calling walkFn.
    93  // It closes the input file after it's done with it, so the caller shouldn't.
    94  func walkFiles(fs http.FileSystem, path string, info os.FileInfo, file http.File, walkFn WalkFilesFunc) error {
    95  	err := walkFn(path, info, file, nil)
    96  	file.Close()
    97  	if err != nil {
    98  		if info.IsDir() && err == filepath.SkipDir {
    99  			return nil
   100  		}
   101  		return err
   102  	}
   103  
   104  	if !info.IsDir() {
   105  		return nil
   106  	}
   107  
   108  	names, err := readDirNames(fs, path)
   109  	if err != nil {
   110  		return walkFn(path, info, nil, err)
   111  	}
   112  
   113  	for _, name := range names {
   114  		filename := pathpkg.Join(path, name)
   115  		file, fileInfo, err := openStat(fs, filename)
   116  		if err != nil {
   117  			if err := walkFn(filename, nil, nil, err); err != nil && err != filepath.SkipDir {
   118  				return err
   119  			}
   120  		} else {
   121  			err = walkFiles(fs, filename, fileInfo, file, walkFn)
   122  			// file is closed by walkFiles, so we don't need to close it here.
   123  			if err != nil {
   124  				if !fileInfo.IsDir() || err != filepath.SkipDir {
   125  					return err
   126  				}
   127  			}
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // openStat performs Open and Stat and returns results, or first error encountered.
   134  // The caller is responsible for closing the returned file when done.
   135  func openStat(fs http.FileSystem, name string) (http.File, os.FileInfo, error) {
   136  	f, err := fs.Open(name)
   137  	if err != nil {
   138  		return nil, nil, err
   139  	}
   140  	fi, err := f.Stat()
   141  	if err != nil {
   142  		f.Close()
   143  		return nil, nil, err
   144  	}
   145  	return f, fi, nil
   146  }
   147  

View as plain text