package watcher import ( "context" "log" "os" "path/filepath" "strings" "sync" "time" "github.com/fsnotify/fsnotify" "github.com/mathieu/project-notes/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 }) }