use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[cfg_attr(feature = "ts-export", derive(ts_rs::TS), ts(export))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EventSourceMeta {
pub subject: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub envelope_id: Option<Uuid>,
pub synthesis_mode: String,
}
pub fn format_event_subscriber_source(source_id: &str) -> String {
format!("event:{source_id}")
}
pub fn format_dispatch_source(extension_id: &str, channel: &str, account_id: &str) -> String {
format!("dispatch:{extension_id}:{channel}:{account_id}")
}
pub fn format_rate_limit_hit(tool: &str, binding_id: Option<&str>, rps: f64) -> String {
let bid = binding_id.unwrap_or("none");
format!("rate_limited:tool={tool},binding={bid},rps={rps}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_through_serde() {
let original = EventSourceMeta {
subject: "webhook.github.pull_request".into(),
envelope_id: Some(Uuid::nil()),
synthesis_mode: "synthesize".into(),
};
let json = serde_json::to_string(&original).unwrap();
let back: EventSourceMeta = serde_json::from_str(&json).unwrap();
assert_eq!(original, back);
}
#[test]
fn skips_envelope_id_when_none() {
let meta = EventSourceMeta {
subject: "x".into(),
envelope_id: None,
synthesis_mode: "tick".into(),
};
let v = serde_json::to_value(&meta).unwrap();
assert!(v.get("subject").is_some());
assert!(v.get("envelope_id").is_none());
assert_eq!(v["synthesis_mode"], "tick");
}
#[test]
fn wire_shape_lock_down() {
let meta = EventSourceMeta {
subject: "s".into(),
envelope_id: Some(Uuid::from_u128(1)),
synthesis_mode: "synthesize".into(),
};
let v = serde_json::to_value(&meta).unwrap();
for key in ["subject", "envelope_id", "synthesis_mode"] {
assert!(v.get(key).is_some(), "missing key `{key}` in event_source");
}
}
#[test]
fn format_marker_prefixes_with_event() {
assert_eq!(
format_event_subscriber_source("github_main"),
"event:github_main"
);
assert_eq!(format_event_subscriber_source(""), "event:");
}
#[test]
fn format_dispatch_marker_concatenates_three_segments() {
assert_eq!(
format_dispatch_source("marketing-saas", "whatsapp", "personal"),
"dispatch:marketing-saas:whatsapp:personal"
);
}
#[test]
fn format_dispatch_marker_handles_empty_segments() {
assert_eq!(format_dispatch_source("", "", ""), "dispatch:::");
}
#[test]
fn format_rate_limit_hit_renders_canonical_string() {
assert_eq!(
format_rate_limit_hit("marketing_send_drip", Some("whatsapp:free"), 0.167),
"rate_limited:tool=marketing_send_drip,binding=whatsapp:free,rps=0.167"
);
}
#[test]
fn format_rate_limit_hit_with_binding_none_says_none() {
assert_eq!(
format_rate_limit_hit("read_state", None, 1.0),
"rate_limited:tool=read_state,binding=none,rps=1"
);
}
}