1
1
package watch
2
2
3
3
import (
4
+ "fmt"
4
5
"os"
5
6
"path/filepath"
6
7
"strings"
@@ -9,99 +10,130 @@ import (
9
10
"github.com/jcelliott/lumber"
10
11
)
11
12
12
- type notify struct {
13
- path string
14
- events chan event
15
- watcher * fsnotify.Watcher
13
+ type event struct {
14
+ file string
15
+ error error
16
+ fsnotify.Event
16
17
}
17
18
18
- func newNotifyWatcher (path string ) Watcher {
19
- return & notify {
20
- path : path ,
21
- events : make (chan event , 10 ),
22
- }
19
+ type Watcher interface {
20
+ watch () error
21
+ eventChan () chan event
22
+ close () error
23
23
}
24
24
25
- // start the watching process and return an error if we cant watch all the files
26
- func (n * notify ) watch () (err error ) {
25
+ type notify struct {
26
+ events chan event // separate event channel so we don't send on all fsnotify.Events
27
+ * fsnotify.Watcher
28
+ }
27
29
28
- n .watcher , err = fsnotify .NewWatcher ()
29
- if err != nil {
30
- return err
30
+ func newRecursiveWatcher (path string ) (Watcher , error ) {
31
+ folders := subfolders (path )
32
+ if len (folders ) == 0 {
33
+ return nil , fmt .Errorf ("No folders to watch" )
31
34
}
32
35
33
- err = filepath . Walk ( n . path , n . walkFunc )
36
+ watcher , err := fsnotify . NewWatcher ( )
34
37
if err != nil {
35
- return err
38
+ return nil , err
36
39
}
37
40
38
- go n . EventHandler ()
41
+ notifyWatcher := & notify { Watcher : watcher , events : make ( chan event )}
39
42
40
- return
43
+ for i := range folders {
44
+ lumber .Info ("Adding %s" , folders [i ])
45
+ err = notifyWatcher .Add (folders [i ])
46
+ if err != nil {
47
+ return nil , err
48
+ }
49
+ }
50
+ return notifyWatcher , nil
41
51
}
42
52
43
- func (n * notify ) eventChan () chan event {
44
- return n .events
45
- }
53
+ func run (watcher * notify ) {
54
+ for {
55
+ select {
56
+ case evnt := <- watcher .Events :
57
+ if shouldIgnoreFile (filepath .Base (evnt .Name )) {
58
+ continue
59
+ }
46
60
47
- // close the watcher
48
- func (n * notify ) close () error {
49
- return n .watcher .Close ()
50
- }
61
+ if evnt .Op & fsnotify .Create == fsnotify .Create {
62
+ fi , err := os .Stat (evnt .Name )
63
+ if err != nil {
64
+ // stat ./4913: no such file or directory
65
+ lumber .Error ("Failed to stat on event - %s" , err .Error ())
66
+ } else if fi .IsDir () {
67
+ lumber .Info ("Detected dir creation: %s" , evnt .Name ) // todo: Debug doesn't work
68
+ if ! shouldIgnoreFile (filepath .Base (evnt .Name )) {
69
+ err = watcher .Add (evnt .Name )
70
+ if err != nil {
71
+ lumber .Error ("ERROR - %s" , err .Error ())
72
+ }
73
+ }
74
+ } else {
75
+ lumber .Info ("Detected creation: %s" , evnt .Name ) // todo: Debug doesn't work
76
+ watcher .events <- event {file : evnt .Name }
77
+ }
78
+ }
51
79
52
- // add a file that is being walked to the watch system
53
- func (n * notify ) walkFunc (path string , info os.FileInfo , err error ) error {
54
- if err != nil {
55
- return err
80
+ if evnt .Op & fsnotify .Write == fsnotify .Write {
81
+ lumber .Info ("Detected modification: %s" , evnt .Name ) // todo: Debug doesn't work
82
+ watcher .events <- event {file : evnt .Name }
83
+ }
84
+
85
+ case err := <- watcher .Errors :
86
+ lumber .Error ("Watcher error encountered - %s" , err .Error ())
87
+ }
56
88
}
89
+ }
57
90
58
- for _ , ignoreName := range ignoreFile {
59
- if strings .HasSuffix (path , ignoreName ) {
60
- lumber .Info ("watcher: skipping %s" , path )
61
- if info .IsDir () {
62
- // if the thing we are ignoring is a directory
91
+ // subfolders returns a slice of subfolders (recursive), including the folder provided.
92
+ func subfolders (path string ) (paths []string ) {
93
+ filepath .Walk (path , func (newPath string , info os.FileInfo , err error ) error {
94
+ if err != nil {
95
+ return err
96
+ }
97
+
98
+ if info .IsDir () {
99
+ name := info .Name ()
100
+ // skip folders that begin with a dot
101
+ if shouldIgnoreFile (name ) && name != "." && name != ".." {
63
102
return filepath .SkipDir
64
103
}
65
- // if its not just skip the file
66
- return nil
104
+ paths = append (paths , newPath )
105
+ }
106
+ return nil
107
+ })
108
+ return paths
109
+ }
110
+
111
+ // shouldIgnoreFile determines if a file should be ignored.
112
+ // Ignore files that start with `.` or `_` or end with `~`.
113
+ func shouldIgnoreFile (name string ) bool {
114
+ for i := range ignoreFile {
115
+ if name == ignoreFile [i ] {
116
+ return true
67
117
}
68
118
}
69
119
70
- return n .watcher .Add (path )
120
+ return strings .HasPrefix (name , "." ) ||
121
+ strings .HasPrefix (name , "_" ) ||
122
+ strings .HasSuffix (name , ".swp" ) ||
123
+ strings .HasSuffix (name , "~" )
71
124
}
72
125
73
- func (n * notify ) EventHandler () {
74
- for {
75
- select {
76
- case e := <- n .watcher .Events :
77
- lumber .Debug ("e: %+v" , e )
78
- switch {
79
- case e .Op & fsnotify .Create == fsnotify .Create :
80
- // a new file/folder was created.. add it
81
- n .watcher .Add (e .Name )
82
- // send an event
83
- n .events <- event {file : e .Name }
84
-
85
- case e .Op & fsnotify .Write == fsnotify .Write :
86
- // a file was written to.. send the event
87
- n .events <- event {file : e .Name }
88
-
89
- case e .Op & fsnotify .Remove == fsnotify .Remove :
90
- // a file was removed. remove it
91
- n .watcher .Remove (e .Name )
92
-
93
- case e .Op & fsnotify .Rename == fsnotify .Rename :
94
- // remove from watcher because we no longer need to watch
95
- n .watcher .Remove (e .Name )
96
-
97
- case e .Op & fsnotify .Chmod == fsnotify .Chmod :
98
- // ignore anything that is just changing modes
99
- // mostlikely it was just a touch
126
+ // start the watching process and return an error if we cant watch all the files
127
+ func (n * notify ) watch () (err error ) {
128
+ go run (n )
129
+ return nil
130
+ }
100
131
101
- }
102
- case err := <- n .watcher .Errors :
103
- n .events <- event {file : "" , error : err }
104
- }
105
- }
132
+ func (n * notify ) eventChan () chan event {
133
+ return n .events
134
+ }
106
135
136
+ // close the watcher
137
+ func (n * notify ) close () error {
138
+ return n .Close ()
107
139
}
0 commit comments