use std::io;
use std::path::Path;
use clap::Parser;
use repograph_core::{CONFIG_FILE_NAME, Config, DoctorReport, RepographError};
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
use crate::output::{OutputMode, render_doctor};
#[derive(Debug, Parser)]
pub struct Args {
#[arg(long)]
pub json: bool,
}
#[tracing::instrument(skip(args, config_dir), fields(json = args.json))]
pub fn run(args: &Args, config_dir: &Path) -> Result<(), RepographError> {
tracing::debug!(command = "doctor", json = args.json, "start");
let mode = OutputMode::detect(args.json);
let config_path = config_dir.join(CONFIG_FILE_NAME);
let generated_at = now_rfc3339();
let load = Config::load(config_dir);
if let Err(RepographError::Io(ref e)) = load {
if e.kind() == io::ErrorKind::PermissionDenied {
return Err(load
.err()
.unwrap_or_else(|| RepographError::Io(io::Error::other("permission denied"))));
}
}
let report = match &load {
Ok(cfg) => DoctorReport::run(Ok(cfg), &config_path, generated_at),
Err(err) => DoctorReport::run(Err(err), &config_path, generated_at),
};
render_doctor(mode, &report)?;
if mode == OutputMode::Tty && report.summary.error > 0 {
eprintln!("→ run `repograph doctor --json | jq` for machine-readable detail");
}
tracing::info!(
ok = report.summary.ok,
warn = report.summary.warn,
error = report.summary.error,
total = report.summary.total,
"doctor complete",
);
if report.summary.error > 0 {
return Err(RepographError::DoctorErrorsFound {
count: report.summary.error,
});
}
Ok(())
}
fn now_rfc3339() -> String {
OffsetDateTime::now_utc()
.format(&Rfc3339)
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string())
}