1use anyhow::Result;
4use clap::{Args, ValueEnum};
5
6use crate::sanity::{self, SanityOptions};
7use crate::{commands::doctor, config};
8
9#[derive(Debug, Clone, Copy, Default, ValueEnum)]
11pub enum DoctorFormat {
12 #[default]
14 Text,
15 Json,
17}
18
19#[derive(Args)]
21pub struct DoctorArgs {
22 #[arg(long, conflicts_with = "no_sanity_checks")]
24 pub auto_fix: bool,
25
26 #[arg(long, conflicts_with = "auto_fix")]
28 pub no_sanity_checks: bool,
29
30 #[arg(long, value_enum, default_value = "text")]
32 pub format: DoctorFormat,
33}
34
35pub fn handle_doctor(args: DoctorArgs) -> Result<()> {
36 let resolved = config::resolve_from_cwd_for_doctor()?;
39
40 if !args.no_sanity_checks {
42 let options = SanityOptions {
43 auto_fix: args.auto_fix,
44 skip: false,
45 non_interactive: false, };
47 let sanity_result = sanity::run_sanity_checks(&resolved, &options)?;
48
49 if !sanity_result.auto_fixes.is_empty() {
51 log::info!(
52 "Sanity checks applied {} fix(es):",
53 sanity_result.auto_fixes.len()
54 );
55 for fix in &sanity_result.auto_fixes {
56 log::info!(" - {}", fix);
57 }
58 }
59
60 if !sanity_result.needs_attention.is_empty() {
61 log::warn!(
62 "Sanity checks found {} issue(s) needing attention:",
63 sanity_result.needs_attention.len()
64 );
65 for issue in &sanity_result.needs_attention {
66 match issue.severity {
67 sanity::IssueSeverity::Warning => log::warn!(" - {}", issue.message),
68 sanity::IssueSeverity::Error => log::error!(" - {}", issue.message),
69 }
70 }
71 }
72 }
73
74 let report = doctor::run_doctor(&resolved, args.auto_fix)?;
76
77 match args.format {
79 DoctorFormat::Text => {
80 doctor::print_doctor_report_text(&report);
81 }
82 DoctorFormat::Json => {
83 println!("{}", serde_json::to_string_pretty(&report)?);
84 }
85 }
86
87 if report.success {
89 Ok(())
90 } else {
91 anyhow::bail!("Doctor check failed: one or more critical issues found")
92 }
93}