use crate::db::Database;
use crate::models::downloader::{
get_dense_model_dir, get_model_dir, is_dense_model_installed, is_model_installed,
DENSE_MODEL_ID, MODEL_ID,
};
use anyhow::Result;
use colored::Colorize;
use std::env;
const PASS: &str = "✓";
const FAIL: &str = "✗";
pub fn run() -> Result<bool> {
let mut all_ok = true;
let version = env!("CARGO_PKG_VERSION");
println!("{} Binary version: {}", PASS.green(), version.cyan());
let project_root = env::current_dir()?;
let minni_dir = project_root.join(".minni");
if minni_dir.exists() {
let probe_path = minni_dir.join(".write_probe");
match std::fs::write(&probe_path, b"") {
Ok(_) => {
let _ = std::fs::remove_file(&probe_path);
println!("{} .minni/ directory: exists and writable", PASS.green());
}
Err(e) => {
println!("{} .minni/ directory: exists but NOT writable", FAIL.red());
println!(" Fix: Check permissions on {}", minni_dir.display());
println!(" Error: {}", e);
all_ok = false;
}
}
} else {
println!("{} .minni/ directory: not found", FAIL.red());
println!(
" Fix: Run {} to initialise minni in this directory",
"minni init".cyan()
);
all_ok = false;
}
if Database::minni_dir_exists(&project_root) {
match Database::open(&project_root) {
Ok(_) => println!("{} SQLite database: opens successfully", PASS.green()),
Err(e) => {
println!("{} SQLite database: failed to open", FAIL.red());
println!(
" Fix: Try removing .minni/ and running {} again",
"minni init".cyan()
);
println!(" Error: {}", e);
all_ok = false;
}
}
} else {
println!(
"{} SQLite database: not found (run {} first)",
FAIL.red(),
"minni init".cyan()
);
all_ok = false;
}
let bm25_dir = minni_dir.join("bm25_index");
if bm25_dir.exists() {
match std::fs::read_dir(&bm25_dir) {
Ok(mut entries) => {
if entries.next().is_some() {
println!("{} BM25 index: exists and non-empty", PASS.green());
} else {
println!("{} BM25 index: directory exists but is empty", FAIL.red());
println!(
" Fix: Run {} to build the search index",
"minni index".cyan()
);
all_ok = false;
}
}
Err(e) => {
println!("{} BM25 index: cannot read index directory", FAIL.red());
println!(" Error: {}", e);
println!(" Fix: Check permissions on {}", bm25_dir.display());
all_ok = false;
}
}
} else {
println!("{} BM25 index: not found", FAIL.red());
println!(
" Fix: Run {} to build the search index",
"minni index".cyan()
);
all_ok = false;
}
if is_model_installed() {
println!("{} Reranker model: installed", PASS.green());
} else if !MODEL_ID.is_empty() {
let model_dir = get_model_dir().unwrap_or_else(|_| std::path::PathBuf::from("<unknown>"));
println!(
"{} Reranker model: not installed (will download on first search)",
"~".yellow()
);
println!(" Expected path: {}", model_dir.display());
} else {
let model_dir = get_model_dir().unwrap_or_else(|_| std::path::PathBuf::from("<unknown>"));
println!(
"{} Reranker model: missing and no download configured",
FAIL.red()
);
println!(" Expected path: {}", model_dir.display());
all_ok = false;
}
if is_dense_model_installed() {
println!("{} Dense model: installed", PASS.green());
} else if !DENSE_MODEL_ID.is_empty() {
let model_dir =
get_dense_model_dir().unwrap_or_else(|_| std::path::PathBuf::from("<unknown>"));
println!(
"{} Dense model: not installed (will download on first index/search)",
"~".yellow()
);
println!(" Expected path: {}", model_dir.display());
} else {
let model_dir =
get_dense_model_dir().unwrap_or_else(|_| std::path::PathBuf::from("<unknown>"));
println!(
"{} Dense model: missing and no download configured",
FAIL.red()
);
println!(" Expected path: {}", model_dir.display());
all_ok = false;
}
check_onnx_runtime(&mut all_ok);
println!();
if all_ok {
println!("{} All checks passed.", "✓ Doctor:".green().bold());
} else {
println!(
"{} Some checks failed — see details above.",
"✗ Doctor:".red().bold()
);
}
Ok(all_ok)
}
fn check_onnx_runtime(all_ok: &mut bool) {
if !is_model_installed() {
println!(
"{} ONNX runtime: skipped (no model installed — install reranker model first)",
"~".yellow()
);
return;
}
match get_model_dir() {
Ok(model_dir) => {
let model_file = model_dir.join("model.onnx");
match try_load_onnx_session(&model_file) {
Ok(()) => println!("{} ONNX runtime: loads successfully", PASS.green()),
Err(e) => {
println!("{} ONNX runtime: failed to load", FAIL.red());
println!(" Error: {}", e);
println!(
" Fix: Delete the model at {} and let it re-download on next search.",
model_dir.display()
);
*all_ok = false;
}
}
}
Err(e) => {
println!(
"{} ONNX runtime: could not determine model directory",
FAIL.red()
);
println!(" Error: {}", e);
*all_ok = false;
}
}
}
fn try_load_onnx_session(model_file: &std::path::Path) -> Result<()> {
use ort::session::Session;
Session::builder()?.commit_from_file(model_file)?;
Ok(())
}