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 super::{error::EntityHydrationError, 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    fn event_type(&self) -> &'static str;
71}
72
73/// Required trait for converting new entities into their initial events before persistence.
74///
75/// All `NewEntity` types must implement this trait and its `into_events` method to emit the initial
76/// events that need to be persisted, later the `Entity` is re-constructed by replaying these events.
77///
78/// # Example
79///
80/// ```rust
81/// use es_entity::*;
82/// use serde::{Serialize, Deserialize};
83///
84/// entity_id!{ UserId }
85///
86/// #[derive(EsEvent, Serialize, Deserialize)]
87/// #[serde(tag = "type", rename_all = "snake_case")]
88/// #[es_event(id = "UserId")]
89/// pub enum UserEvent {
90///     Initialized { id: UserId, name: String },
91///     NameUpdated { name: String }
92/// }
93///
94/// // The main `Entity` type
95/// #[derive(EsEntity)]
96/// pub struct User {
97///     pub id: UserId,
98///     name: String,
99///     events: EntityEvents<UserEvent>
100/// }
101///
102/// // The `NewEntity` type used for initialization.
103/// pub struct NewUser {
104///     id: UserId,
105///     name: String
106/// }
107///
108/// // The `IntoEvents` implementation which emits an event stream.
109/// // These events help track `Entity` state mutations
110/// // Returns the `EntityEvents<UserEvent>`
111/// impl IntoEvents<UserEvent> for NewUser {
112///     fn into_events(self) -> EntityEvents<UserEvent> {
113///         EntityEvents::init(
114///             self.id,
115///             [UserEvent::Initialized {
116///                 id: self.id,
117///                 name: self.name,
118///             }],
119///         )
120///     }
121/// }
122///
123/// // The `TryFromEvents` implementation to hydrate entities by replaying events chronologically.
124/// impl TryFromEvents<UserEvent> for User {
125///     fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EntityHydrationError> {
126///         let mut name = String::new();
127///         for event in events.iter_all() {
128///              match event {
129///                 UserEvent::Initialized { name: n, .. } => name = n.clone(),
130///                 UserEvent::NameUpdated { name: n, .. } => name = n.clone(),
131///                 // ...similarly other events can be matched
132///             }
133///         }
134///         Ok(User { id: events.id().clone(), name, events })
135///     }
136/// }
137/// ```
138pub trait IntoEvents<E: EsEvent> {
139    /// Method to implement which emits event stream from a `NewEntity`
140    fn into_events(self) -> EntityEvents<E>;
141}
142
143/// Required trait for re-constructing entities from their events in chronological order.
144///
145/// All `Entity` types must implement this trait and its `try_from_events` method to hydrate
146/// entities post-persistence.
147///
148/// # Example
149///
150/// ```rust
151/// use es_entity::*;
152/// use serde::{Serialize, Deserialize};
153///
154/// entity_id!{ UserId }
155///
156/// #[derive(EsEvent, Serialize, Deserialize)]
157/// #[serde(tag = "type", rename_all = "snake_case")]
158/// #[es_event(id = "UserId")]
159/// pub enum UserEvent {
160///     Initialized { id: UserId, name: String },
161///     NameUpdated { name: String }
162/// }
163///
164/// // The main 'Entity' type
165/// #[derive(EsEntity)]
166/// pub struct User {
167///     pub id: UserId,
168///     name: String,
169///     events: EntityEvents<UserEvent>
170/// }
171///
172/// // The 'NewEntity' type used for initialization.
173/// pub struct NewUser {
174///     id: UserId,
175///     name: String
176/// }
177///
178/// // The IntoEvents implementation which emits an event stream.
179/// impl IntoEvents<UserEvent> for NewUser {
180///     fn into_events(self) -> EntityEvents<UserEvent> {
181///         EntityEvents::init(
182///             self.id,
183///             [UserEvent::Initialized {
184///                 id: self.id,
185///                 name: self.name,
186///             }],
187///         )
188///     }
189/// }
190///
191/// // The `TryFromEvents` implementation to hydrate entities by replaying events chronologically.
192/// // Returns the re-constructed `User` entity
193/// impl TryFromEvents<UserEvent> for User {
194///     fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EntityHydrationError> {
195///         let mut name = String::new();
196///         for event in events.iter_all() {
197///              match event {
198///                 UserEvent::Initialized { name: n, .. } => name = n.clone(),
199///                 UserEvent::NameUpdated { name: n, .. } => name = n.clone(),
200///                 // ...similarly other events can be matched
201///             }
202///         }
203///         Ok(User { id: events.id().clone(), name, events })
204///     }
205/// }
206/// ```
207pub trait TryFromEvents<E: EsEvent> {
208    /// Method to implement which hydrates `Entity` by replaying its events chronologically
209    fn try_from_events(events: EntityEvents<E>) -> Result<Self, EntityHydrationError>
210    where
211        Self: Sized;
212}
213
214/// Required trait for all entities to be compatible and recognised by es-entity.
215///
216/// All `Entity` types implement this trait to satisfy the basic requirements for
217/// event sourcing. The trait ensures the entity implements traits like `IntoEvents`
218/// and has the required components like `EntityEvent`, with helper methods to access the events sequence.
219/// Implemented by the [`EsEntity`][es_entity_macros::EsEntity] derive macro.
220///
221/// # Example
222///
223/// ```compile_fail
224/// use es_entity::*;
225/// use serde::{Serialize, Deserialize};
226///
227/// entity_id!{ UserId }
228///
229/// #[derive(EsEvent, Serialize, Deserialize)]
230/// #[serde(tag = "type", rename_all = "snake_case")]
231/// #[es_event(id = "UserId")]
232/// pub enum UserEvent {
233///     Initialized { id: UserId, name: String },
234/// }
235///
236/// // Compile-time error: Missing required trait implementations
237/// // - TryFromEvents<UserEvent> for User
238/// // - IntoEvents<UserEvent> for NewUser (associated type New)
239/// // - NewUser type definition
240/// #[derive(EsEntity)]
241/// pub struct User {
242///     pub id: UserId,
243///     pub name: String,
244///     events: EntityEvents<UserEvent>,
245/// }
246/// ```
247pub trait EsEntity: TryFromEvents<Self::Event> + Send {
248    type Event: EsEvent;
249    type New: IntoEvents<Self::Event>;
250
251    /// Returns an immutable reference to the entity's events
252    fn events(&self) -> &EntityEvents<Self::Event>;
253
254    /// Returns the last `n` persisted events
255    fn last_persisted(&self, n: usize) -> crate::events::LastPersisted<'_, Self::Event> {
256        self.events().last_persisted(n)
257    }
258
259    /// Returns mutable reference to the entity's events
260    fn events_mut(&mut self) -> &mut EntityEvents<Self::Event>;
261}
262
263/// Required trait for all repositories to be compatible with es-entity and generate functions.
264///
265/// All repositories implement this trait to satisfy the basic requirements for
266/// type-safe database operations with the associated entity. The trait ensures validation
267/// that required fields (like entity) are present with compile-time errors.
268/// Implemented by the [`EsRepo`][es_entity_macros::EsRepo] derive macro with `#[es_repo]` attributes.
269///
270/// # Example
271///
272/// ```ignore
273///
274/// // Would show error for missing entity field if not provided in the `es_repo` attribute
275/// #[derive(EsRepo, Debug)]
276/// #[es_repo(entity = "User", columns(name(ty = "String")))]
277/// pub struct Users {
278///     pool: PgPool,  // Required field for database operations
279/// }
280///
281/// impl Users {
282///     pub fn new(pool: PgPool) -> Self {
283///         Self { pool }
284///    }
285/// }
286/// ```
287pub trait EsRepo: Send {
288    type Entity: EsEntity;
289    type CreateError;
290    type ModifyError;
291    type FindError: From<sqlx::Error> + From<EntityHydrationError> + Send;
292    type QueryError: From<sqlx::Error> + From<EntityHydrationError> + Send;
293    type EsQueryFlavor;
294
295    /// Loads all nested entities for a given set of parent entities within an atomic operation.
296    fn load_all_nested_in_op<OP, E>(
297        op: &mut OP,
298        entities: &mut [Self::Entity],
299    ) -> impl Future<Output = Result<(), E>> + Send
300    where
301        OP: AtomicOperation,
302        E: From<sqlx::Error> + From<EntityHydrationError> + Send;
303}
304
305pub trait RetryableInto<T>: Into<T> + Copy + std::fmt::Debug {}
306impl<T, O> RetryableInto<O> for T where T: Into<O> + Copy + std::fmt::Debug {}