blob: 74968b9fc28f2cd2bacdcc70550b092256b0fe39 [file] [log] [blame]
// Package fs provides filesystem-related functions.
package fs
import (
"errors"
"os"
"path/filepath"
)
// Walker provides a convenient interface for iterating over the
// descendents of a filesystem path. It wraps filepath.Walk with
// an interface patterned after bufio.Scanner and sql.Rows.
// Successive calls to the Step method will step through each
// file or directory in the tree, including the root. The files
// are walked in lexical order, which makes the output deterministic
// but means that for very large directories Walker can be inefficient.
// Walker does not follow symbolic links.
//
// Walking continues until Stop is called or there are no more
// filesystem entries.
type Walker struct {
itemch chan item
cur item
ok bool // is cur valid?
retch chan error
ret error
}
type item struct {
path string
info os.FileInfo
err error
}
// Walk returns a new Walker rooted at root.
func Walk(root string) *Walker {
w := new(Walker)
w.itemch = make(chan item)
w.retch = make(chan error, 1)
go func() {
filepath.Walk(root, w.recv)
close(w.itemch)
}()
return w
}
func (w *Walker) recv(path string, info os.FileInfo, err error) error {
w.itemch <- item{path, info, err}
return <-w.retch
}
// Step advances the Walker to the next file or directory,
// which will then be available through the Path, Stat,
// and Err methods.
// It returns false when the walk stops, either at the
// end of the tree or from a call to Stop.
func (w *Walker) Step() bool {
if w.ok {
w.retch <- w.ret
}
w.cur, w.ok = <-w.itemch
w.ret = nil
return w.ok
}
// Path returns the path to the most recent file or directory
// visited by a call to Step. It contains the argument to Walk
// as a prefix; that is, if Walk is called with "dir", which is
// a directory containing the file "a", Path will return "dir/a".
func (w *Walker) Path() string {
return w.cur.path
}
// Stat returns info for the most recent file or directory
// visited by a call to Step.
func (w *Walker) Stat() os.FileInfo {
return w.cur.info
}
// Err returns the error, if any, for the most recent attempt
// by Step to visit a file or directory. If a directory has
// an error, w will not descend into that directory.
func (w *Walker) Err() error {
return w.cur.err
}
// SkipDir causes the currently visited directory to be skipped.
// If w is not on a directory, SkipDir has no effect.
func (w *Walker) SkipDir() {
if w.ok && w.cur.info.IsDir() {
w.ret = filepath.SkipDir
}
}
// Stop stops w from visiting any more filesystem entries.
// Subsequent calls to Step will return false.
func (w *Walker) Stop() {
select {
case w.retch <- errors.New("stop"):
default:
}
}