package file import ( "context" "os" "path/filepath" "sync" "github.com/fsnotify/fsnotify" "github.com/geo-platform/tenant-api/internal/shared/config/runtime" ) type watcher struct { f *file fw *fsnotify.Watcher ctx context.Context cancel context.CancelFunc stopOnce sync.Once } func newWatcher(f *file) (runtime.Watcher, error) { fw, err := fsnotify.NewWatcher() if err != nil { return nil, err } if err := fw.Add(f.path); err != nil { _ = fw.Close() return nil, err } ctx, cancel := context.WithCancel(context.Background()) return &watcher{f: f, fw: fw, ctx: ctx, cancel: cancel}, nil } func (w *watcher) Next() ([]*runtime.KeyValue, error) { for { select { case <-w.ctx.Done(): return nil, w.ctx.Err() case event := <-w.fw.Events: if !w.f.match(event.Name) { continue } if event.Op == fsnotify.Rename { if _, err := os.Stat(event.Name); err == nil || os.IsExist(err) { if err := w.fw.Add(event.Name); err != nil { return nil, err } } } info, err := os.Stat(w.f.path) if err != nil { return nil, err } path := w.f.path if info.IsDir() { path = filepath.Join(w.f.path, filepath.Base(event.Name)) } kv, err := w.f.loadFile(path) if err != nil { return nil, err } return []*runtime.KeyValue{kv}, nil case err := <-w.fw.Errors: return nil, err } } } func (w *watcher) Stop() error { var err error w.stopOnce.Do(func() { w.cancel() err = w.fw.Close() }) return err }