Skip to main content

salamander/
event.rs

1//! DESIGN.md §4 — the event envelope, generic over its payload.
2//!
3//! Design rule (P1): the engine frames, orders, and persists bodies but
4//! never interprets them. Only projections do. The payload type `B` is a
5//! free parameter — the agent vocabulary in [`crate::agent`] is just one
6//! choice of `B`, not something the core depends on. Adding a new payload
7//! variant must never require touching `log/`.
8
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11
12/// One durable, offset-ordered fact carrying a payload of type `B`.
13///
14/// The engine treats `body` as opaque bytes-to-be; it round-trips it
15/// through serde without ever matching on it.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Event<B> {
18    /// Assigned by the log at append.
19    pub offset: u64,
20    /// Wall clock, informational only — offset is the ordering.
21    pub timestamp_ms: u64,
22    /// e.g. session id.
23    pub namespace: String,
24    /// The application payload; opaque to the engine.
25    pub body: B,
26}
27
28/// The bound set every payload type must satisfy, aliased under one name so
29/// the engine's generic signatures stay legible (`B: Body` rather than the
30/// four-trait mouthful everywhere).
31///
32/// It is *blanket-implemented*: any type that is serde-serializable,
33/// owned-deserializable, cloneable, and `'static` is a `Body`
34/// automatically, so implementers never write `impl Body` by hand.
35/// `DeserializeOwned` (rather than `Deserialize<'de>`) is what lets the log
36/// hand back a payload that owns its data, decoupled from the lifetime of
37/// the record bytes it was decoded from.
38pub trait Body: Serialize + DeserializeOwned + Clone + 'static {}
39
40impl<T: Serialize + DeserializeOwned + Clone + 'static> Body for T {}