package main import ( "fmt" "os" "path/filepath" "github.com/fsnotify/fsnotify" ) func WatchDirectory(op fsnotify.Op, path string) (<-chan string, error) { watcher, err := fsnotify.NewWatcher() if err != nil { panic(err) // TODO error handling } err = watcher.Add(path) if err != nil { return nil, err } files, err := os.ReadDir(path) if err != nil { return nil, err } ch := make(chan string, 10) // close channel on app shutdown go func() { <-AppCtx.Done() watcher.Close() }() AppExitWg.Add(1) go func(watcher *fsnotify.Watcher, ch chan<- string) { defer AppExitWg.Done() defer watcher.Close() for _, file := range files { path := filepath.Join(path, file.Name()) if file.IsDir() { //fmt.Println("watching directory", path) watcher.Add(path) if dirFiles, err := os.ReadDir(path); err == nil { for _, dirFile := range dirFiles { ch <- filepath.Join(path, dirFile.Name()) } } } else { ch <- path } } for { select { case event, ok := <-watcher.Events: if !ok { close(ch) return } if fi, err := os.Stat(event.Name); err == nil && fi.IsDir() { if event.Op&fsnotify.Create == fsnotify.Create { //fmt.Println("watching directory", event.Name) watcher.Add(event.Name) } // read dir immediately because directory files could simultanously with its parent directory if dirFiles, err := os.ReadDir(event.Name); err == nil { for _, dirFile := range dirFiles { ch <- filepath.Join(event.Name, dirFile.Name()) } } } else if err != nil && event.Op&fsnotify.Remove == fsnotify.Remove { //fmt.Println("stopped watching directory", event.Name) watcher.Remove(event.Name) } if event.Op&op == op { ch <- event.Name } case err, ok := <-watcher.Errors: if ok { fmt.Println(err, ok) } close(ch) return } } }(watcher, ch) return ch, nil }