1 package vfsutil
2
3 import (
4 "io"
5 "net/http"
6 "os"
7 pathpkg "path"
8 "path/filepath"
9 "sort"
10 )
11
12
13
14
15
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
25
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
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
78
79 type WalkFilesFunc func(path string, info os.FileInfo, rs io.ReadSeeker, err error) error
80
81
82
83
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
93
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
123 if err != nil {
124 if !fileInfo.IsDir() || err != filepath.SkipDir {
125 return err
126 }
127 }
128 }
129 }
130 return nil
131 }
132
133
134
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