use serde::Serialize;
use crate::report::{CallReport, RowKind};
pub const TRACE_CHAIN_RULE: &str =
"first-chunk naming heuristic (RFC 05 §3 idiom); a naming coincidence is tagged the same way";
pub const TRACE_EXCLUDED: &str = "the verbatim planes (`@rpc`, `@blob`, `@media`, `@adv`, \
`@catalog`): `**` never crosses an `@`-chunk (RFC 03 §4 D2), so the artifact bytes on \
`@blob` are outside this window — excluded, not empty";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TraceRelation {
DeclaredChain,
SameOriginUndeclared,
SameOriginRegistryNotLoaded,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum HlcReference {
Reply,
None,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct TraceRow {
pub key: String,
pub relation: TraceRelation,
pub arrival_delta_ms: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub hlc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hlc_delta_ms: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stamped_by: Option<String>,
pub kind: RowKind,
pub payload_bytes: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub break_before: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ConcurrentLane {
pub samples: u64,
pub keys: u64,
pub examples: Vec<String>,
pub dropped: u64,
}
#[derive(Debug, Clone, Serialize)]
pub struct TraceReport {
pub call: CallReport,
pub scopes: Vec<String>,
pub excluded: &'static str,
pub window_s: f64,
pub subscribed_before_call: bool,
pub t0_unix_s: f64,
pub call_returned_ms: f64,
pub hlc_reference: HlcReference,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_hlc: Option<String>,
pub chain_rule: &'static str,
pub registry_loaded: bool,
pub idiom: String,
pub attributed: Vec<TraceRow>,
pub same_origin: Vec<TraceRow>,
pub concurrent: ConcurrentLane,
pub dropped: u64,
pub keys_evicted: u64,
}
impl TraceReport {
pub fn exit_code(&self) -> i32 {
self.call.exit_code()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_trace_exits_as_its_call_does() {
let call = |answers| CallReport {
key: "k".into(),
timeout_s: 5.0,
answers,
};
let report = |call| TraceReport {
call,
scopes: vec![],
excluded: TRACE_EXCLUDED,
window_s: 10.0,
subscribed_before_call: true,
t0_unix_s: 0.0,
call_returned_ms: 0.0,
hlc_reference: HlcReference::None,
reply_hlc: None,
chain_rule: TRACE_CHAIN_RULE,
registry_loaded: false,
idiom: "undeclared".into(),
attributed: vec![],
same_origin: vec![],
concurrent: ConcurrentLane {
samples: 0,
keys: 0,
examples: vec![],
dropped: 0,
},
dropped: 0,
keys_evicted: 0,
};
assert_eq!(report(call(vec![])).exit_code(), 2);
assert_eq!(
report(call(vec![crate::report::CallAnswer {
origin: "h-1".into(),
outcome: crate::report::CallOutcome::Ok {
value: None,
text: None,
},
attachment: None,
attachment_bytes: None,
}]))
.exit_code(),
0
);
}
#[test]
fn an_unstamped_row_carries_only_its_arrival() {
let row = TraceRow {
key: "v1/h-3fa9c2d41b7e/telemetry/other/noise".into(),
relation: TraceRelation::SameOriginUndeclared,
arrival_delta_ms: 1.5,
hlc: None,
hlc_delta_ms: None,
stamped_by: None,
kind: RowKind::Put,
payload_bytes: 3,
break_before: None,
};
assert_eq!(
serde_json::to_value(&row).unwrap(),
serde_json::json!({
"key": "v1/h-3fa9c2d41b7e/telemetry/other/noise",
"relation": "same_origin_undeclared",
"arrival_delta_ms": 1.5,
"kind": "put",
"payload_bytes": 3
})
);
}
}