use crate::admin::daemon::{generate, parse_admin_file};
use crate::utils::trad::{t, tf};
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::{
fs,
path::Path,
sync::mpsc,
time::{Duration, Instant},
};
pub(crate) fn watch(admin_path: &Path, main_path: &Path) -> Result<(), String> {
let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
let mut watcher: RecommendedWatcher = RecommendedWatcher::new(tx, Config::default())
.map_err(|e| format!("Unable to create watcher: {}", e))?;
watcher
.watch(admin_path, RecursiveMode::NonRecursive)
.map_err(|e| format!("Unable to watch {}: {}", admin_path.display(), e))?;
if main_path.exists() {
watcher
.watch(main_path, RecursiveMode::NonRecursive)
.map_err(|e| format!("Unable to watch {}: {}", main_path.display(), e))?;
}
run_generation(admin_path);
let mut last_event = Instant::now()
.checked_sub(Duration::from_secs(10))
.unwrap_or_else(Instant::now);
let debounce = Duration::from_millis(300);
for event in rx {
match event {
Ok(ev) => {
if is_write_event(&ev) {
let now = Instant::now();
if now.duration_since(last_event) > debounce {
last_event = now;
let is_main = ev.paths.iter().any(|p| {
p.file_name()
.and_then(|n| n.to_str())
.map(|n| n == "main.rs")
.unwrap_or(false)
});
if is_main {
if !admin_still_active(main_path) {
println!("\n {}", t("daemon.with_admin_disabled"));
return Ok(());
}
continue;
}
println!("\n {}", t("daemon.modification_detected"));
run_generation(admin_path);
}
}
}
Err(e) => eprintln!(" {}", tf("daemon.watcher_error", &[&e.to_string()])),
}
}
Ok(())
}
fn admin_still_active(main_path: &Path) -> bool {
let Ok(source) = fs::read_to_string(main_path) else {
return false;
};
source.lines().any(|line| {
let trimmed = line.trim();
!trimmed.starts_with("//") && trimmed.contains(".with_admin(")
})
}
fn is_write_event(event: &Event) -> bool {
matches!(event.kind, EventKind::Modify(_) | EventKind::Create(_))
}
fn run_generation(admin_path: &Path) {
let source = match fs::read_to_string(admin_path) {
Ok(s) => s,
Err(e) => {
eprintln!(" {}", tf("daemon.unable_read", &[&e.to_string()]));
return;
}
};
match parse_admin_file(&source) {
Err(e) => {
eprintln!(" {}", tf("daemon.parse_error", &[&e.to_string()]));
}
Ok(parsed) => {
if parsed.resources.is_empty() {
println!(" {}", t("daemon.no_resource"));
return;
}
match generate(&parsed) {
Ok(()) => {
println!(" {}", t("daemon.operational"));
}
Err(e) => {
eprintln!(" {}", tf("daemon.generation_error", &[&e.to_string()]));
}
}
}
}
}