notifier
This commit is contained in:
@@ -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{}
|
||||
}
|
||||
|
||||
138
lib/osal/notifier/notifier_test.v
Normal file
138
lib/osal/notifier/notifier_test.v
Normal file
@@ -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)!
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
- 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
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
module processmanager
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
|
||||
pub struct ProcessManager {
|
||||
// pub mut:
|
||||
}
|
||||
|
||||
pub fn new() !ProcessManager {
|
||||
mut pm := ProcessManager{}
|
||||
}
|
||||
Reference in New Issue
Block a user