Skip to main content

chronicle/cli/
doctor.rs

1use std::path::PathBuf;
2
3use crate::doctor::{run_doctor, DoctorStatus};
4use crate::error::Result;
5use crate::git::CliOps;
6
7/// Run `git chronicle doctor`.
8pub fn run(json: 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 report = run_doctor(&git_ops, &git_dir)?;
14
15    if json {
16        let output = serde_json::to_string_pretty(&report).map_err(|e| {
17            crate::error::ChronicleError::Json {
18                source: e,
19                location: snafu::Location::default(),
20            }
21        })?;
22        println!("{output}");
23    } else {
24        println!("chronicle doctor");
25        for check in &report.checks {
26            let icon = match check.status {
27                DoctorStatus::Pass => "pass",
28                DoctorStatus::Warn => "warn",
29                DoctorStatus::Fail => "FAIL",
30            };
31            println!("  [{icon}] {}: {}", check.name, check.message);
32            if let Some(ref hint) = check.fix_hint {
33                println!("         {hint}");
34            }
35        }
36        println!();
37        let overall = match report.overall {
38            DoctorStatus::Pass => "all checks passed",
39            DoctorStatus::Warn => "some warnings",
40            DoctorStatus::Fail => "some checks failed",
41        };
42        println!("Overall: {overall}");
43    }
44
45    if report.has_failures() {
46        std::process::exit(1);
47    }
48
49    Ok(())
50}
51
52/// Find the .git directory.
53fn find_git_dir() -> Result<PathBuf> {
54    let output = std::process::Command::new("git")
55        .args(["rev-parse", "--git-dir"])
56        .output()
57        .map_err(|e| crate::error::ChronicleError::Io {
58            source: e,
59            location: snafu::Location::default(),
60        })?;
61
62    if output.status.success() {
63        let dir = String::from_utf8_lossy(&output.stdout).trim().to_string();
64        let path = PathBuf::from(&dir);
65        if path.is_relative() {
66            let cwd = std::env::current_dir().map_err(|e| crate::error::ChronicleError::Io {
67                source: e,
68                location: snafu::Location::default(),
69            })?;
70            Ok(cwd.join(path))
71        } else {
72            Ok(path)
73        }
74    } else {
75        let cwd = std::env::current_dir().map_err(|e| crate::error::ChronicleError::Io {
76            source: e,
77            location: snafu::Location::default(),
78        })?;
79        Err(crate::error::chronicle_error::NotARepositorySnafu { path: cwd }.build())
80    }
81}