pub type SnapshotWriter =
std::sync::Arc<std::sync::Mutex<Option<nmbrs_metrics::reporters::sqlite::SqliteReporter>>>;
pub fn lod_str(lod: super::Lod) -> &'static str {
match lod {
super::Lod::Compact => "compact",
super::Lod::Labeled => "labeled",
super::Lod::Expanded => "expanded",
}
}
pub fn capture(
writer: Option<&SnapshotWriter>,
slot: &str,
exec_id: u64,
subject_kind: &str,
subject_id: &str,
readout_name: &str,
lod: &str,
rendered: &str,
) {
let Some(writer) = writer else {
return;
};
let plain = strip_ansi(rendered);
let now_nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as i64)
.unwrap_or(0);
let body_ansi = if rendered != plain {
Some(rendered.as_bytes())
} else {
None
};
let mut guard = match writer.lock() {
Ok(g) => g,
Err(_) => return,
};
if let Some(reporter) = guard.as_mut() {
reporter.upsert_readout_snapshot(
slot,
exec_id,
subject_kind,
subject_id,
readout_name,
lod,
now_nanos,
body_ansi,
&plain,
);
}
}
pub fn strip_ansi(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut chars = s.char_indices();
while let Some((_, c)) = chars.next() {
if c == '\x1b' {
for (_, ch) in chars.by_ref() {
if ch.is_ascii_alphabetic() {
break;
}
}
continue;
}
out.push(c);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strip_ansi_removes_sgr_runs() {
assert_eq!(
strip_ansi("\x1b[34m[setup]\x1b[0m 100% \x1b[32m\u{2713}\x1b[0m"),
"[setup] 100% \u{2713}",
);
}
#[test]
fn strip_ansi_handles_no_escapes() {
assert_eq!(strip_ansi("plain text"), "plain text");
}
#[test]
fn strip_ansi_preserves_non_escape_unicode() {
assert_eq!(strip_ansi("(profile=alpha) ✓"), "(profile=alpha) ✓");
}
#[test]
fn strip_ansi_handles_carriage_return_and_clear() {
assert_eq!(strip_ansi("\x1b[Khello"), "hello",);
}
}