Skip to main content

chronicle/cli/
doctor.rs

1use crate::doctor::{run_doctor, DoctorCheck, DoctorStatus};
2use crate::error::Result;
3use crate::git::{CliOps, GitOps};
4
5use super::util::find_git_dir;
6
7/// Run `git chronicle doctor`.
8pub fn run(json: bool, staleness: bool) -> Result<()> {
9    let git_dir = find_git_dir()?;
10    let repo_dir = git_dir.parent().unwrap_or(&git_dir).to_path_buf();
11    let git_ops = CliOps::new(repo_dir);
12
13    let mut report = run_doctor(&git_ops, &git_dir)?;
14
15    if staleness {
16        let staleness_check = check_staleness(&git_ops);
17        if staleness_check.status == DoctorStatus::Warn && report.overall == DoctorStatus::Pass {
18            report.overall = DoctorStatus::Warn;
19        }
20        report.checks.push(staleness_check);
21    }
22
23    if json {
24        let output = serde_json::to_string_pretty(&report).map_err(|e| {
25            crate::error::ChronicleError::Json {
26                source: e,
27                location: snafu::Location::default(),
28            }
29        })?;
30        println!("{output}");
31    } else {
32        println!("chronicle doctor");
33        for check in &report.checks {
34            let icon = match check.status {
35                DoctorStatus::Pass => "pass",
36                DoctorStatus::Warn => "warn",
37                DoctorStatus::Fail => "FAIL",
38            };
39            println!("  [{icon}] {}: {}", check.name, check.message);
40            if let Some(ref hint) = check.fix_hint {
41                println!("         {hint}");
42            }
43        }
44        println!();
45        let overall = match report.overall {
46            DoctorStatus::Pass => "all checks passed",
47            DoctorStatus::Warn => "some warnings",
48            DoctorStatus::Fail => "some checks failed",
49        };
50        println!("Overall: {overall}");
51    }
52
53    if report.has_failures() {
54        std::process::exit(1);
55    }
56
57    Ok(())
58}
59
60/// Check annotation staleness across the repo.
61fn check_staleness(git_ops: &dyn GitOps) -> DoctorCheck {
62    match crate::read::staleness::scan_staleness(git_ops, 50) {
63        Ok(report) => {
64            if report.stale_count == 0 {
65                DoctorCheck {
66                    name: "staleness".to_string(),
67                    status: DoctorStatus::Pass,
68                    message: format!(
69                        "{} annotations checked, none stale",
70                        report.total_annotations
71                    ),
72                    fix_hint: None,
73                }
74            } else {
75                DoctorCheck {
76                    name: "staleness".to_string(),
77                    status: DoctorStatus::Warn,
78                    message: format!(
79                        "{} stale annotation(s) out of {} checked",
80                        report.stale_count, report.total_annotations
81                    ),
82                    fix_hint: Some(
83                        "Run `git chronicle annotate` on stale files to refresh annotations."
84                            .to_string(),
85                    ),
86                }
87            }
88        }
89        Err(_) => DoctorCheck {
90            name: "staleness".to_string(),
91            status: DoctorStatus::Warn,
92            message: "could not check staleness".to_string(),
93            fix_hint: None,
94        },
95    }
96}