fn check_msrv_defined(project_path: &Path) -> DiagnosticCheck {
let cargo_toml = project_path.join("Cargo.toml");
let content = std::fs::read_to_string(&cargo_toml).unwrap_or_default();
let (status, score, message) = if content.contains("rust-version") {
(
HealthStatus::Green,
5.0,
"MSRV defined (rust-version field)".to_string(),
)
} else {
(
HealthStatus::Yellow,
0.0,
"No MSRV - add rust-version to Cargo.toml".to_string(),
)
};
DiagnosticCheck {
name: "MSRV Defined".to_string(),
category: "Advanced".to_string(),
status,
message,
score,
max_score: 5.0,
}
}
fn check_benchmarks(project_path: &Path) -> DiagnosticCheck {
let benches_dir = project_path.join("benches");
let cargo_toml = project_path.join("Cargo.toml");
let content = std::fs::read_to_string(&cargo_toml).unwrap_or_default();
let has_bench_dir = benches_dir.exists() && benches_dir.is_dir();
let has_criterion = content.contains("criterion");
let (status, score, message) = if has_bench_dir && has_criterion {
(
HealthStatus::Green,
5.0,
"Criterion benchmarks configured".to_string(),
)
} else if has_bench_dir {
(
HealthStatus::Yellow,
3.0,
"Benchmarks present (consider Criterion)".to_string(),
)
} else {
(
HealthStatus::Yellow,
0.0,
"No benchmarks - add benches/ directory".to_string(),
)
};
DiagnosticCheck {
name: "Benchmarks".to_string(),
category: "Advanced".to_string(),
status,
message,
score,
max_score: 5.0,
}
}
fn score_github_workflows(workflows_dir: &Path) -> (HealthStatus, f64, String) {
let workflow_count = std::fs::read_dir(workflows_dir)
.map(|entries| entries.filter_map(|e| e.ok()).count())
.unwrap_or(0);
match workflow_count {
3.. => (HealthStatus::Green, 5.0, format!("{workflow_count} GitHub Actions workflows")),
1..=2 => (HealthStatus::Yellow, 3.0, format!("{workflow_count} GitHub Actions workflow(s)")),
_ => (HealthStatus::Yellow, 1.0, "Empty .github/workflows directory".to_string()),
}
}
fn detect_ci_system(project_path: &Path) -> (HealthStatus, f64, String) {
let github_workflows = project_path.join(".github").join("workflows");
if github_workflows.exists() && github_workflows.is_dir() {
return score_github_workflows(&github_workflows);
}
if project_path.join(".gitlab-ci.yml").exists() {
return (HealthStatus::Green, 5.0, "GitLab CI configured".to_string());
}
if project_path.join("Jenkinsfile").exists() {
return (HealthStatus::Green, 5.0, "Jenkins pipeline configured".to_string());
}
(HealthStatus::Red, 0.0, "No CI configured - add .github/workflows/".to_string())
}
fn check_ci_configured(project_path: &Path) -> DiagnosticCheck {
let (status, score, message) = detect_ci_system(project_path);
DiagnosticCheck {
name: "CI Configured".to_string(),
category: "Advanced".to_string(),
status,
message,
score,
max_score: 5.0,
}
}
fn dir_size(path: &Path) -> std::io::Result<u64> {
let mut total = 0u64;
if path.is_dir() {
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
total += dir_size(&path).unwrap_or(0);
} else {
total += entry.metadata().map(|m| m.len()).unwrap_or(0);
}
}
}
Ok(total)
}
fn file_has_test_markers(path: &Path) -> bool {
let is_rs = path.extension().map(|e| e == "rs").unwrap_or(false);
if !is_rs {
return false;
}
std::fs::read_to_string(path)
.map(|c| c.contains("#[test]") || c.contains("#[cfg(test)]"))
.unwrap_or(false)
}
fn has_test_annotations(dir: &Path) -> bool {
let entries = match std::fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return false,
};
for entry in entries.filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() && file_has_test_markers(&path) {
return true;
}
if path.is_dir() && has_test_annotations(&path) {
return true;
}
}
false
}
fn format_summary(report: &DiagnosticReport, failures_only: bool) -> String {
let mut output = String::new();
output.push_str(&format!(
"\n {}\n",
colors::header(&format!("Project Diagnostics: {}", colors::path(&report.project_path)))
));
output.push_str(&format!(" {}\n\n", colors::rule()));
let status_icon = match report.overall_status {
HealthStatus::Green => format!("{}GREEN{}", colors::BOLD_GREEN, colors::RESET),
HealthStatus::Yellow => format!("{}YELLOW{}", colors::BOLD_YELLOW, colors::RESET),
HealthStatus::Red => format!("{}RED{}", colors::BOLD_RED, colors::RESET),
HealthStatus::Skip => format!("{}SKIP{}", colors::DIM, colors::RESET),
};
output.push_str(&format!(
" Overall: {} {} ({})\n\n",
status_icon,
colors::score(report.total_score, report.max_score, 85.0, 60.0),
colors::pct(report.percentage, 85.0, 60.0)
));
for cat in &report.categories {
output.push_str(&format!(
" {} [{}/{}]\n",
colors::label(&cat.name),
colors::number(&cat.passed.to_string()),
cat.total
));
}
output.push('\n');
output.push_str(&format!(" {}\n", colors::subheader("Checks:")));
output.push_str(&format!(" {}\n", colors::separator()));
for check in &report.checks {
if failures_only && check.status == HealthStatus::Green {
continue;
}
let line = format!("{} - {}", check.name, check.message);
let formatted = match check.status {
HealthStatus::Green => colors::pass(&line),
HealthStatus::Yellow => colors::warn(&line),
HealthStatus::Red => colors::fail(&line),
HealthStatus::Skip => colors::skip(&line),
};
output.push_str(&format!(" {}\n", formatted));
}
output.push('\n');
output
}
fn format_json(report: &DiagnosticReport) -> Result<String> {
serde_json::to_string_pretty(report).map_err(|e| anyhow::anyhow!(e))
}
fn format_markdown(report: &DiagnosticReport, failures_only: bool) -> String {
let mut output = String::new();
output.push_str(&format!(
"# Project Diagnostics: {}\n\n",
report.project_path
));
let badge = match report.overall_status {
HealthStatus::Green => "",
HealthStatus::Yellow => "",
HealthStatus::Red => "",
HealthStatus::Skip => "",
};
output.push_str(&format!("{}\n\n", badge));
output.push_str(&format!(
"**Score:** {:.1}/{:.1} ({:.1}%)\n\n",
report.total_score, report.max_score, report.percentage
));
output.push_str("## Categories\n\n");
output.push_str("| Category | Passed | Warned | Failed | Score |\n");
output.push_str("|----------|--------|--------|--------|-------|\n");
for cat in &report.categories {
output.push_str(&format!(
"| {} | {} | {} | {} | {:.1}/{:.1} |\n",
cat.name, cat.passed, cat.warned, cat.failed, cat.score, cat.max_score
));
}
output.push('\n');
output.push_str("## Checks\n\n");
output.push_str("| Status | Check | Message |\n");
output.push_str("|--------|-------|--------|\n");
for check in &report.checks {
if failures_only && check.status == HealthStatus::Green {
continue;
}
let emoji = match check.status {
HealthStatus::Green => "✅",
HealthStatus::Yellow => "⚠️",
HealthStatus::Red => "❌",
HealthStatus::Skip => "⏭️",
};
output.push_str(&format!(
"| {} | {} | {} |\n",
emoji, check.name, check.message
));
}
output
}
fn format_andon(report: &DiagnosticReport) -> String {
let mut output = String::new();
output.push('\n');
output.push_str(&format!(" {}╔══════════════════════════════════════════════════════════════╗{}\n", colors::BOLD, colors::RESET));
output.push_str(&format!(" {}║ PROJECT DIAGNOSTICS ║{}\n", colors::BOLD, colors::RESET));
output.push_str(&format!(" {}║ (Andon Board) ║{}\n", colors::BOLD, colors::RESET));
output.push_str(&format!(" {}╠══════════════════════════════════════════════════════════════╣{}\n", colors::BOLD, colors::RESET));
let bar_width = 40;
let filled = ((report.percentage / 100.0) * bar_width as f64) as usize;
let empty = bar_width - filled;
let bar_color = if report.percentage >= 85.0 {
colors::GREEN
} else if report.percentage >= 60.0 {
colors::YELLOW
} else {
colors::RED
};
let progress_bar = format!(
"{}{}{}{}",
bar_color,
"#".repeat(filled),
colors::DIM,
"-".repeat(empty)
);
output.push_str(&format!(
" {}║{} Score: [{progress_bar}{}] {} {}║{}\n",
colors::BOLD, colors::RESET,
colors::RESET,
colors::pct(report.percentage, 85.0, 60.0),
colors::BOLD, colors::RESET
));
output.push_str(&format!(" {}╠══════════════════════════════════════════════════════════════╣{}\n", colors::BOLD, colors::RESET));
for cat in &report.categories {
let light = if cat.failed > 0 {
format!("{}●{} ", colors::RED, colors::RESET)
} else if cat.warned > 0 {
format!("{}●{} ", colors::YELLOW, colors::RESET)
} else {
format!("{}●{} ", colors::GREEN, colors::RESET)
};
output.push_str(&format!(
" {}║{} {} {:20} {}/{} checks passed {}║{}\n",
colors::BOLD, colors::RESET,
light, cat.name, cat.passed, cat.total,
colors::BOLD, colors::RESET
));
}
output.push_str(&format!(" {}╠══════════════════════════════════════════════════════════════╣{}\n", colors::BOLD, colors::RESET));
let failures: Vec<_> = report
.checks
.iter()
.filter(|c| c.status == HealthStatus::Red)
.collect();
if failures.is_empty() {
output.push_str(&format!(
" {}║{} {}No critical issues - production ready{} {}║{}\n",
colors::BOLD, colors::RESET,
colors::GREEN, colors::RESET,
colors::BOLD, colors::RESET
));
} else {
output.push_str(&format!(
" {}║{} {}ANDON CORD TRIGGERED{} - Issues require attention: {}║{}\n",
colors::BOLD, colors::RESET,
colors::BOLD_RED, colors::RESET,
colors::BOLD, colors::RESET
));
for check in failures.iter().take(5) {
output.push_str(&format!(
" {}║{} {}●{} {:<54} {}║{}\n",
colors::BOLD, colors::RESET,
colors::RED, colors::RESET,
check.name,
colors::BOLD, colors::RESET
));
}
}
output.push_str(&format!(" {}╚══════════════════════════════════════════════════════════════╝{}\n", colors::BOLD, colors::RESET));
output.push('\n');
output
}