forked from struckchure/gv
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwatcher.go
More file actions
140 lines (120 loc) · 2.71 KB
/
watcher.go
File metadata and controls
140 lines (120 loc) · 2.71 KB
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
134
135
136
137
138
139
140
package gv
import (
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/fsnotify/fsnotify"
)
type FileCallback func(path string)
type Watcher struct {
watcher *fsnotify.Watcher
rootPath string
callback FileCallback
ignorePaths []string
closeOnce sync.Once
done chan struct{}
}
func NewWatcher(rootPath string, ignorePaths []string, cb FileCallback) (*Watcher, error) {
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
return &Watcher{
watcher: w,
rootPath: rootPath,
ignorePaths: ignorePaths,
callback: cb,
done: make(chan struct{}),
}, nil
}
func (w *Watcher) isIgnored(path string) bool {
for _, ignore := range w.ignorePaths {
if strings.HasPrefix(path, ignore) {
return true
}
}
return false
}
func (w *Watcher) watchRecursive(dir string) error {
return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() && !w.isIgnored(path) {
err := w.watcher.Add(path)
if err != nil {
log.Println("Watcher add error:", err)
return err
}
}
return nil
})
}
func (w *Watcher) listen() {
defer log.Println("File watcher stopped")
for {
select {
case <-w.done:
return
case event, ok := <-w.watcher.Events:
if !ok {
return
}
if w.isIgnored(event.Name) {
log.Printf("Ignoring event for path: %s", event.Name)
continue
}
// Handle directory creation
if event.Op&fsnotify.Create == fsnotify.Create {
// Wait a bit for the file system to stabilize
time.Sleep(100 * time.Millisecond)
info, err := os.Stat(event.Name)
if err == nil && info.IsDir() {
log.Printf("Adding new directory to watch: %s", event.Name)
if err := w.watchRecursive(event.Name); err != nil {
log.Printf("Error adding directory to watch: %s - %v", event.Name, err)
}
}
}
// Handle file events
if event.Op&(fsnotify.Write|fsnotify.Create) != 0 {
info, err := os.Stat(event.Name)
if err == nil && !info.IsDir() {
// Use a separate goroutine to avoid blocking the event loop
go func(path string) {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic in callback: %v", r)
}
}()
w.callback(path)
}(event.Name)
}
}
case err, ok := <-w.watcher.Errors:
if !ok {
return
}
log.Println("Watcher error:", err)
}
}
}
func (w *Watcher) Start() error {
if err := w.watchRecursive(w.rootPath); err != nil {
return err
}
// Start listening in a separate goroutine
go w.listen()
return nil
}
func (w *Watcher) Close() error {
var err error
w.closeOnce.Do(func() {
close(w.done)
err = w.watcher.Close()
})
return err
}