use crate::{
daemon_base_url, fix_stale_lock, run_doctor_checks, run_reindex, CheckResult, EmptyIndex,
};
use anyhow::Result;
use colored::Colorize;
pub async fn handle_doctor(fix: bool) -> Result<()> {
println!("\ntrusty-search doctor\n");
println!("Checking configuration...\n");
crate::commands::daemon_guard::ensure_daemon_running_or_exit(&daemon_base_url()).await;
let (checks, empty_indexes) = run_doctor_checks().await;
for check in &checks {
check.print();
}
let errors = checks.iter().filter(|c| c.is_error()).count();
let warnings = checks.iter().filter(|c| c.is_warn()).count();
if fix {
apply_fixes(&checks, &empty_indexes).await;
}
print_summary(errors, warnings, fix);
if errors > 0 {
std::process::exit(1);
}
Ok(())
}
async fn apply_fixes(checks: &[CheckResult], empty_indexes: &[EmptyIndex]) {
let mut fixed_any = false;
let data_dir = dirs::data_local_dir()
.map(|d| d.join("trusty-search"))
.unwrap_or_else(|| std::path::PathBuf::from("~/.local/share/trusty-search"));
let lock_path = data_dir.join("daemon.lock");
if lock_path.exists() {
let pid_opt = std::fs::read_to_string(&lock_path)
.ok()
.and_then(|s| s.trim().parse::<u32>().ok());
let stale = pid_opt
.map(|pid| unsafe { libc::kill(pid as libc::pid_t, 0) } != 0)
.unwrap_or(true);
if stale {
println!("\nFixing issues...");
fix_stale_lock(&data_dir);
fixed_any = true;
}
}
if !empty_indexes.is_empty() {
if !fixed_any {
println!("\nFixing issues...");
fixed_any = true;
}
for idx in empty_indexes {
if idx.root_path.is_empty() {
eprintln!(
" {} '{}' has no root_path — cannot auto-fix; run `trusty-search index` manually",
"⚠".yellow(),
idx.name
);
continue;
}
let root = std::path::PathBuf::from(&idx.root_path);
println!(" Indexing '{}'...", idx.name);
match run_reindex(&idx.name, &root, 600).await {
Ok(()) => println!(" {} '{}' done", "✓".green(), idx.name),
Err(e) => println!(" {} '{}' failed: {e}", "✗".red(), idx.name),
}
}
}
let has_model_warn = checks.iter().any(|c| {
matches!(c, CheckResult::Warn(msg) if msg.contains("not cached") || msg.contains("not found"))
});
if has_model_warn {
if !fixed_any {
println!("\nFixing issues...");
}
println!(
" {} Model downloads automatically on `trusty-search start` — no manual action needed",
"·".dimmed()
);
}
}
fn print_summary(errors: usize, warnings: usize, fix: bool) {
println!();
if errors == 0 && warnings == 0 {
println!("{}", "Everything looks good!".green().bold());
return;
}
if errors > 0 || warnings > 0 {
println!(
"Issues found: {} warning{}, {} error{}",
warnings,
if warnings == 1 { "" } else { "s" },
errors,
if errors == 1 { "" } else { "s" }
);
}
if !fix {
println!(
"Run {} to attempt automatic repair.",
"trusty-search doctor --fix".cyan()
);
}
}