use crate::obsidian::sync::SyncEngine;
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc;
pub struct ObsidianWatcher {
engine: Arc<SyncEngine>,
debounce_ms: u64,
}
impl ObsidianWatcher {
pub fn new(engine: Arc<SyncEngine>, debounce_ms: u64) -> Self {
Self {
engine,
debounce_ms,
}
}
pub async fn watch(&self, vault_path: &str) -> Result<(), ObsidianError> {
let (tx, mut rx) = mpsc::channel::<Event>(100);
let tx_clone = tx.clone();
let watch_path = vault_path.to_string();
std::thread::spawn(move || {
let mut watcher = RecommendedWatcher::new(
move |res: Result<Event, notify::Error>| {
if let Ok(event) = res {
let _ = tx_clone.blocking_send(event);
}
},
Config::default().with_poll_interval(Duration::from_secs(1)),
)
.unwrap();
watcher
.watch(Path::new(&watch_path), RecursiveMode::Recursive)
.unwrap();
loop {
std::thread::sleep(Duration::from_secs(1));
}
});
let mut last_event = std::time::Instant::now();
let debounce = Duration::from_millis(self.debounce_ms);
while let Some(event) = rx.recv().await {
if last_event.elapsed() < debounce {
continue;
}
last_event = std::time::Instant::now();
if self.should_sync_event(&event) {
println!("Detected change in: {:?}", event.paths);
if let Err(e) = self.engine.pull().await {
eprintln!("Pull failed: {}", e);
}
}
}
Ok(())
}
fn should_sync_event(&self, event: &Event) -> bool {
use notify::EventKind;
matches!(
event.kind,
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_)
)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ObsidianError {
#[error("IO error: {0}")]
IoError(String),
#[error("Watch error: {0}")]
WatchError(String),
}