use crate::debug::{log, FeludaError, FeludaResult, LogLevel};
use crate::manifest;
use crate::{analyze_dependencies, annotate_compatibility, report_analysis, CheckConfig};
use colored::Colorize;
use notify::{Event, RecursiveMode, Watcher};
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::{Duration, Instant};
fn scan_once(config: &CheckConfig) {
match analyze_dependencies(config) {
Ok((mut analyzed_data, project_license)) => {
if analyzed_data.is_empty() {
log(LogLevel::Warn, "No dependencies found to analyze.");
return;
}
annotate_compatibility(&mut analyzed_data, &project_license, config.strict);
let _ = report_analysis(analyzed_data, project_license, config);
}
Err(e) => {
e.log();
}
}
}
fn event_touches_dependency(result: ¬ify::Result<Event>) -> bool {
match result {
Ok(event) => event.paths.iter().any(|p| manifest::is_relevant_change(p)),
Err(_) => false,
}
}
pub fn handle_watch_command(config: CheckConfig, debounce_ms: u64) -> FeludaResult<()> {
let path = config.path.clone();
let root = Path::new(&path);
if !root.exists() {
eprintln!("β Watch path does not exist: {path}");
return Err(FeludaError::InvalidData(format!(
"Watch path does not exist: {path}"
)));
}
log(
LogLevel::Info,
&format!("Starting watch mode on path: {path}"),
);
scan_once(&config);
let watched = manifest::discover_dependency_files(root);
println!(
"\n{} {}",
"π Watching".bright_cyan().bold(),
format!(
"{} ({} dependency file{} tracked) β press Ctrl-C to stop",
path,
watched.len(),
if watched.len() == 1 { "" } else { "s" }
)
.bright_cyan()
);
if watched.is_empty() {
println!(
"{}",
"β No dependency files found yet; will react when one appears.".yellow()
);
}
let (tx, rx) = channel::<notify::Result<Event>>();
let mut watcher = notify::recommended_watcher(move |res| {
let _ = tx.send(res);
})
.map_err(|e| FeludaError::InvalidData(format!("Failed to initialize file watcher: {e}")))?;
watcher
.watch(root, RecursiveMode::Recursive)
.map_err(|e| FeludaError::InvalidData(format!("Failed to watch {path}: {e}")))?;
let debounce = Duration::from_millis(debounce_ms);
loop {
let first = match rx.recv() {
Ok(event) => event,
Err(_) => {
log(LogLevel::Warn, "Watch channel closed, stopping watch mode");
break;
}
};
if !event_touches_dependency(&first) {
continue;
}
let deadline = Instant::now() + debounce;
while let Some(remaining) = deadline.checked_duration_since(Instant::now()) {
match rx.recv_timeout(remaining) {
Ok(_) => continue,
Err(_) => break,
}
}
println!(
"\n{} {}",
"π".bright_yellow(),
"Dependency change detected β re-scanningβ¦"
.bright_yellow()
.bold()
);
scan_once(&config);
}
Ok(())
}