use std::io::Write;
use std::time::{Duration, Instant};
use serde::Serialize;
use super::BoundaryVerdict;
pub const DIAGNOSTICS_ENV: &str = "ONEVCS_BOUNDARY_DIAGNOSTICS";
#[derive(Debug, Clone, Default)]
pub struct Phases {
pub derivation: Duration,
pub diff: Duration,
pub matcher_build: Duration,
pub matching: Duration,
pub total: Duration,
pub terms: usize,
pub identities: usize,
pub commits: usize,
pub paths: usize,
pub bytes: usize,
}
impl Phases {
pub fn ended(mut self, started: Instant) -> Phases {
self.total = started.elapsed();
self
}
}
#[derive(Serialize)]
struct Line<'a> {
check: &'a str,
verdict: &'static str,
total_us: u128,
derivation_us: u128,
diff_us: u128,
matcher_build_us: u128,
matching_us: u128,
terms: usize,
identities: usize,
commits: usize,
paths: usize,
bytes: usize,
}
pub fn record(check: &str, verdict: &BoundaryVerdict, phases: &Phases) {
let Some(path) = std::env::var_os(DIAGNOSTICS_ENV).filter(|value| !value.is_empty()) else {
return;
};
let line = Line {
check,
verdict: match verdict {
BoundaryVerdict::Pass => "pass",
BoundaryVerdict::Refuse { .. } => "refuse",
BoundaryVerdict::Unavailable { .. } => "unavailable",
},
total_us: phases.total.as_micros(),
derivation_us: phases.derivation.as_micros(),
diff_us: phases.diff.as_micros(),
matcher_build_us: phases.matcher_build.as_micros(),
matching_us: phases.matching.as_micros(),
terms: phases.terms,
identities: phases.identities,
commits: phases.commits,
paths: phases.paths,
bytes: phases.bytes,
};
let written = serde_json::to_string(&line)
.map_err(std::io::Error::other)
.and_then(|json| {
let mut file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)?;
writeln!(file, "{json}")
});
if let Err(error) = written {
eprintln!(
"onevcs: warning: the boundary diagnostics could not be written to {}: {error}",
std::path::Path::new(&path).display()
);
}
}