From 3bceab7105749656edee7956bfd7cae1f8dcdd3e Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Mon, 30 Oct 2023 11:20:55 +0100 Subject: [PATCH] feat: Support for tags #7 --- dedup.go | 90 ---------------------------------------------- lighthouse.go | 74 +++++++++++++++++++++++++++++++++++++- lighthouse_test.go | 54 ++++++++++++++++++++++++++-- 3 files changed, 125 insertions(+), 93 deletions(-) delete mode 100644 dedup.go diff --git a/dedup.go b/dedup.go deleted file mode 100644 index b53b8ff..0000000 --- a/dedup.go +++ /dev/null @@ -1,90 +0,0 @@ -package watch - -// -//// dedup is a function that watches a set of paths and prints a message when -//func (w *Watch) Decouple(waitFor time.Duration) { -// if waitFor == 0 { -// waitFor = 100 * time.Millisecond -// } -// go decoupleLoop(w.watcher, waitFor) -// <-make(chan struct{}) // Block forever -//} -// -//// dedupLoop is the main loop for dedup. -//func decoupleLoop(w *fsnotify.Watcher, waitFor time.Duration) { -// var ( -// mu sync.Mutex -// timers = make(map[string]*time.Timer) -// -// printEvent = func(e fsnotify.Event) { -// fmt.Printf("%s: %s\n", e.Op, e.Name) -// -// // Don't need to remove the timer if you don't have a lot of files. -// mu.Lock() -// delete(timers, e.Name) -// mu.Unlock() -// } -// ) -// -// for { -// select { -// // Read from Errors. -// case err, ok := <-w.Errors: -// if !ok { // Channel was closed (i.e. Watcher.Close() was called). -// return -// } -// fmt.Println("error:", err) -// // Read from Events. -// case e, ok := <-w.Events: -// if !ok { // Channel was closed (i.e. Watcher.Close() was called). -// return -// } -// -// if e.Op&fsnotify.Chmod == fsnotify.Chmod { -// //util.Remove(fullPath) -// } -// -// if e.Op&fsnotify.Write == fsnotify.Write { -// //util.Remove(fullPath) -// } -// -// if e.Op&fsnotify.Create == fsnotify.Create { -// //logging.LogInfo("watching: created file: %s", d) -// //scanDirectory(fullPath, "") -// //navigation.AddFile(fullPath) -// } -// -// if e.Op&fsnotify.Remove == fsnotify.Remove { -// //logging.LogInfo("watching: removed file: %s", d) -// //removeFromWatchlist(fullPath) -// //util.Remove(fullPath) -// //navigation.RemoveFile(fullPath) -// } -// -// if e.Op&fsnotify.Rename == fsnotify.Rename { -// //logging.LogInfo("watching: renamed file: %s", event.Name) -// //removeFromWatchlist(fullPath) -// //util.Remove(fullPath) -// //navigation.RemoveFile(fullPath) -// } -// -// // Get timer. -// mu.Lock() -// t, ok := timers[e.Name] -// mu.Unlock() -// -// // No timer yet, so create one. -// if !ok { -// t = time.AfterFunc(math.MaxInt64, func() { printEvent(e) }) -// t.Stop() -// -// mu.Lock() -// timers[e.Name] = t -// mu.Unlock() -// } -// -// // Reset the timer for this path, so it will start from 100ms again. -// t.Reset(waitFor) -// } -// } -//} diff --git a/lighthouse.go b/lighthouse.go index 723cbea..a2b503c 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -15,6 +15,7 @@ type Watch struct { OnChange EventCallback OnDelete EventCallback OnRename EventCallback + Tags []string } type EventErrorCallback func(err error) @@ -64,6 +65,10 @@ type Lighthouse interface { Sync() error IsInSync() bool + + WatchListByTags(tags []string) []string + + RemoveByTags(tags []string) error } // NewLighthouse creates a new lighthouse instance. @@ -121,6 +126,33 @@ func (l *lighthouse) WatchList() []string { } +// WatchListByTags returns a list of all watched paths with the given tags. +func (l *lighthouse) WatchListByTags(tags []string) []string { + + if len(tags) == 0 { + return l.WatchList() + } + + l.mutex.Lock() + defer l.mutex.Unlock() + var list []string + for k := range l.watchers { + + if len(l.watchers[k].Tags) == 0 { + continue + } + + for _, tag := range tags { + if tag == k { + list = append(list, k) + break + } + } + } + return list + +} + // IsInSync returns true if all paths are watched by fsnotify. func (l *lighthouse) IsInSync() bool { l.mutex.Lock() @@ -238,6 +270,46 @@ func (l *lighthouse) Add(watch *Watch) error { return nil } +func (l *lighthouse) RemoveByTags(tags []string) error { + + if len(tags) == 0 { + return nil + } + + l.mutex.Lock() + defer l.mutex.Unlock() + + var errReturn error + + for _, tag := range tags { + + for k := range l.watchers { + + if len(l.watchers[k].Tags) == 0 { + continue + } + + for _, t := range l.watchers[k].Tags { + if t == tag { + err := l.fsnotify.Remove(k) + if err != nil { + if errReturn == nil { + errReturn = err + } else { + errReturn = fmt.Errorf("%w, %v", errReturn, err) + } + } + delete(l.watchers, k) + break + } + } + } + } + + return errReturn + +} + // Remove removes a path from the watcher. If the path is not being watched or // the watcher is not active, an error is returned. func (l *lighthouse) Remove(path string) error { @@ -254,9 +326,9 @@ func (l *lighthouse) Remove(path string) error { return UnwatchedPathError{UnwatchedPath: path} } + err := l.fsnotify.Remove(path) delete(l.watchers, path) - err := l.fsnotify.Remove(path) if err != nil { return err } diff --git a/lighthouse_test.go b/lighthouse_test.go index f9118fd..a62edbc 100644 --- a/lighthouse_test.go +++ b/lighthouse_test.go @@ -2,12 +2,62 @@ package watch import ( "errors" + "github.com/stretchr/testify/assert" "os" "testing" - - "github.com/stretchr/testify/assert" + "time" ) +func TestRemoveByTagsAndWatchListByTags(t *testing.T) { + l := NewLighthouse() + l.SetDebounce(500 * time.Millisecond) + + tmpDir1 := t.TempDir() + tmpDir2 := t.TempDir() + tmpDir3 := t.TempDir() + + // Add a few watches + err := l.Add(&Watch{Path: tmpDir1, Tags: []string{"tag1", "tag2"}}) + if err != nil { + t.Errorf("Failed to add watch: %v", err) + } + + err = l.Add(&Watch{Path: tmpDir2, Tags: []string{"tag1"}}) + if err != nil { + t.Errorf("Failed to add watch: %v", err) + } + + err = l.Add(&Watch{Path: tmpDir3, Tags: []string{"tag2"}}) + if err != nil { + t.Errorf("Failed to add watch: %v", err) + } + + // Test WatchListByTags + list := l.WatchListByTags([]string{"tag1"}) + expectedList := []string{tmpDir1, tmpDir2} + for i, path := range list { + if path != expectedList[i] { + t.Errorf("Expected %s, got %s", expectedList[i], path) + } + } + + // Test RemoveByTags + err = l.RemoveByTags([]string{"tag1"}) + if err != nil { + t.Errorf("Failed to remove by tags: %v", err) + } + + // Confirm removal + if l.IsWatched(tmpDir1) || l.IsWatched(tmpDir2) { + t.Errorf("RemoveByTags did not remove the watches correctly") + } + + // Confirm remaining watch + if !l.IsWatched(tmpDir3) { + t.Errorf("RemoveByTags removed an unrelated watch") + } +} + func TestAddWatch(t *testing.T) { tempDir, err := os.MkdirTemp("", "watchtest") assert.Nil(t, err) -- GitLab