cs-midi-docs/internal/content/watcher.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
})
}