use crate::types::{DoctorCheck, RepoInfo};
pub trait DoctorChecks {
fn repo_info() -> RepoInfo;
fn current_version() -> &'static str;
fn tool_checks(&self) -> Vec<DoctorCheck> {
Vec::new()
}
}
pub fn run_doctor<T: DoctorChecks>(tool: &T) -> i32 {
let tool_name = T::repo_info().name;
println!("🏥 {tool_name} health check");
println!("{}", "=".repeat(tool_name.len() + 14));
println!();
let mut has_errors = false;
let has_warnings = false;
let tool_checks = tool.tool_checks();
if !tool_checks.is_empty() {
println!("Configuration:");
for check in tool_checks {
if check.passed {
println!(" ✅ {}", check.name);
} else {
println!(" ❌ {}", check.name);
if let Some(msg) = check.message {
println!(" {msg}");
}
has_errors = true;
}
}
println!();
}
if has_errors {
println!("❌ Issues found - see above for details");
1
} else if has_warnings {
println!("⚠️ Warnings found");
0 } else {
println!("✨ Everything looks healthy!");
0
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestTool;
impl DoctorChecks for TestTool {
fn repo_info() -> RepoInfo {
RepoInfo::new("workhelix", "test-tool")
}
fn current_version() -> &'static str {
"1.0.0"
}
fn tool_checks(&self) -> Vec<DoctorCheck> {
vec![
DoctorCheck::pass("Test check 1"),
DoctorCheck::fail("Test check 2", "This is a failure"),
]
}
}
#[test]
fn test_run_doctor() {
let tool = TestTool;
let exit_code = run_doctor(&tool);
assert_eq!(exit_code, 1);
}
}