walk.go 2.5 KB

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