Skip to main content

es_entity/
nested.rs

1//! Handle operations for nested entities.
2
3use crate::{error::EntityHydrationError, operation::AtomicOperation, traits::*};
4
5use std::collections::HashMap;
6
7pub struct Nested<T: EsEntity> {
8    entities: HashMap<<<T as EsEntity>::Event as EsEvent>::EntityId, T>,
9    new_entities: Vec<<T as EsEntity>::New>,
10}
11
12impl<T: EsEntity> Default for Nested<T> {
13    fn default() -> Self {
14        Self {
15            entities: HashMap::new(),
16            new_entities: Vec::new(),
17        }
18    }
19}
20
21impl<T: EsEntity> Nested<T> {
22    pub fn add_new(&mut self, new: <T as EsEntity>::New) -> &<T as EsEntity>::New {
23        let len = self.new_entities.len();
24        self.new_entities.push(new);
25        &self.new_entities[len]
26    }
27
28    pub fn len_new(&self) -> usize {
29        self.new_entities.len()
30    }
31
32    pub fn find_new<P>(&self, predicate: P) -> Option<&<T as EsEntity>::New>
33    where
34        P: FnMut(&&<T as EsEntity>::New) -> bool,
35    {
36        self.new_entities.iter().find(predicate)
37    }
38
39    pub fn find_map_new<B, F>(&self, f: F) -> Option<B>
40    where
41        F: FnMut(&<T as EsEntity>::New) -> Option<B>,
42    {
43        self.new_entities.iter().find_map(f)
44    }
45
46    pub fn new_entities_mut(&mut self) -> &mut Vec<<T as EsEntity>::New> {
47        &mut self.new_entities
48    }
49
50    pub fn get_persisted(&self, id: &<<T as EsEntity>::Event as EsEvent>::EntityId) -> Option<&T> {
51        self.entities.get(id)
52    }
53
54    pub fn get_persisted_mut(
55        &mut self,
56        id: &<<T as EsEntity>::Event as EsEvent>::EntityId,
57    ) -> Option<&mut T> {
58        self.entities.get_mut(id)
59    }
60
61    pub fn find_persisted<P>(&self, predicate: P) -> Option<&T>
62    where
63        P: FnMut(&&T) -> bool,
64    {
65        self.entities.values().find(predicate)
66    }
67
68    pub fn find_map_persisted<B, F>(&self, f: F) -> Option<B>
69    where
70        F: FnMut(&T) -> Option<B>,
71    {
72        self.entities.values().find_map(f)
73    }
74
75    pub fn find_persisted_mut<P>(&mut self, predicate: P) -> Option<&mut T>
76    where
77        P: FnMut(&&mut T) -> bool,
78    {
79        self.entities.values_mut().find(predicate)
80    }
81
82    pub fn len_persisted(&self) -> usize {
83        self.entities.len()
84    }
85
86    pub fn iter_persisted(
87        &self,
88    ) -> std::collections::hash_map::Values<'_, <<T as EsEntity>::Event as EsEvent>::EntityId, T>
89    {
90        self.entities.values()
91    }
92
93    pub fn iter_persisted_mut(
94        &mut self,
95    ) -> std::collections::hash_map::ValuesMut<'_, <<T as EsEntity>::Event as EsEvent>::EntityId, T>
96    {
97        self.entities.values_mut()
98    }
99
100    pub fn load(&mut self, entities: impl IntoIterator<Item = T>) {
101        self.entities.extend(
102            entities
103                .into_iter()
104                .map(|entity| (entity.events().entity_id.clone(), entity)),
105        );
106    }
107}
108
109pub trait PopulateNested<ID>: EsRepo {
110    fn populate_in_op<OP, P, E>(
111        op: &mut OP,
112        lookup: std::collections::HashMap<ID, &mut P>,
113    ) -> impl Future<Output = Result<(), E>> + Send
114    where
115        OP: AtomicOperation,
116        P: Parent<<Self as EsRepo>::Entity>,
117        E: From<sqlx::Error> + From<EntityHydrationError> + Send;
118
119    /// Like [`populate_in_op`](PopulateNested::populate_in_op) but includes soft-deleted children.
120    ///
121    /// Default implementation delegates to `populate_in_op` (correct for repos without soft-delete).
122    fn populate_in_op_include_deleted<OP, P, E>(
123        op: &mut OP,
124        lookup: std::collections::HashMap<ID, &mut P>,
125    ) -> impl Future<Output = Result<(), E>> + Send
126    where
127        OP: AtomicOperation,
128        P: Parent<<Self as EsRepo>::Entity>,
129        E: From<sqlx::Error> + From<EntityHydrationError> + Send,
130    {
131        Self::populate_in_op(op, lookup)
132    }
133}
134
135/// Trait for cascade soft-deleting child entities when a parent is deleted.
136///
137/// Generated automatically for nested repositories that have both a `parent` column
138/// and `delete = "soft"` configured.
139pub trait CascadeDeleteNested<ID>: EsRepo {
140    fn cascade_delete_in_op<OP, E>(
141        op: &mut OP,
142        parent_id: &ID,
143    ) -> impl Future<Output = Result<(), E>> + Send
144    where
145        OP: AtomicOperation,
146        E: From<sqlx::Error> + Send;
147}
148
149/// Trait that entities implement for every field marked `#[es_entity(nested)]`
150///
151/// Will be auto-implemented when [`#[derive(EsEntity)]`](`EsEntity`) is used.
152pub trait Parent<T: EsEntity>: Send {
153    /// Access new child entities to persist them.
154    fn new_children_mut(&mut self) -> &mut Vec<<T as EsEntity>::New>;
155    /// Access existing children to update them incase they were mutated.
156    fn iter_persisted_children_mut(
157        &mut self,
158    ) -> std::collections::hash_map::ValuesMut<'_, <<T as EsEntity>::Event as EsEvent>::EntityId, T>;
159    /// Inject hydrated children while loading the parent.
160    fn inject_children(&mut self, entities: impl IntoIterator<Item = T>);
161}