94 lines
1.6 KiB
Go
94 lines
1.6 KiB
Go
package content
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
type Watcher struct {
|
|
root string
|
|
mu sync.RWMutex
|
|
tree *Node
|
|
onChange []func()
|
|
}
|
|
|
|
func NewWatcher(root string) (*Watcher, error) {
|
|
tree, err := BuildTree(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Watcher{root: root, tree: tree}, nil
|
|
}
|
|
|
|
func (w *Watcher) Tree() *Node {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.tree
|
|
}
|
|
|
|
func (w *Watcher) Rebuild() error {
|
|
tree, err := BuildTree(w.root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.mu.Lock()
|
|
w.tree = tree
|
|
w.mu.Unlock()
|
|
for _, fn := range w.onChange {
|
|
fn()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *Watcher) OnChange(fn func()) {
|
|
w.onChange = append(w.onChange, fn)
|
|
}
|
|
|
|
func (w *Watcher) Start() {
|
|
fw, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
log.Printf("fsnotify: %v", err)
|
|
return
|
|
}
|
|
walkAndWatch(fw, w.root)
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case ev, ok := <-fw.Events:
|
|
if !ok {
|
|
return
|
|
}
|
|
if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Remove) ||
|
|
ev.Has(fsnotify.Write) || ev.Has(fsnotify.Rename) {
|
|
if err := w.Rebuild(); err != nil {
|
|
log.Printf("rebuild: %v", err)
|
|
}
|
|
}
|
|
case err, ok := <-fw.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
log.Printf("fsnotify error: %v", err)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func walkAndWatch(fw *fsnotify.Watcher, root string) {
|
|
filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil || !d.IsDir() {
|
|
return nil
|
|
}
|
|
if d.Name()[0] == '.' && path != root {
|
|
return filepath.SkipDir
|
|
}
|
|
fw.Add(path)
|
|
return nil
|
|
})
|
|
}
|