grex_cli/cli/verbs/
doctor.rs1use crate::cli::args::{DoctorArgs, GlobalFlags};
7use anyhow::Result;
8use grex_core::doctor::{run_doctor, DoctorOpts, DoctorReport, Severity};
9use tokio_util::sync::CancellationToken;
10
11pub fn run(args: DoctorArgs, global: &GlobalFlags, _cancel: &CancellationToken) -> Result<()> {
12 let workspace = std::env::current_dir()?;
13 let opts = DoctorOpts { fix: args.fix, lint_config: args.lint_config, shallow: args.shallow };
14 let report = run_doctor(&workspace, &opts)?;
15
16 if global.json {
17 println!("{}", render_json(&report));
18 } else {
19 print_table(&report);
20 }
21
22 std::process::exit(report.exit_code());
23}
24
25fn print_table(report: &DoctorReport) {
27 println!("{:<18} {:<8} DETAIL", "CHECK", "STATUS");
28 for f in &report.findings {
29 let status = match f.severity {
30 Severity::Ok => "OK",
31 Severity::Warning => "WARN",
32 Severity::Error => "ERROR",
33 };
34 let detail = if f.detail.is_empty() { "-".to_string() } else { f.detail.clone() };
35 let pack = f.pack.as_deref().unwrap_or("");
36 let label = if pack.is_empty() {
37 f.check.label().to_string()
38 } else {
39 format!("{}[{}]", f.check.label(), pack)
40 };
41 println!("{label:<18} {status:<8} {detail}");
42 }
43}
44
45fn render_json(report: &DoctorReport) -> String {
50 let findings: Vec<serde_json::Value> = report
51 .findings
52 .iter()
53 .map(|f| {
54 serde_json::json!({
55 "check": f.check.label(),
56 "severity": severity_label(f.severity),
57 "pack": f.pack,
58 "detail": f.detail,
59 "auto_fixable": f.auto_fixable,
60 "synthetic": f.synthetic,
61 })
62 })
63 .collect();
64 let doc = serde_json::json!({
65 "exit_code": report.exit_code(),
66 "worst_severity": severity_label(report.worst()),
67 "findings": findings,
68 });
69 serde_json::to_string(&doc).unwrap_or_else(|_| "{}".to_string())
72}
73
74fn severity_label(s: Severity) -> &'static str {
75 match s {
76 Severity::Ok => "ok",
77 Severity::Warning => "warning",
78 Severity::Error => "error",
79 }
80}