-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathrfsnotify.go
133 lines (115 loc) · 3.12 KB
/
rfsnotify.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Package rfsnotify implements recursive folder monitoring by wrapping the excellent fsnotify library
package rfsnotify
import (
"gopkg.in/fsnotify.v1"
"errors"
"os"
"path/filepath"
)
// RWatcher wraps fsnotify.Watcher. When fsnotify adds recursive watches, you should be able to switch your code to use fsnotify.Watcher
type RWatcher struct {
Events chan fsnotify.Event
Errors chan error
done chan struct{}
fsnotify *fsnotify.Watcher
isClosed bool
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*RWatcher, error) {
fsWatch, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
m := &RWatcher{}
m.fsnotify = fsWatch
m.Events = make(chan fsnotify.Event)
m.Errors = make(chan error)
m.done = make(chan struct{})
go m.start()
return m, nil
}
// Add starts watching the named file or directory (non-recursively).
func (m *RWatcher) Add(name string) error {
if m.isClosed {
return errors.New("rfsnotify instance already closed")
}
return m.fsnotify.Add(name)
}
// AddRecursive starts watching the named directory and all sub-directories.
func (m *RWatcher) AddRecursive(name string) error {
if m.isClosed {
return errors.New("rfsnotify instance already closed")
}
if err := m.watchRecursive(name, false); err != nil {
return err
}
return nil
}
// Remove stops watching the the named file or directory (non-recursively).
func (m *RWatcher) Remove(name string) error {
return m.fsnotify.Remove(name)
}
// RemoveRecursive stops watching the named directory and all sub-directories.
func (m *RWatcher) RemoveRecursive(name string) error {
if err := m.watchRecursive(name, true); err != nil {
return err
}
return nil
}
// Close removes all watches and closes the events channel.
func (m *RWatcher) Close() error {
if m.isClosed {
return nil
}
close(m.done)
m.isClosed = true
return nil
}
func (m *RWatcher) start() {
for {
select {
case e := <-m.fsnotify.Events:
s, err := os.Stat(e.Name)
if err == nil && s != nil && s.IsDir() {
if e.Op&fsnotify.Create != 0 {
m.watchRecursive(e.Name, false)
}
}
//Can't stat a deleted directory, so just pretend that it's always a directory and
//try to remove from the watch list... we really have no clue if it's a directory or not...
if e.Op&fsnotify.Remove != 0 {
m.fsnotify.Remove(e.Name)
}
m.Events <- e
case e := <-m.fsnotify.Errors:
m.Errors <- e
case <-m.done:
m.fsnotify.Close()
close(m.Events)
close(m.Errors)
return
}
}
}
// watchRecursive adds all directories under the given one to the watch list.
// this is probably a very racey process. What if a file is added to a folder before we get the watch added?
func (m *RWatcher) watchRecursive(path string, unWatch bool) error {
err := filepath.Walk(path, func(walkPath string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
if unWatch {
if err = m.fsnotify.Remove(walkPath); err != nil {
return err
}
} else {
if err = m.fsnotify.Add(walkPath); err != nil {
return err
}
}
}
return nil
})
return err
}