127 lines
2.5 KiB
Go
127 lines
2.5 KiB
Go
package watcher
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
|
"github.com/mathieu/personotes/internal/indexer"
|
|
)
|
|
|
|
// Watcher observe les modifications dans le repertoire des notes et relance l indexation au besoin.
|
|
type Watcher struct {
|
|
fs *fsnotify.Watcher
|
|
idx *indexer.Indexer
|
|
dir string
|
|
logger *log.Logger
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// Start initialise le watcher et commence la surveillance dans une goroutine.
|
|
func Start(ctx context.Context, dir string, idx *indexer.Indexer, logger *log.Logger) (*Watcher, error) {
|
|
fsWatcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w := &Watcher{
|
|
fs: fsWatcher,
|
|
idx: idx,
|
|
dir: dir,
|
|
logger: logger,
|
|
}
|
|
|
|
if err := w.addDirRecursive(dir); err != nil {
|
|
fsWatcher.Close()
|
|
return nil, err
|
|
}
|
|
|
|
w.wg.Add(1)
|
|
go w.run(ctx)
|
|
|
|
return w, nil
|
|
}
|
|
|
|
// Close arrete le watcher et attend la fin des goroutines.
|
|
func (w *Watcher) Close() error {
|
|
err := w.fs.Close()
|
|
w.wg.Wait()
|
|
return err
|
|
}
|
|
|
|
func (w *Watcher) run(ctx context.Context) {
|
|
defer w.wg.Done()
|
|
|
|
debounce := time.NewTimer(0)
|
|
if !debounce.Stop() {
|
|
<-debounce.C
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case event, ok := <-w.fs.Events:
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if event.Op&fsnotify.Create != 0 {
|
|
if info, err := os.Stat(event.Name); err == nil && info.IsDir() {
|
|
if err := w.addDirRecursive(event.Name); err != nil {
|
|
w.logger.Printf("watcher: ajout repertoire %s impossible: %v", event.Name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if w.shouldReindex(event.Name, event.Op) {
|
|
if !debounce.Stop() {
|
|
select {
|
|
case <-debounce.C:
|
|
default:
|
|
}
|
|
}
|
|
debounce.Reset(200 * time.Millisecond)
|
|
}
|
|
case err, ok := <-w.fs.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
w.logger.Printf("watcher: erreur: %v", err)
|
|
case <-debounce.C:
|
|
w.logger.Printf("watcher: reindexation suite a modification")
|
|
if err := w.idx.Load(w.dir); err != nil {
|
|
w.logger.Printf("watcher: echec reindexation: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *Watcher) shouldReindex(path string, op fsnotify.Op) bool {
|
|
ext := strings.ToLower(filepath.Ext(path))
|
|
if ext != ".md" && op&fsnotify.Remove == 0 && op&fsnotify.Rename == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (w *Watcher) addDirRecursive(root string) error {
|
|
return filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if entry.IsDir() {
|
|
if err := w.fs.Add(path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|