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