use crate::registry::ProviderRegistry;
use crate::types::RoutingError;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::{mpsc, Mutex};
pub struct ProviderDirectoryWatcher {
path: PathBuf,
_watcher: RecommendedWatcher,
stop_sender: mpsc::Sender<()>,
}
impl ProviderDirectoryWatcher {
pub fn start(
path: PathBuf,
registry: Arc<Mutex<ProviderRegistry>>,
) -> Result<Self, RoutingError> {
let (tx, rx) = std::sync::mpsc::channel();
let (stop_sender, stop_receiver) = mpsc::channel::<()>(1);
let watcher = notify::recommended_watcher(tx)
.map_err(|e| RoutingError::Io(format!("Failed to create file watcher: {}", e)))?;
let mut watcher = watcher;
watcher
.watch(&path, RecursiveMode::NonRecursive)
.map_err(|e| {
RoutingError::Io(format!("Failed to watch directory {:?}: {}", path, e))
})?;
let reload_path = path.clone();
tokio::spawn(Self::reload_task(rx, stop_receiver, reload_path, registry));
Ok(Self {
path,
_watcher: watcher,
stop_sender,
})
}
async fn reload_task(
event_rx: std::sync::mpsc::Receiver<Result<notify::Event, notify::Error>>,
mut stop_rx: mpsc::Receiver<()>,
path: PathBuf,
registry: Arc<Mutex<ProviderRegistry>>,
) {
loop {
tokio::select! {
_ = stop_rx.recv() => {
tracing::info!(path = ?path, "Provider directory watcher stopped");
break;
}
_ = tokio::time::sleep(tokio::time::Duration::from_millis(100)) => {
let mut has_events = false;
while event_rx.try_recv().is_ok() {
has_events = true;
}
if has_events {
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
while event_rx.try_recv().is_ok() {}
tracing::info!(path = ?path, "Reloading providers from directory");
let mut reg = registry.lock().await;
match reg.load_from_dir(&path).await {
Ok(count) => {
tracing::info!(
path = ?path,
provider_count = count,
"Providers reloaded successfully"
);
}
Err(e) => {
tracing::warn!(
path = ?path,
error = %e,
"Failed to reload providers"
);
}
}
}
}
}
}
}
pub fn path(&self) -> &PathBuf {
&self.path
}
pub async fn stop(self) {
let _ = self.stop_sender.send(()).await;
}
}
impl std::fmt::Debug for ProviderDirectoryWatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProviderDirectoryWatcher")
.field("path", &self.path)
.finish()
}
}