1// Package fs provides filesystem-related functions.
2package fs
3
4import (
5	"os"
6)
7
8// Walker provides a convenient interface for iterating over the
9// descendants of a filesystem path.
10// Successive calls to the Step method will step through each
11// file or directory in the tree, including the root. The files
12// are walked in lexical order, which makes the output deterministic
13// but means that for very large directories Walker can be inefficient.
14// Walker does not follow symbolic links.
15type Walker struct {
16	fs      FileSystem
17	cur     item
18	stack   []item
19	descend bool
20}
21
22type item struct {
23	path string
24	info os.FileInfo
25	err  error
26}
27
28// Walk returns a new Walker rooted at root.
29func Walk(root string) *Walker {
30	return WalkFS(root, new(fs))
31}
32
33// WalkFS returns a new Walker rooted at root on the FileSystem fs.
34func WalkFS(root string, fs FileSystem) *Walker {
35	info, err := fs.Lstat(root)
36	return &Walker{
37		fs:    fs,
38		stack: []item{{root, info, err}},
39	}
40}
41
42// Step advances the Walker to the next file or directory,
43// which will then be available through the Path, Stat,
44// and Err methods.
45// It returns false when the walk stops at the end of the tree.
46func (w *Walker) Step() bool {
47	if w.descend && w.cur.err == nil && w.cur.info.IsDir() {
48		list, err := w.fs.ReadDir(w.cur.path)
49		if err != nil {
50			w.cur.err = err
51			w.stack = append(w.stack, w.cur)
52		} else {
53			for i := len(list) - 1; i >= 0; i-- {
54				path := w.fs.Join(w.cur.path, list[i].Name())
55				w.stack = append(w.stack, item{path, list[i], nil})
56			}
57		}
58	}
59
60	if len(w.stack) == 0 {
61		return false
62	}
63	i := len(w.stack) - 1
64	w.cur = w.stack[i]
65	w.stack = w.stack[:i]
66	w.descend = true
67	return true
68}
69
70// Path returns the path to the most recent file or directory
71// visited by a call to Step. It contains the argument to Walk
72// as a prefix; that is, if Walk is called with "dir", which is
73// a directory containing the file "a", Path will return "dir/a".
74func (w *Walker) Path() string {
75	return w.cur.path
76}
77
78// Stat returns info for the most recent file or directory
79// visited by a call to Step.
80func (w *Walker) Stat() os.FileInfo {
81	return w.cur.info
82}
83
84// Err returns the error, if any, for the most recent attempt
85// by Step to visit a file or directory. If a directory has
86// an error, w will not descend into that directory.
87func (w *Walker) Err() error {
88	return w.cur.err
89}
90
91// SkipDir causes the currently visited directory to be skipped.
92// If w is not on a directory, SkipDir has no effect.
93func (w *Walker) SkipDir() {
94	w.descend = false
95}
96