module notifier import os import time // NotifyEvent represents the type of file system event pub enum NotifyEvent { create modify delete rename } // NotifyCallback is the function signature for event callbacks pub type NotifyCallback = fn (event NotifyEvent, path string, args map[string]string) // WatchEntry represents a watched path and its associated callback struct WatchEntry { pub mut: path string callback ?NotifyCallback pid int } // Notifier manages file system notifications using fswatch pub struct Notifier { pub mut: name string watch_list []WatchEntry is_watching bool args map[string]string } // new creates a new Notifier instance pub fn new(name string) !&Notifier { // Check if fswatch is installed if !os.exists_in_system_path('fswatch') { return error('fswatch is not installed. Please install it first (brew install fswatch).') } return &Notifier{ name: name watch_list: []WatchEntry{} is_watching: false } } // add_watch adds a path to watch with an associated callback pub fn (mut n Notifier) add_watch(path string, callback NotifyCallback) ! { if !os.exists(path) { return error('Path does not exist: ${path}') } n.watch_list << WatchEntry{ path: path callback: callback pid: 0 } println('Added watch for: ${path}') } // remove_watch removes a watched path pub fn (mut n Notifier) remove_watch(path string) ! { for i, entry in n.watch_list { if entry.path == path { if entry.pid > 0 { os.system('kill ${entry.pid}') } n.watch_list.delete(i) println('Removed watch for: ${path}') return } } return error('Path not found in watch list: ${path}') } // start begins watching for events pub fn (mut n Notifier) start() ! { if n.is_watching { return error('Notifier is already watching') } if n.watch_list.len == 0 { return error('No paths are being watched') } n.is_watching = true if n.watch_list.len > 1 { return error('only support watchers with len 1 for now') } // Start a watcher for each path for mut entry in n.watch_list { // spawn n.watch_path(mut entry) n.watch_path(mut entry) } } // stop stops watching for events pub fn (mut n Notifier) stop() { n.is_watching = false // Kill all fswatch processes for entry in n.watch_list { if entry.pid > 0 { os.system('kill ${entry.pid}') } } } fn (mut n Notifier) watch_path(mut entry WatchEntry) { // Start fswatch process mut p := os.new_process('/opt/homebrew/bin/fswatch') p.set_args(['-x', '--event-flags', entry.path]) p.set_redirect_stdio() p.run() entry.pid = p.pid for n.is_watching { line := p.stdout_read() if line.len > 0 { parts := line.split(' ') if parts.len >= 2 { path := parts[0] flags := parts[1] mut event := NotifyEvent.modify // Default to modify // Parse fswatch event flags // See: https://emcrisostomo.github.io/fswatch/doc/1.17.1/fswatch.html#Event-Flags if flags.contains('Created') { event = .create } else if flags.contains('Removed') { event = .delete } else if flags.contains('Renamed') { event = .rename } else if flags.contains('Updated') || flags.contains('Modified') { event = .modify } if cb := entry.callback { cb(event, path, n.args) } } } time.sleep(100 * time.millisecond) } p.close() }