use axum::body::Bytes;
use serde::Serialize;
use trusty_common::host_metrics::HostMetrics;
use super::transitions::ServiceTransition;
#[derive(Debug, Clone)]
pub enum HistoryEvent {
Sample(Box<HostMetrics>),
Transition(Box<ServiceTransition>),
}
impl HistoryEvent {
#[must_use]
pub fn kind(&self) -> &'static str {
match self {
HistoryEvent::Sample(_) => "sample",
HistoryEvent::Transition(_) => "transition",
}
}
#[must_use]
pub fn frame(&self) -> Bytes {
match self {
HistoryEvent::Sample(m) => sse_frame("sample", m.as_ref()),
HistoryEvent::Transition(t) => sse_frame("transition", t.as_ref()),
}
}
}
#[must_use]
pub fn sse_frame(kind: &str, payload: &impl Serialize) -> Bytes {
let json = serde_json::to_string(payload)
.unwrap_or_else(|e| format!(r#"{{"error":"serialise {kind}: {e}"}}"#));
Bytes::from(format!("event: {kind}\ndata: {json}\n\n"))
}
#[must_use]
pub fn lagged_frame(dropped: u64) -> Bytes {
sse_frame("lagged", &serde_json::json!({ "dropped": dropped }))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_frame_names_its_kind_on_one_line() {
let frame = sse_frame("transition", &serde_json::json!({ "a": 1 }));
let text = String::from_utf8(frame.to_vec()).expect("utf8 frame");
assert_eq!(text, "event: transition\ndata: {\"a\":1}\n\n");
let data_lines = text.lines().filter(|l| l.starts_with("data: ")).count();
assert_eq!(data_lines, 1, "one data line per event");
}
#[test]
fn a_sample_frame_carries_the_host_snapshot() {
let metrics = trusty_common::host_metrics::HostSampler::new().sample();
let cores = metrics.cpu.logical_cores;
let event = HistoryEvent::Sample(Box::new(metrics));
assert_eq!(event.kind(), "sample");
let text = String::from_utf8(event.frame().to_vec()).expect("utf8 frame");
let data = text
.strip_prefix("event: sample\ndata: ")
.and_then(|r| r.strip_suffix("\n\n"))
.expect("sample frame shape");
let parsed: serde_json::Value = serde_json::from_str(data).expect("sample json");
assert_eq!(parsed["cpu"]["logical_cores"], cores);
}
#[test]
fn a_lagged_frame_carries_the_count() {
let text = String::from_utf8(lagged_frame(7).to_vec()).expect("utf8 frame");
assert_eq!(text, "event: lagged\ndata: {\"dropped\":7}\n\n");
}
}