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