use std::path::Path;
use crate::error::Result;
use crate::output::OutputFormat;
use super::CommonArgs;
pub(super) fn run(common: &CommonArgs, _format: OutputFormat) -> Result<()> {
let mut failures = 0;
println!("enprot: {}", env!("CARGO_PKG_VERSION"));
let features: Vec<&str> = vec![
#[cfg(feature = "cli")]
"cli",
#[cfg(feature = "cas-s3")]
"cas-s3",
#[cfg(feature = "cas-ipfs")]
"cas-ipfs",
#[cfg(feature = "cas-rekor")]
"cas-rekor",
#[cfg(feature = "async-pipeline")]
"async-pipeline",
#[cfg(feature = "pure-rust-crypto")]
"pure-rust-crypto",
#[cfg(feature = "telemetry")]
"telemetry",
#[cfg(feature = "vendored-rnp")]
"vendored-rnp",
];
if features.is_empty() {
println!(" features: (none — library-only build)");
} else {
println!(" features: {}", features.join(", "));
}
match botan::Version::current() {
Ok(v) => {
println!("Botan: {}.{}.{} ({})", v.major, v.minor, v.patch, v.string);
}
Err(e) => {
println!("Botan: FAIL — {e}");
failures += 1;
}
}
let rnp_ver = rnp_rs_probe();
match rnp_ver {
Ok(v) => println!("librnp: {v}"),
Err(e) => {
println!("librnp: warn — {e}");
println!(" (OpenPGP sign/verify unavailable; WORD encryption unaffected)");
}
}
let policy_name = common.policy.clone().unwrap_or_else(|| {
if common.fips {
"nist (forced by --fips)".to_string()
} else {
"default".to_string()
}
});
println!("policy: {policy_name}");
if common.fips {
println!(" FIPS mode: engaged (--fips)");
}
let casdir = common
.casdir
.clone()
.map(|p| p.display().to_string())
.unwrap_or_else(|| {
if Path::new("cas").is_dir() {
"cas/ (auto-detected)".to_string()
} else {
". (cwd, no cas/ dir)".to_string()
}
});
println!("CAS: {casdir}");
let probe_path = common
.casdir
.clone()
.unwrap_or_else(|| Path::new("cas").to_path_buf());
let probe = std::fs::metadata(&probe_path);
match probe {
Ok(m) if m.is_dir() => {
let test = probe_path.join(".enprot-doctor-probe");
match std::fs::write(&test, b"probe") {
Ok(()) => {
let _ = std::fs::remove_file(&test);
println!(" writability: ok");
}
Err(e) => {
println!(" writability: FAIL — {e}");
failures += 1;
}
}
}
Ok(_) => {
println!(" warn: {probe_path:?} exists but is not a directory");
}
Err(_) => {
println!(" (directory not present; created on first store)");
}
}
let locale = std::env::var("ENPROT_LOCALE").unwrap_or_else(|_| "en (default)".to_string());
println!("locale: {locale}");
if Path::new(".git").is_dir() {
match std::fs::read_to_string(".gitattributes") {
Ok(attr) if attr.contains("enprot") => {
println!("git filter: ok (.gitattributes references enprot)");
}
_ => println!("git filter: not configured (add to .gitattributes for smudge/clean)"),
}
}
if failures > 0 {
println!(
"\n{} failure(s) — see above. Warnings do not affect the exit code.",
failures
);
std::process::exit(1);
}
println!("\nall critical checks passed");
Ok(())
}
fn rnp_rs_probe() -> std::result::Result<String, String> {
let ver = rnp::version_string();
if ver.is_empty() {
return Err("librnp returned an empty version string".to_string());
}
Ok(ver)
}