use std::time::Instant;
pub struct Timings {
command: &'static str,
enabled: bool,
json: bool,
start: Instant,
last: Instant,
entries: Vec<TimingEntry>,
}
struct TimingEntry {
name: &'static str,
duration_ms: f64,
}
impl Timings {
pub fn from_env(command: &'static str) -> Self {
let value = std::env::var("SEM_TIMINGS").unwrap_or_default();
let enabled = !matches!(value.as_str(), "" | "0" | "false" | "off");
let now = Instant::now();
Self {
command,
enabled,
json: value == "json",
start: now,
last: now,
entries: Vec::new(),
}
}
pub fn disabled(command: &'static str) -> Self {
let now = Instant::now();
Self {
command,
enabled: false,
json: false,
start: now,
last: now,
entries: Vec::new(),
}
}
pub fn mark(&mut self, name: &'static str) {
if !self.enabled {
return;
}
let now = Instant::now();
self.entries.push(TimingEntry {
name,
duration_ms: elapsed_ms(now.duration_since(self.last)),
});
self.last = now;
}
pub fn finish(&self) {
if !self.enabled {
return;
}
let total_ms = elapsed_ms(self.start.elapsed());
if self.json {
let phases = self
.entries
.iter()
.map(|entry| {
serde_json::json!({
"name": entry.name,
"durationMs": entry.duration_ms,
})
})
.collect::<Vec<_>>();
let output = serde_json::json!({
"command": self.command,
"phases": phases,
"totalMs": total_ms,
});
eprintln!("{}", serde_json::to_string(&output).unwrap());
} else {
eprintln!("sem timings ({})", self.command);
for entry in &self.entries {
eprintln!(" {:<32} {:>8.3} ms", entry.name, entry.duration_ms);
}
eprintln!(" {:<32} {:>8.3} ms", "total", total_ms);
}
}
}
fn elapsed_ms(duration: std::time::Duration) -> f64 {
duration.as_secs_f64() * 1000.0
}