Skip to main content

es_entity/
id_source.rs

1//! Where a repo's entities get their id from, and the one public route into
2//! an [`EntityEvents`] container.
3//!
4//! A repo resolves the id of every entity it creates according to its
5//! [`IdSource`]: [`FromRepo`] (the default, the repo generates it) or
6//! [`FromCaller`] (the caller puts it in the `New*` value). The
7//! [`IntoEvents`] implementation of a `New*` type receives an
8//! [`EntityEventsBuilder`] whose second parameter is that source, so
9//! [`EntityEventsBuilder::id`] exists only when the repo resolved one, and a
10//! [`FromRepo`] `New*` has no API through which a caller-chosen id can reach
11//! the insert.
12//!
13//! [`IntoEvents`]: crate::IntoEvents
14
15use crate::{events::EntityEvents, traits::EsEvent};
16
17mod sealed {
18    pub trait Sealed {}
19}
20
21/// Where the id of a newly created entity comes from. Sealed: [`FromRepo`]
22/// and [`FromCaller`] are the only sources.
23pub trait IdSource: sealed::Sealed {
24    /// What the [`EntityEventsBuilder`] carries: the resolved id for
25    /// [`FromRepo`], nothing for [`FromCaller`].
26    type Slot<Id>;
27}
28
29/// The repo resolved the id before `into_events` runs; the `New*` type has no
30/// id field and reads it with [`EntityEventsBuilder::id`].
31pub struct FromRepo;
32impl sealed::Sealed for FromRepo {}
33impl IdSource for FromRepo {
34    type Slot<Id> = Id;
35}
36
37/// The caller puts the id (a deterministic one) in the `New*` value, which
38/// hands it to [`EntityEventsBuilder::initial_events`]. A caller's id is a
39/// uniqueness claim, so the primary key stays among the repo's constraint
40/// violations.
41pub struct FromCaller;
42impl sealed::Sealed for FromCaller {}
43impl IdSource for FromCaller {
44    type Slot<Id> = ();
45}
46
47/// The id source applied, no events yet. Handed to
48/// [`IntoEvents::into_events`](crate::IntoEvents::into_events) by the
49/// generated repo code and consumed by `initial_events`, the only public way
50/// to build an [`EntityEvents`] container.
51pub struct EntityEventsBuilder<E: EsEvent, S: IdSource> {
52    id: S::Slot<E::EntityId>,
53}
54
55impl<E: EsEvent> EntityEventsBuilder<E, FromRepo> {
56    #[doc(hidden)]
57    pub fn __from_repo(id: E::EntityId) -> Self {
58        Self { id }
59    }
60
61    /// The id the repo resolved for this entity.
62    pub fn id(&self) -> E::EntityId {
63        self.id.clone()
64    }
65
66    /// The entity's initial events, under the resolved id.
67    pub fn initial_events(self, events: impl IntoIterator<Item = E>) -> EntityEvents<E> {
68        EntityEvents::init(self.id, events)
69    }
70}
71
72impl<E: EsEvent> EntityEventsBuilder<E, FromCaller> {
73    #[doc(hidden)]
74    pub fn __from_caller() -> Self {
75        Self { id: () }
76    }
77
78    /// The entity's initial events under the id the caller put in the `New*`
79    /// value.
80    pub fn initial_events(
81        self,
82        id: E::EntityId,
83        events: impl IntoIterator<Item = E>,
84    ) -> EntityEvents<E> {
85        EntityEvents::init(id, events)
86    }
87}
88
89/// What `create_or_find_in_op` did.
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum Ensured<E> {
92    /// This call inserted the entity.
93    Created(E),
94    /// A unique key of the `new` entity (a caller-chosen id included) named a
95    /// stored entity (a concurrent or earlier create won); this is the stored
96    /// entity. Its id may differ from the `new` entity's id.
97    Found(E),
98}
99
100impl<E> Ensured<E> {
101    /// The entity, however it came about.
102    pub fn into_inner(self) -> E {
103        match self {
104            Self::Created(entity) | Self::Found(entity) => entity,
105        }
106    }
107
108    pub fn is_created(&self) -> bool {
109        matches!(self, Self::Created(_))
110    }
111}