Skip to content
Snippets Groups Projects
Verified Commit 3bceab71 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: Support for tags #7

parent 8a072c4c
No related branches found
No related tags found
No related merge requests found
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)
// }
// }
//}
......@@ -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
}
......
......@@ -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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment