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 {}