prose/watcher/watcher.go
Naman Sood 1981a291aa
All checks were successful
/ Deploy blog to deuterium (push) Successful in 1m9s
rewrite code a bit
Signed-off-by: Naman Sood <mail@nsood.in>
2024-11-23 02:53:25 -05:00

119 lines
2.6 KiB
Go

package watcher
import (
"iter"
"log"
"os"
"runtime"
"strings"
"github.com/rjeczalik/notify"
)
type WatchEventKind int
const (
Update WatchEventKind = iota
Clean
)
// WatchEvent represents a change to a watched folder. It notes which file
// changed and what change happened to it.
type WatchEvent struct {
File string
Kind WatchEventKind
}
func updated(f string) *WatchEvent {
return &WatchEvent{
File: f,
Kind: Update,
}
}
func cleaned(f string) *WatchEvent {
return &WatchEvent{
File: f,
Kind: Clean,
}
}
// Watcher classifies notify.Events into updates and deletes, and calls the
// respective functions for a file when those events happen to that file.
func Watch(folder string, up Updater[string]) {
cwd, err := os.Getwd()
if err != nil {
log.Fatal("could not get current working directory for listener!")
}
cwd = cwd + "/"
c := make(chan notify.EventInfo, 1)
var events []notify.Event
// inotify events prevent double-firing of
// certain events in Linux.
if runtime.GOOS == "linux" {
events = []notify.Event{
notify.InCloseWrite,
notify.InMovedFrom,
notify.InMovedTo,
notify.InDelete,
}
} else {
events = []notify.Event{
notify.Create,
notify.Remove,
notify.Rename,
notify.Write,
}
}
err = notify.Watch(folder, c, events...)
if err != nil {
log.Fatalf("could not setup watcher for folder %s: %s", folder, err)
}
defer notify.Stop(c)
for {
ei := <-c
log.Printf("event: %s", ei.Event())
switch ei.Event() {
case notify.InCloseWrite, notify.InMovedTo, notify.Create, notify.Rename, notify.Write:
filePath := strings.TrimPrefix(ei.Path(), cwd)
log.Printf("updating file %s", filePath)
err := up.Fetch(strings.TrimPrefix(filePath, folder))
if err != nil {
log.Printf("up.Fetch(%q): %v", filePath, err)
}
case notify.InMovedFrom, notify.InDelete, notify.Remove:
filePath := strings.TrimPrefix(ei.Path(), cwd)
log.Printf("cleaning file %s", filePath)
err := up.Delete(strings.TrimPrefix(filePath, folder))
if err != nil {
log.Printf("up.Delete(%q): %v", filePath, err)
}
}
}
}
// Updater is a key-value store which can be informed when to recompute values
// for a particular key. Updaters are normally also AutoMaps.
type Updater[K any] interface {
Fetch(K) error
Delete(K) error
}
// AutoMap is a key-value store where the values are automatically computed by
// the store itself, based on the key.
type AutoMap[K, V any] interface {
Get(K) (V, bool)
}
// OrderedAutoMap is an AutoMap that provides an iterator over its
// currently-existing keys in a known order.
type OrderedAutoMap[K, V any] interface {
AutoMap[K, V]
All() iter.Seq2[K, V]
}