92 lines
3.8 KiB
Rust
92 lines
3.8 KiB
Rust
//! UCXL filesystem watcher.
|
|
//!
|
|
//! This module provides a thin wrapper around the `notify` crate to watch a
|
|
//! directory (or "project") for filesystem events. When a change is detected,
|
|
//! the watcher attempts to construct a corresponding `UCXLAddress` using a
|
|
//! simple heuristic and logs the event. This is primarily used by CHORUS for
|
|
//! reactive workflows such as automatically updating metadata when files are
|
|
//! added, modified or removed.
|
|
|
|
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
|
|
use std::path::Path;
|
|
use std::sync::mpsc::channel;
|
|
use crate::UCXLAddress;
|
|
use std::str::FromStr;
|
|
|
|
/// Represents a watcher rooted at a specific base path.
|
|
///
|
|
/// **What**: Holds the absolute path that the watcher monitors.
|
|
///
|
|
/// **How**: The path is stored as a `PathBuf`. The watcher is created via the
|
|
/// `new` constructor which accepts any type that can be referenced as a `Path`.
|
|
/// The underlying `notify::RecommendedWatcher` is configured with the default
|
|
/// `Config` and set to watch recursively.
|
|
///
|
|
/// **Why**: Encapsulating the watcher logic in a dedicated struct makes it easy
|
|
/// to instantiate multiple independent watchers and keeps the public API tidy.
|
|
pub struct UCXLWatcher {
|
|
base_path: std::path::PathBuf,
|
|
}
|
|
|
|
impl UCXLWatcher {
|
|
/// Creates a new `UCXLWatcher` for the given path.
|
|
///
|
|
/// **What**: Accepts any generic `AsRef<Path>` so callers can pass a `&str`,
|
|
/// `Path`, or `PathBuf`.
|
|
///
|
|
/// **How**: The provided path is converted to a `PathBuf` and stored.
|
|
///
|
|
/// **Why**: Convenience constructor used throughout CHORUS when a watcher is
|
|
/// needed for a project directory.
|
|
pub fn new<P: AsRef<Path>>(path: P) -> Self {
|
|
Self {
|
|
base_path: path.as_ref().to_path_buf(),
|
|
}
|
|
}
|
|
|
|
/// Starts the watch loop, blocking indefinitely while handling events.
|
|
///
|
|
/// **What**: Sets up a channel, creates a `RecommendedWatcher`, and begins
|
|
/// watching the `base_path` recursively. For each incoming event, it
|
|
/// attempts to map the filesystem path to a UCXL address and prints a log.
|
|
///
|
|
/// **How**: Uses the `notify` crate's event API. The heuristic address
|
|
/// format is `ucxl://system:watcher@local:filesystem/#/<relative_path>`.
|
|
/// It parses this string with `UCXLAddress::from_str` and logs the result.
|
|
/// Errors from parsing are ignored (they simply aren't printed).
|
|
///
|
|
/// **Why**: Provides a simple, observable bridge between raw filesystem
|
|
/// changes and the UCXL addressing scheme, allowing other components to react
|
|
/// to changes using a uniform identifier.
|
|
pub fn watch_loop(&self) -> Result<(), Box<dyn std::error::Error>> {
|
|
let (tx, rx) = channel();
|
|
|
|
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
|
watcher.watch(&self.base_path, RecursiveMode::Recursive)?;
|
|
|
|
println!("UCXL Watcher started on {:?}", self.base_path);
|
|
|
|
for res in rx {
|
|
match res {
|
|
Ok(event) => {
|
|
for path in event.paths {
|
|
if let Some(rel_path) = path.strip_prefix(&self.base_path).ok() {
|
|
let rel_str = rel_path.to_string_lossy();
|
|
// Heuristic address mapping: ucxl://system:watcher@local:filesystem/#/path
|
|
let addr_str = format!(
|
|
"ucxl://system:watcher@local:filesystem/#/{}",
|
|
rel_str
|
|
);
|
|
if let Ok(addr) = UCXLAddress::from_str(&addr_str) {
|
|
println!("[UCXL EVENT] {:?} -> {}", event.kind, addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(e) => println!("watch error: {:?}", e),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|