use chrono::{DateTime, Utc};
use uuid::Uuid;
pub trait Event: Send + Sync + std::fmt::Debug {
fn event_id(&self) -> Uuid;
fn trace_id(&self) -> Uuid;
fn sequence(&self) -> u64;
fn occurred_at(&self) -> DateTime<Utc>;
fn event_type(&self) -> &'static str;
}
#[derive(Debug, Clone)]
pub struct EventEnvelope<E> {
pub event_id: Uuid,
pub trace_id: Uuid,
pub sequence: u64,
pub occurred_at: DateTime<Utc>,
pub payload: E,
}
impl<E: std::fmt::Debug + Send + Sync> EventEnvelope<E> {
pub fn new(trace_id: Uuid, sequence: u64, payload: E) -> Self {
Self {
event_id: Uuid::new_v4(),
trace_id,
sequence,
occurred_at: Utc::now(),
payload,
}
}
pub fn map<F, U: std::fmt::Debug + Send + Sync>(self, f: F) -> EventEnvelope<U>
where
F: FnOnce(E) -> U,
{
EventEnvelope {
event_id: self.event_id,
trace_id: self.trace_id,
sequence: self.sequence,
occurred_at: self.occurred_at,
payload: f(self.payload),
}
}
}
impl<E: std::fmt::Debug + Send + Sync> Event for EventEnvelope<E> {
fn event_id(&self) -> Uuid {
self.event_id
}
fn trace_id(&self) -> Uuid {
self.trace_id
}
fn sequence(&self) -> u64 {
self.sequence
}
fn occurred_at(&self) -> DateTime<Utc> {
self.occurred_at
}
fn event_type(&self) -> &'static str {
"envelope"
}
}
pub fn new_trace_id() -> Uuid {
Uuid::new_v4()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope_roundtrip() {
let trace = new_trace_id();
let env = EventEnvelope::new(trace, 1, "hello");
assert_eq!(env.trace_id, trace);
assert_eq!(env.sequence, 1);
assert_eq!(env.payload, "hello");
}
#[test]
fn envelope_map_preserves_correlation() {
let trace = new_trace_id();
let env = EventEnvelope::new(trace, 42, 10u32);
let mapped = env.map(|v| v.to_string());
assert_eq!(mapped.trace_id, trace);
assert_eq!(mapped.sequence, 42);
assert_eq!(mapped.payload, "10");
}
}