6
6
"io/ioutil"
7
7
"os"
8
8
"path/filepath"
9
+ "regexp"
9
10
"strings"
10
11
"sync"
11
12
"time"
24
25
// ErrWatchedFileDeleted is an error that occurs when a file or folder that was
25
26
// being watched has been deleted.
26
27
ErrWatchedFileDeleted = errors .New ("error: watched file or folder deleted" )
28
+
29
+ // ErrSkip is less of an error, but more of a way for path hooks to skip a file or
30
+ // directory.
31
+ ErrSkip = errors .New ("error: skipping file" )
27
32
)
28
33
29
34
// An Op is a type that is used to describe what type
@@ -78,7 +83,31 @@ func (e Event) String() string {
78
83
pathType = "DIRECTORY"
79
84
}
80
85
return fmt .Sprintf ("%s %q %s [%s]" , pathType , e .Name (), e .Op , e .Path )
86
+ }
87
+
88
+ // FilterFileHookFunc is a function that is called to filter files during listings.
89
+ // If a file is ok to be listed, nil is returned otherwise ErrSkip is returned.
90
+ type FilterFileHookFunc func (info os.FileInfo , fullPath string ) error
91
+
92
+ // RegexFilterHook is a function that accepts or rejects a file
93
+ // for listing based on whether it's filename or full path matches
94
+ // a regular expression.
95
+ func RegexFilterHook (r * regexp.Regexp , useFullPath bool ) FilterFileHookFunc {
96
+ return func (info os.FileInfo , fullPath string ) error {
97
+ str := info .Name ()
98
+
99
+ if useFullPath {
100
+ str = fullPath
101
+ }
102
+
103
+ // Match
104
+ if r .MatchString (str ) {
105
+ return nil
106
+ }
81
107
108
+ // No match.
109
+ return ErrSkip
110
+ }
82
111
}
83
112
84
113
// Watcher describes a process that watches files for changes.
@@ -91,6 +120,7 @@ type Watcher struct {
91
120
92
121
// mu protects the following.
93
122
mu * sync.Mutex
123
+ ffh []FilterFileHookFunc
94
124
running bool
95
125
names map [string ]bool // bool for recursive or not.
96
126
files map [string ]os.FileInfo // map of files.
@@ -128,6 +158,13 @@ func (w *Watcher) SetMaxEvents(delta int) {
128
158
w .mu .Unlock ()
129
159
}
130
160
161
+ // AddFilterHook
162
+ func (w * Watcher ) AddFilterHook (f FilterFileHookFunc ) {
163
+ w .mu .Lock ()
164
+ w .ffh = append (w .ffh , f )
165
+ w .mu .Unlock ()
166
+ }
167
+
131
168
// IgnoreHiddenFiles sets the watcher to ignore any file or directory
132
169
// that starts with a dot.
133
170
func (w * Watcher ) IgnoreHiddenFiles (ignore bool ) {
@@ -203,12 +240,24 @@ func (w *Watcher) list(name string) (map[string]os.FileInfo, error) {
203
240
// Add all of the files in the directory to the file list as long
204
241
// as they aren't on the ignored list or are hidden files if ignoreHidden
205
242
// is set to true.
243
+ outer:
206
244
for _ , fInfo := range fInfoList {
207
245
path := filepath .Join (name , fInfo .Name ())
208
246
_ , ignored := w .ignored [path ]
209
247
if ignored || (w .ignoreHidden && strings .HasPrefix (fInfo .Name (), "." )) {
210
248
continue
211
249
}
250
+
251
+ for _ , f := range w .ffh {
252
+ err := f (fInfo , path )
253
+ if err == ErrSkip {
254
+ continue outer
255
+ }
256
+ if err != nil {
257
+ return nil , err
258
+ }
259
+ }
260
+
212
261
fileList [path ] = fInfo
213
262
}
214
263
return fileList , nil
@@ -245,6 +294,17 @@ func (w *Watcher) listRecursive(name string) (map[string]os.FileInfo, error) {
245
294
if err != nil {
246
295
return err
247
296
}
297
+
298
+ for _ , f := range w .ffh {
299
+ err := f (info , path )
300
+ if err == ErrSkip {
301
+ return nil
302
+ }
303
+ if err != nil {
304
+ return err
305
+ }
306
+ }
307
+
248
308
// If path is ignored and it's a directory, skip the directory. If it's
249
309
// ignored and it's a single file, skip the file.
250
310
_ , ignored := w .ignored [path ]
0 commit comments