Skip to main content

es_entity/
traits.rs

1//! Traits to orchestrate and maintain the event-sourcing pattern.
2
3use serde::{Serialize, de::DeserializeOwned};
4
5use std::collections::HashMap;
6
7use super::{db, error::EntityHydrationError, events::EntityEvents, tree_query::TreeSpec};
8
9/// Required trait for all event enums to be compatible and recognised by es-entity.
10///
11/// All `EntityEvent` enums implement this trait to ensure it satisfies basic requirements for
12/// es-entity compatibility. The trait ensures trait implementations and compile-time validation that required fields (like id) are present.
13/// Implemented by the [`EsEvent`][es_entity_macros::EsEvent] derive macro with `#[es_event]` attribute.
14///
15/// # Example
16///
17/// ```compile_fail
18/// use es_entity::*;
19/// use serde::{Serialize, Deserialize};
20///
21/// entity_id!{ UserId }
22///
23/// // Compile-time error: missing `id` attribute in `es_event`
24/// #[derive(EsEvent, Serialize, Deserialize)]
25/// #[serde(tag = "type", rename_all = "snake_case")]
26/// // #[es_event(id = "UserId")] <- This line is required!
27/// pub enum UserEvent {
28///     Initialized { id: UserId, name: String },
29///     NameUpdated { name: String },
30///     Deactivated { reason: String }
31/// }
32/// ```
33///
34/// Correct usage:
35///
36/// ```rust
37/// use es_entity::*;
38/// use serde::{Serialize, Deserialize};
39///
40/// entity_id!{ UserId }
41///
42/// #[derive(EsEvent, Serialize, Deserialize)]
43/// #[serde(tag = "type", rename_all = "snake_case")]
44/// #[es_event(id = "UserId")]
45/// pub enum UserEvent {
46///     Initialized { id: UserId, name: String },
47///     NameUpdated { name: String },
48///     Deactivated { reason: String }
49/// }
50/// ```
51pub trait EsEvent: DeserializeOwned + Serialize + Send + Sync {
52    #[cfg(feature = "instrument")]
53    type EntityId: Clone
54        + PartialEq
55        + sqlx::Type<db::Db>
56        + Eq
57        + std::hash::Hash
58        + Send
59        + Sync
60        + std::fmt::Debug;
61
62    #[cfg(not(feature = "instrument"))]
63    type EntityId: Clone + PartialEq + sqlx::Type<db::Db> + Eq + std::hash::Hash + Send + Sync;
64
65    fn event_context() -> bool;
66    fn event_type(&self) -> &'static str;
67
68    /// Whether this event type has any `Forgettable<T>` fields.
69    ///
70    /// The `#[derive(EsEvent)]` macro sets this automatically via an inherent const
71    /// that shadows this default. Manual implementors can override it if needed.
72    #[doc(hidden)]
73    const HAS_FORGETTABLE_FIELDS: bool = false;
74}
75
76/// Required trait for converting new entities into their initial events before persistence.
77///
78/// All `NewEntity` types must implement this trait and its `into_events` method to emit the initial
79/// events that need to be persisted, later the `Entity` is re-constructed by replaying these events.
80///
81/// # Example
82///
83/// ```rust
84/// use es_entity::*;
85/// use serde::{Serialize, Deserialize};
86///
87/// entity_id!{ UserId }
88///
89/// #[derive(EsEvent, Serialize, Deserialize)]
90/// #[serde(tag = "type", rename_all = "snake_case")]
91/// #[es_event(id = "UserId")]
92/// pub enum UserEvent {
93///     Initialized { id: UserId, name: String },
94///     NameUpdated { name: String }
95/// }
96///
97/// // The main `Entity` type
98/// #[derive(EsEntity)]
99/// pub struct User {
100///     pub id: UserId,
101///     name: String,
102///     events: EntityEvents<UserEvent>
103/// }
104///
105/// // The `NewEntity` type used for initialization.
106/// pub struct NewUser {
107///     id: UserId,
108///     name: String
109/// }
110///
111/// // The `IntoEvents` implementation which emits an event stream.
112/// // These events help track `Entity` state mutations
113/// // Returns the `EntityEvents<UserEvent>`
114/// impl IntoEvents<UserEvent> for NewUser {
115///     fn into_events(self) -> EntityEvents<UserEvent> {
116///         EntityEvents::init(
117///             self.id,
118///             [UserEvent::Initialized {
119///                 id: self.id,
120///                 name: self.name,
121///             }],
122///         )
123///     }
124/// }
125///
126/// // The `TryFromEvents` implementation to hydrate entities by replaying events chronologically.
127/// impl TryFromEvents<UserEvent> for User {
128///     fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EntityHydrationError> {
129///         let mut name = String::new();
130///         for event in events.iter_all() {
131///              match event {
132///                 UserEvent::Initialized { name: n, .. } => name = n.clone(),
133///                 UserEvent::NameUpdated { name: n, .. } => name = n.clone(),
134///                 // ...similarly other events can be matched
135///             }
136///         }
137///         Ok(User { id: events.id().clone(), name, events })
138///     }
139/// }
140/// ```
141pub trait IntoEvents<E: EsEvent> {
142    /// Method to implement which emits event stream from a `NewEntity`
143    fn into_events(self) -> EntityEvents<E>;
144}
145
146/// Required trait for re-constructing entities from their events in chronological order.
147///
148/// All `Entity` types must implement this trait and its `try_from_events` method to hydrate
149/// entities post-persistence.
150///
151/// # Example
152///
153/// ```rust
154/// use es_entity::*;
155/// use serde::{Serialize, Deserialize};
156///
157/// entity_id!{ UserId }
158///
159/// #[derive(EsEvent, Serialize, Deserialize)]
160/// #[serde(tag = "type", rename_all = "snake_case")]
161/// #[es_event(id = "UserId")]
162/// pub enum UserEvent {
163///     Initialized { id: UserId, name: String },
164///     NameUpdated { name: String }
165/// }
166///
167/// // The main 'Entity' type
168/// #[derive(EsEntity)]
169/// pub struct User {
170///     pub id: UserId,
171///     name: String,
172///     events: EntityEvents<UserEvent>
173/// }
174///
175/// // The 'NewEntity' type used for initialization.
176/// pub struct NewUser {
177///     id: UserId,
178///     name: String
179/// }
180///
181/// // The IntoEvents implementation which emits an event stream.
182/// impl IntoEvents<UserEvent> for NewUser {
183///     fn into_events(self) -> EntityEvents<UserEvent> {
184///         EntityEvents::init(
185///             self.id,
186///             [UserEvent::Initialized {
187///                 id: self.id,
188///                 name: self.name,
189///             }],
190///         )
191///     }
192/// }
193///
194/// // The `TryFromEvents` implementation to hydrate entities by replaying events chronologically.
195/// // Returns the re-constructed `User` entity
196/// impl TryFromEvents<UserEvent> for User {
197///     fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EntityHydrationError> {
198///         let mut name = String::new();
199///         for event in events.iter_all() {
200///              match event {
201///                 UserEvent::Initialized { name: n, .. } => name = n.clone(),
202///                 UserEvent::NameUpdated { name: n, .. } => name = n.clone(),
203///                 // ...similarly other events can be matched
204///             }
205///         }
206///         Ok(User { id: events.id().clone(), name, events })
207///     }
208/// }
209/// ```
210pub trait TryFromEvents<E: EsEvent> {
211    /// Method to implement which hydrates `Entity` by replaying its events chronologically
212    fn try_from_events(events: EntityEvents<E>) -> Result<Self, EntityHydrationError>
213    where
214        Self: Sized;
215}
216
217/// Required trait for all entities to be compatible and recognised by es-entity.
218///
219/// All `Entity` types implement this trait to satisfy the basic requirements for
220/// event sourcing. The trait ensures the entity implements traits like `IntoEvents`
221/// and has the required components like `EntityEvent`, with helper methods to access the events sequence.
222/// Implemented by the [`EsEntity`][es_entity_macros::EsEntity] derive macro.
223///
224/// # Example
225///
226/// ```compile_fail
227/// use es_entity::*;
228/// use serde::{Serialize, Deserialize};
229///
230/// entity_id!{ UserId }
231///
232/// #[derive(EsEvent, Serialize, Deserialize)]
233/// #[serde(tag = "type", rename_all = "snake_case")]
234/// #[es_event(id = "UserId")]
235/// pub enum UserEvent {
236///     Initialized { id: UserId, name: String },
237/// }
238///
239/// // Compile-time error: Missing required trait implementations
240/// // - TryFromEvents<UserEvent> for User
241/// // - IntoEvents<UserEvent> for NewUser (associated type New)
242/// // - NewUser type definition
243/// #[derive(EsEntity)]
244/// pub struct User {
245///     pub id: UserId,
246///     pub name: String,
247///     events: EntityEvents<UserEvent>,
248/// }
249/// ```
250pub trait EsEntity: TryFromEvents<Self::Event> + Send {
251    type Event: EsEvent;
252    type New: IntoEvents<Self::Event>;
253
254    /// Returns an immutable reference to the entity's events
255    fn events(&self) -> &EntityEvents<Self::Event>;
256
257    /// Returns the last `n` persisted events
258    fn last_persisted(&self, n: usize) -> crate::events::LastPersisted<'_, Self::Event> {
259        self.events().last_persisted(n)
260    }
261
262    /// Returns mutable reference to the entity's events
263    fn events_mut(&mut self) -> &mut EntityEvents<Self::Event>;
264}
265
266/// Required trait for all repositories to be compatible with es-entity and generate functions.
267///
268/// All repositories implement this trait to satisfy the basic requirements for
269/// type-safe database operations with the associated entity. The trait ensures validation
270/// that required fields (like entity) are present with compile-time errors.
271/// Implemented by the [`EsRepo`][es_entity_macros::EsRepo] derive macro with `#[es_repo]` attributes.
272///
273/// # Example
274///
275/// ```ignore
276///
277/// // Would show error for missing entity field if not provided in the `es_repo` attribute
278/// #[derive(EsRepo, Debug)]
279/// #[es_repo(entity = "User", columns(name(ty = "String")))]
280/// pub struct Users {
281///     pool: PgPool,  // Required field for database operations
282/// }
283///
284/// impl Users {
285///     pub fn new(pool: PgPool) -> Self {
286///         Self { pool }
287///    }
288/// }
289/// ```
290pub trait EsRepo: Send {
291    type Entity: EsEntity;
292    type CreateError;
293    type ModifyError;
294    type FindError: From<sqlx::Error> + From<EntityHydrationError> + Send;
295    type QueryError: From<sqlx::Error> + From<EntityHydrationError> + Send;
296    type EsQueryFlavor;
297
298    fn nested_tree_spec() -> TreeSpec;
299
300    fn hydrate_nested_from_rows<E>(
301        rows_by_tag: &mut HashMap<i32, Vec<db::Row>>,
302        tag_cursor: &mut i32,
303        entities: &mut [Self::Entity],
304    ) -> Result<(), E>
305    where
306        E: From<sqlx::Error> + From<EntityHydrationError>;
307}
308
309pub trait RetryableInto<T>: Into<T> + Copy + std::fmt::Debug {}
310impl<T, O> RetryableInto<O> for T where T: Into<O> + Copy + std::fmt::Debug {}