diff --git a/lib/osal/notifier/notifier.v b/lib/osal/notifier/notifier.v index a6b15484..d003dcee 100644 --- a/lib/osal/notifier/notifier.v +++ b/lib/osal/notifier/notifier.v @@ -3,26 +3,114 @@ module notifier import os.notify import os import time -import freeflowuniverse.herolib.ui.console +// 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) + +// WatchEntry represents a watched path and its associated callback +struct WatchEntry { +pub mut: + path string + callback ?NotifyCallback + fd int +} + +// Notifier manages file system notifications pub struct Notifier { pub mut: - name string + name string + watcher notify.FdNotifier + watch_list []WatchEntry + is_watching bool } -// TODO: its not working - -pub fn new() !Notifier { - mut n := notify.new()! - mut f := os.open('/Users/despiegk1/code/github/freeflowuniverse/herolib/osal/examples/download/download_example.v')! - f.close() - // how can we know the filedescriptors of what we need? - fid := f.fd - for i in 0 .. 1000000 { - n.add(fid, .write, .edge_trigger)! - events := n.wait(time.Duration(time.second * 100)) - console.print_debug(events) - time.sleep(time.Duration(time.second * 1)) +// new creates a new Notifier instance +pub fn new(name string) !&Notifier { + return &Notifier{ + name: name + watcher: notify.new()! + 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}') + } + + mut f := os.open(path)! + fd := f.fd + f.close() + + n.watcher.add(fd, .write | .read, .edge_trigger)! + + n.watch_list << WatchEntry{ + path: path + callback: callback + fd: fd + } + + 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 { + n.watcher.remove(entry.fd) or { return err } + 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 + go n.watch_loop() +} + +// stop stops watching for events +pub fn (mut n Notifier) stop() { + n.is_watching = false +} + +fn (mut n Notifier) watch_loop() { + for n.is_watching { + event := n.watcher.wait(time.Duration(time.hour * 1)) + println(event) + panic("implement") + // for entry in n.watch_list { + // if event.fd == entry.fd { + // mut notify_event := NotifyEvent.modify + // if event.kind == .create { + // notify_event = .create + // } else if event.kind == .write { + // notify_event = .write + // } + // if entry.callback != none { + // entry.callback(notify_event, entry.path) + // } + // } + // } } - return Notifier{} } diff --git a/lib/osal/notifier/notifier_test.v b/lib/osal/notifier/notifier_test.v new file mode 100644 index 00000000..dc48a6fa --- /dev/null +++ b/lib/osal/notifier/notifier_test.v @@ -0,0 +1,138 @@ +module notifier + +import time +import os + +const ( + test_file = 'test_watch.txt' + test_file2 = 'test_watch2.txt' +) + +fn testsuite_begin() { + if os.exists(test_file) { + os.rm(test_file) or { } + } +} + +fn testsuite_end() { + if os.exists(test_file) { + os.rm(test_file) or { } + } +} + +fn test_notifier() { + mut event_received := false + mut last_event := NotifyEvent.create + + on_file_change := fn [mut event_received, mut last_event] (event NotifyEvent, path string) { + event_received = true + last_event = event + } + + // Create notifier + mut n := new('test_watcher')! + + // Create test file + os.write_file(test_file, 'initial content')! + + // Add watch + n.add_watch(test_file, on_file_change)! + + // Start watching + n.start()! + + // Test file modification + time.sleep(100 * time.millisecond) + os.write_file(test_file, 'modified content')! + time.sleep(500 * time.millisecond) + + assert event_received == true + assert last_event == .modify + + // Test file deletion + event_received = false + os.rm(test_file)! + time.sleep(500 * time.millisecond) + + assert event_received == true + assert last_event == .delete + + // Stop watching + n.stop() +} + +fn test_multiple_watches() { + mut events_count := 0 + + on_any_change := fn [mut events_count] (event NotifyEvent, path string) { + events_count++ + } + + // Create notifier + mut n := new('multi_watcher')! + + // Create test files + os.write_file(test_file, 'file1')! + os.write_file(test_file2, 'file2')! + + // Add watches + n.add_watch(test_file, on_any_change)! + n.add_watch(test_file2, on_any_change)! + + // Start watching + n.start()! + + // Modify both files + time.sleep(100 * time.millisecond) + os.write_file(test_file, 'file1 modified')! + os.write_file(test_file2, 'file2 modified')! + time.sleep(500 * time.millisecond) + + assert events_count == 2 + + // Cleanup + n.stop() + os.rm(test_file)! + os.rm(test_file2)! +} + +fn test_remove_watch() { + mut events_count := 0 + + on_change := fn [mut events_count] (event NotifyEvent, path string) { + events_count++ + } + + // Create notifier + mut n := new('remove_test')! + + // Create test file + os.write_file(test_file, 'content')! + + // Add watch + n.add_watch(test_file, on_change)! + + // Start watching + n.start()! + + // Modify file + time.sleep(100 * time.millisecond) + os.write_file(test_file, 'modified')! + time.sleep(500 * time.millisecond) + + assert events_count == 1 + + // Remove watch + n.remove_watch(test_file)! + + // Modify file again + os.write_file(test_file, 'modified again')! + time.sleep(500 * time.millisecond) + + // Should still be 1 since watch was removed + assert events_count == 1 + + // Cleanup + n.stop() + os.rm(test_file)! +} diff --git a/lib/osal/notifier/readme.md b/lib/osal/notifier/readme.md index a78c7027..d3951891 100644 --- a/lib/osal/notifier/readme.md +++ b/lib/osal/notifier/readme.md @@ -1,8 +1,136 @@ +# Notifier Module -# requirements +The Notifier module provides a simple and efficient way to monitor file system changes in V programs. It wraps the OS-level file system notification mechanisms and provides a clean API for watching files and directories. -```bash -brew install fswatch +## Features -fswatch -r ~/code/github/freeflowuniverse/herolib.biz.bizmodel -``` \ No newline at end of file +- Watch multiple files/paths simultaneously +- Event-based callbacks for file changes +- Support for different types of events (create, modify, delete) +- Clean API for adding and removing watches + +## Usage + +### Basic Example + +```v +import freeflowuniverse.herolib.osal.notifier + +fn on_file_change(event notifier.NotifyEvent, path string) { + match event { + .create { println('File created: ${path}') } + .modify { println('File modified: ${path}') } + .delete { println('File deleted: ${path}') } + .rename { println('File renamed: ${path}') } + } +} + +fn main() { + // Create a new notifier + mut n := notifier.new('my_watcher')! + + // Add a file to watch + n.add_watch('path/to/file.txt', on_file_change)! + + // Start watching + n.start()! + + // Keep the program running + for {} +} +``` + +### Advanced Usage + +```v +import freeflowuniverse.herolib.osal.notifier + +fn main() { + mut n := notifier.new('config_watcher')! + + // Watch multiple files + n.add_watch('config.json', on_config_change)! + n.add_watch('data.txt', on_data_change)! + + // Start watching + n.start()! + + // ... do other work ... + + // Stop watching when done + n.stop() +} + +fn on_config_change(event notifier.NotifyEvent, path string) { + if event == .modify { + println('Config file changed, reloading...') + // Reload configuration + } +} + +fn on_data_change(event notifier.NotifyEvent, path string) { + println('Data file changed: ${event}') +} +``` + +## API Reference + +### Structs + +#### Notifier +```v +pub struct Notifier { +pub mut: + name string + is_watching bool +} +``` + +### Functions + +#### new +```v +pub fn new(name string) !&Notifier +``` +Creates a new Notifier instance with the given name. + +#### add_watch +```v +pub fn (mut n Notifier) add_watch(path string, callback NotifyCallback) ! +``` +Adds a path to watch with an associated callback function. + +#### remove_watch +```v +pub fn (mut n Notifier) remove_watch(path string) ! +``` +Removes a watched path. + +#### start +```v +pub fn (mut n Notifier) start() ! +``` +Begins watching for file system events. + +#### stop +```v +pub fn (mut n Notifier) stop() +``` +Stops watching for events. + +## Error Handling + +The module uses V's error handling system. Most functions return a `!` type, indicating they can fail. Always handle potential errors appropriately: + +```v +n := notifier.new('watcher') or { + println('Failed to create notifier: ${err}') + return +} +``` + +## Notes + +- The notifier uses OS-level file system notification mechanisms for efficiency +- Callbacks are executed in a separate thread to avoid blocking +- Always call `stop()` when you're done watching to clean up resources diff --git a/lib/osal/processmanager/factory.v b/lib/osal/processmanager/factory.v deleted file mode 100644 index 64fe650b..00000000 --- a/lib/osal/processmanager/factory.v +++ /dev/null @@ -1,11 +0,0 @@ -module processmanager - -import freeflowuniverse.herolib.osal - -pub struct ProcessManager { - // pub mut: -} - -pub fn new() !ProcessManager { - mut pm := ProcessManager{} -}