es_entity/
traits.rs

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