Skip to main content

maiko/
envelope.rs

1use std::{fmt, hash};
2
3use crate::{ActorId, EventId, Meta};
4
5/// The unit carried through all Maiko channels.
6///
7/// Every event in the system travels as `Arc<Envelope<E>>` - from producer
8/// through the broker to each subscriber's mailbox. It pairs the user-defined
9/// event payload with [`Meta`] (sender, timestamp, parent ID).
10#[derive(Clone)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[cfg_attr(
13    feature = "serde",
14    serde(bound(
15        serialize = "E: serde::Serialize",
16        deserialize = "E: serde::de::DeserializeOwned"
17    ))
18)]
19pub struct Envelope<E> {
20    meta: Meta,
21    event: E,
22}
23
24impl<E> Envelope<E> {
25    /// Create a new envelope tagging the event with the given actor name.
26    #[must_use]
27    pub fn new(event: E, actor_id: ActorId) -> Self {
28        Self {
29            meta: Meta::new(actor_id, None),
30            event,
31        }
32    }
33
34    /// Set the parent event ID for causality tracking.
35    ///
36    /// Prefer [`Context::send_child_event`](crate::Context::send_child_event) which
37    /// sets this automatically. Use `with_parent_id` directly only when constructing
38    /// envelopes outside the normal actor context (e.g., in test setup or bridges).
39    #[must_use]
40    pub fn with_parent_id(mut self, parent_id: EventId) -> Self {
41        self.meta.set_parent(parent_id);
42        self
43    }
44
45    /// Returns a reference to the event payload.
46    ///
47    /// This is a convenience method for pattern matching.
48    ///
49    /// # Example
50    ///
51    /// ```ignore
52    /// match envelope.event() {
53    ///     MyEvent::Foo(x) => handle_foo(x),
54    ///     MyEvent::Bar => handle_bar(),
55    /// }
56    /// ```
57    #[inline]
58    pub fn event(&self) -> &E {
59        &self.event
60    }
61
62    /// Returns the event metadata (sender, timestamp, parent ID).
63    #[inline]
64    pub fn meta(&self) -> &Meta {
65        &self.meta
66    }
67
68    /// Shorthand for `self.meta().id()`.
69    #[inline]
70    pub fn id(&self) -> EventId {
71        self.meta.id()
72    }
73}
74
75impl<E: PartialEq> PartialEq for Envelope<E> {
76    fn eq(&self, other: &Self) -> bool {
77        self.meta.id() == other.meta.id() && self.event == other.event
78    }
79}
80
81impl<E: Eq> Eq for Envelope<E> {}
82
83impl<E: hash::Hash> hash::Hash for Envelope<E> {
84    fn hash<H: hash::Hasher>(&self, state: &mut H) {
85        self.meta.id().hash(state);
86        self.event.hash(state);
87    }
88}
89
90impl<E: fmt::Debug> fmt::Debug for Envelope<E> {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.debug_struct("Envelope")
93            .field("id", &self.meta.id())
94            .field("sender", &self.meta.actor_name())
95            .field("event", &self.event)
96            .field("timestamp", &self.meta.timestamp())
97            .field("parent_id", &self.meta.parent_id())
98            .finish()
99    }
100}
101
102impl<E: fmt::Display> fmt::Display for Envelope<E> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(
105            f,
106            "Envelope {{ id: {}, sender: {}, event: {} }}",
107            self.meta.id(),
108            self.meta.actor_name(),
109            self.event
110        )
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::Event;
118
119    #[derive(Clone, Debug)]
120    #[allow(unused)]
121    struct TestEvent(i32);
122    impl Event for TestEvent {}
123
124    #[test]
125    fn envelope_debug() {
126        let actor = ActorId::new("test-actor");
127        let envelope = Envelope::new(TestEvent(42), actor);
128        let debug_str = format!("{:?}", envelope);
129
130        assert!(debug_str.contains("TestEvent"));
131        assert!(debug_str.contains("42"));
132        assert!(debug_str.contains("test-actor"));
133    }
134}