Skip to main content

evento_core/
metadata.rs

1//! Standard event metadata types.
2//!
3//! This module provides standard metadata types for events, including
4//! user identification and unique metadata IDs.
5//!
6//! # Types
7//!
8//! - [`Metadata`] - Standard metadata with ID and extensible key-value storage
9//! - [`Event`] - Typed event wrapper with deserialized data
10//! - [`RawEvent`] - Raw event without deserialization (for batch processing)
11//!
12//! # Example
13//!
14//! ```rust,ignore
15//! use evento::metadata::Metadata;
16//!
17//! // Create default metadata (anonymous)
18//! let mut metadata = Metadata::default();
19//!
20//! // Set who is making the request
21//! metadata.set_requested_by("user-123");
22//!
23//! // Set who the request is on behalf of (for impersonation)
24//! metadata.set_requested_as("impersonated-user-789");
25//!
26//! // Use with event creation
27//! create()
28//!     .event(&my_event)
29//!     .metadata(&metadata)
30//!     .commit(&executor)
31//!     .await?;
32//!
33//! // Access metadata from events
34//! if let Ok(user_id) = event.metadata.requested_by() {
35//!     println!("Requested by: {}", user_id);
36//! }
37//! ```
38
39use std::{collections::HashMap, marker::PhantomData, ops::Deref};
40use thiserror::Error;
41use ulid::Ulid;
42
43const REQUESTED_BY: &str = "EVENTO_REQUESTED_BY";
44const REQUESTED_AS: &str = "EVENTO_REQUESTED_AS";
45
46/// Errors when accessing metadata fields.
47#[derive(Debug, Error)]
48pub enum MetadataError {
49    #[error("not found")]
50    NotFound,
51
52    #[error("decode: {0}")]
53    Decode(#[from] bitcode::Error),
54}
55
56/// Standard event metadata.
57///
58/// Contains a unique ID and user identification. Default creates
59/// anonymous metadata with an auto-generated ULID.
60#[derive(Clone, PartialEq, Debug, bitcode::Encode, bitcode::Decode)]
61pub struct Metadata {
62    /// Unique metadata ID (ULID)
63    pub id: String,
64    meta: HashMap<String, Vec<u8>>,
65}
66
67impl Metadata {
68    pub(crate) fn insert_enc<V: bitcode::Encode>(
69        &mut self,
70        key: impl Into<String>,
71        value: &V,
72    ) -> &mut Self {
73        self.meta.insert(key.into(), bitcode::encode(value));
74
75        self
76    }
77
78    pub fn try_get<D: bitcode::DecodeOwned>(&self, key: &str) -> Result<D, MetadataError> {
79        let Some(value) = self.meta.get(key) else {
80            return Err(MetadataError::NotFound);
81        };
82
83        Ok(bitcode::decode(value)?)
84    }
85
86    pub fn set_requested_as(&mut self, value: impl Into<String>) -> &mut Self {
87        let value = value.into();
88        self.insert_enc(REQUESTED_AS, &value);
89
90        self
91    }
92
93    pub fn requested_as(&self) -> Result<String, MetadataError> {
94        self.try_get(REQUESTED_AS)
95    }
96
97    pub fn set_requested_by(&mut self, value: impl Into<String>) -> &mut Self {
98        let value = value.into();
99        self.insert_enc(REQUESTED_BY, &value);
100
101        self
102    }
103
104    pub fn requested_by(&self) -> Result<String, MetadataError> {
105        self.try_get(REQUESTED_BY)
106    }
107}
108
109impl Default for Metadata {
110    fn default() -> Self {
111        Self {
112            id: Ulid::new().to_string(),
113            meta: Default::default(),
114        }
115    }
116}
117
118impl Deref for Metadata {
119    type Target = HashMap<String, Vec<u8>>;
120
121    fn deref(&self) -> &Self::Target {
122        &self.meta
123    }
124}
125
126impl From<&Metadata> for Metadata {
127    fn from(value: &Metadata) -> Self {
128        value.clone()
129    }
130}
131
132/// Typed event with deserialized data.
133///
134/// `Event` wraps a raw [`crate::Event`] and provides typed access
135/// to the deserialized event data. It implements `Deref` to
136/// provide access to the underlying event fields (id, timestamp, version, metadata, etc.).
137///
138/// # Type Parameters
139///
140/// - `D`: The event data type (e.g., `AccountOpened`)
141///
142/// # Example
143///
144/// ```rust,ignore
145/// use evento::metadata::Event;
146///
147/// #[evento::handler]
148/// async fn handle_deposit(
149///     event: Event<MoneyDeposited>,
150///     view: &mut AccountView,
151/// ) -> anyhow::Result<()> {
152///     // Access typed data
153///     println!("Amount: {}", event.data.amount);
154///
155///     // Access metadata via Deref
156///     if let Ok(user) = event.metadata.requested_by() {
157///         println!("By user: {}", user);
158///     }
159///
160///     // Access underlying event fields via Deref
161///     println!("Event ID: {}", event.id);
162///     println!("Version: {}", event.version);
163///
164///     Ok(())
165/// }
166/// ```
167pub struct Event<D> {
168    event: crate::Event,
169    /// The typed event data
170    pub data: D,
171}
172
173impl<D> Deref for Event<D> {
174    type Target = crate::Event;
175
176    fn deref(&self) -> &Self::Target {
177        &self.event
178    }
179}
180
181impl<D> TryFrom<&crate::Event> for Event<D>
182where
183    D: bitcode::DecodeOwned,
184{
185    type Error = bitcode::Error;
186
187    fn try_from(value: &crate::Event) -> Result<Self, Self::Error> {
188        let data = bitcode::decode::<D>(&value.data)?;
189        Ok(Event {
190            data,
191            event: value.clone(),
192        })
193    }
194}
195
196pub struct RawEvent<D>(pub crate::Event, pub PhantomData<D>);
197
198impl<D> Deref for RawEvent<D> {
199    type Target = crate::Event;
200
201    fn deref(&self) -> &Self::Target {
202        &self.0
203    }
204}