internal/pkg/cli/file/watch.go (76 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package file import ( "io/fs" "path/filepath" "github.com/fsnotify/fsnotify" ) // RecursiveWatcher wraps an fsnotify Watcher to recursively watch all files in a directory. type RecursiveWatcher struct { fsnotifyWatcher *fsnotify.Watcher done chan struct{} closed bool events chan fsnotify.Event errors chan error } // NewRecursiveWatcher returns a RecursiveWatcher which notifies when changes are made to files inside a recursive directory tree. func NewRecursiveWatcher(buffer uint) (*RecursiveWatcher, error) { watcher, err := fsnotify.NewBufferedWatcher(buffer) if err != nil { return nil, err } rw := &RecursiveWatcher{ events: make(chan fsnotify.Event, buffer), errors: make(chan error), fsnotifyWatcher: watcher, done: make(chan struct{}), closed: false, } go rw.start() return rw, nil } // Add recursively adds a directory tree to the list of watched files. func (rw *RecursiveWatcher) Add(path string) error { if rw.closed { return fsnotify.ErrClosed } return filepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error { if err != nil { // swallow error from WalkDir, don't attempt to add to watcher. return nil } if d.IsDir() { return rw.fsnotifyWatcher.Add(p) } return nil }) } // Events returns the events channel. func (rw *RecursiveWatcher) Events() <-chan fsnotify.Event { return rw.events } // Errors returns the errors channel. func (rw *RecursiveWatcher) Errors() <-chan error { return rw.errors } // Close closes the RecursiveWatcher. func (rw *RecursiveWatcher) Close() error { if rw.closed { return nil } rw.closed = true close(rw.done) return rw.fsnotifyWatcher.Close() } func (rw *RecursiveWatcher) start() { for { select { case <-rw.done: close(rw.events) close(rw.errors) return case event := <-rw.fsnotifyWatcher.Events: // handle recursive watch switch event.Op { case fsnotify.Create: if err := rw.Add(event.Name); err != nil { rw.errors <- err } } rw.events <- event case err := <-rw.fsnotifyWatcher.Errors: rw.errors <- err } } }