Skip to main content

teaql_runtime/repository/
base.rs

1use teaql_core::{
2    BatchInsertCommand, BatchUpdateCommand, DeleteCommand, Entity, InsertCommand, Record,
3    RecoverCommand, SelectQuery, SmartList, UpdateCommand,
4};
5use teaql_sql::{CompiledQuery, SqlDialect};
6
7use crate::{MetadataStore, RepositoryError, RuntimeError};
8
9use super::{QueryExecutor, Repository};
10
11impl<'a, D, M, E> Repository<'a, D, M, E>
12where
13    D: SqlDialect,
14    M: MetadataStore,
15    E: QueryExecutor,
16{
17    pub fn new(dialect: &'a D, metadata: &'a M, executor: &'a E) -> Self {
18        Self {
19            dialect,
20            metadata,
21            executor,
22        }
23    }
24
25    pub fn compile(&self, query: &SelectQuery) -> Result<CompiledQuery, RuntimeError> {
26        let entity = self
27            .metadata
28            .entity(&query.entity)
29            .ok_or_else(|| RuntimeError::MissingEntity(query.entity.clone()))?;
30        Ok(self.dialect.compile_select(entity, query)?)
31    }
32
33    pub fn compile_insert(&self, command: &InsertCommand) -> Result<CompiledQuery, RuntimeError> {
34        let entity = self
35            .metadata
36            .entity(&command.entity)
37            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
38        Ok(self.dialect.compile_insert(entity, command)?)
39    }
40
41    pub fn compile_update(&self, command: &UpdateCommand) -> Result<CompiledQuery, RuntimeError> {
42        let entity = self
43            .metadata
44            .entity(&command.entity)
45            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
46        Ok(self.dialect.compile_update(entity, command)?)
47    }
48
49    pub fn compile_batch_insert(
50        &self,
51        command: &BatchInsertCommand,
52    ) -> Result<CompiledQuery, RuntimeError> {
53        let entity = self
54            .metadata
55            .entity(&command.entity)
56            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
57        Ok(self.dialect.compile_batch_insert(entity, command)?)
58    }
59
60    pub fn compile_batch_update(
61        &self,
62        command: &BatchUpdateCommand,
63    ) -> Result<CompiledQuery, RuntimeError> {
64        let entity = self
65            .metadata
66            .entity(&command.entity)
67            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
68        Ok(self.dialect.compile_batch_update(entity, command)?)
69    }
70
71    pub fn compile_delete(&self, command: &DeleteCommand) -> Result<CompiledQuery, RuntimeError> {
72        let entity = self
73            .metadata
74            .entity(&command.entity)
75            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
76        Ok(self.dialect.compile_delete(entity, command)?)
77    }
78
79    pub fn compile_recover(&self, command: &RecoverCommand) -> Result<CompiledQuery, RuntimeError> {
80        let entity = self
81            .metadata
82            .entity(&command.entity)
83            .ok_or_else(|| RuntimeError::MissingEntity(command.entity.clone()))?;
84        Ok(self.dialect.compile_recover(entity, command)?)
85    }
86
87    pub fn fetch_all(&self, query: &SelectQuery) -> Result<Vec<Record>, RepositoryError<E::Error>> {
88        let compiled = self.compile(query).map_err(RepositoryError::Runtime)?;
89        self.executor
90            .fetch_all(&compiled)
91            .map_err(RepositoryError::Executor)
92    }
93
94    pub fn fetch_smart_list(
95        &self,
96        query: &SelectQuery,
97    ) -> Result<SmartList<Record>, RepositoryError<E::Error>> {
98        self.fetch_all(query).map(SmartList::from)
99    }
100
101    pub fn fetch_entities<T>(
102        &self,
103        query: &SelectQuery,
104    ) -> Result<SmartList<T>, RepositoryError<E::Error>>
105    where
106        T: Entity,
107    {
108        self.fetch_all(query)?
109            .into_iter()
110            .map(T::from_record)
111            .collect::<Result<Vec<_>, _>>()
112            .map(SmartList::from)
113            .map_err(RepositoryError::Entity)
114    }
115
116    pub fn fetch_enhanced_entities<T>(
117        &self,
118        query: &SelectQuery,
119    ) -> Result<SmartList<T>, RepositoryError<E::Error>>
120    where
121        T: Entity,
122    {
123        self.fetch_entities(query)
124    }
125
126    pub fn insert(&self, command: &InsertCommand) -> Result<u64, RepositoryError<E::Error>> {
127        let compiled = self
128            .compile_insert(command)
129            .map_err(RepositoryError::Runtime)?;
130        self.executor
131            .execute(&compiled)
132            .map_err(RepositoryError::Executor)
133    }
134
135    pub fn update(&self, command: &UpdateCommand) -> Result<u64, RepositoryError<E::Error>> {
136        let compiled = self
137            .compile_update(command)
138            .map_err(RepositoryError::Runtime)?;
139        let affected = self
140            .executor
141            .execute(&compiled)
142            .map_err(RepositoryError::Executor)?;
143
144        if command.expected_version.is_some() && affected == 0 {
145            return Err(RepositoryError::Runtime(
146                RuntimeError::OptimisticLockConflict {
147                    entity: command.entity.clone(),
148                    id: format!("{:?}", command.id),
149                },
150            ));
151        }
152
153        Ok(affected)
154    }
155
156    pub fn delete(&self, command: &DeleteCommand) -> Result<u64, RepositoryError<E::Error>> {
157        let compiled = self
158            .compile_delete(command)
159            .map_err(RepositoryError::Runtime)?;
160        let affected = self
161            .executor
162            .execute(&compiled)
163            .map_err(RepositoryError::Executor)?;
164
165        if command.expected_version.is_some() && affected == 0 {
166            return Err(RepositoryError::Runtime(
167                RuntimeError::OptimisticLockConflict {
168                    entity: command.entity.clone(),
169                    id: format!("{:?}", command.id),
170                },
171            ));
172        }
173
174        Ok(affected)
175    }
176
177    pub fn batch_insert(
178        &self,
179        command: &teaql_core::BatchInsertCommand,
180    ) -> Result<u64, RepositoryError<E::Error>> {
181        let compiled = self
182            .compile_batch_insert(command)
183            .map_err(RepositoryError::Runtime)?;
184        self.executor
185            .execute(&compiled)
186            .map_err(RepositoryError::Executor)
187    }
188
189    pub fn batch_update(
190        &self,
191        command: &teaql_core::BatchUpdateCommand,
192    ) -> Result<u64, RepositoryError<E::Error>> {
193        let compiled = self
194            .compile_batch_update(command)
195            .map_err(RepositoryError::Runtime)?;
196        let affected = self
197            .executor
198            .execute(&compiled)
199            .map_err(RepositoryError::Executor)?;
200
201        if command.batch_expected_versions.iter().any(|v| v.is_some()) {
202            if affected != command.batch_ids.len() as u64 {
203                // To be precise we should know WHICH id failed, but for now we just error
204                return Err(RepositoryError::Runtime(
205                    RuntimeError::OptimisticLockConflict {
206                        entity: command.entity.clone(),
207                        id: "BATCH".to_owned(),
208                    },
209                ));
210            }
211        }
212
213        Ok(affected)
214    }
215
216    pub fn recover(&self, command: &RecoverCommand) -> Result<u64, RepositoryError<E::Error>> {
217        let compiled = self
218            .compile_recover(command)
219            .map_err(RepositoryError::Runtime)?;
220        let affected = self
221            .executor
222            .execute(&compiled)
223            .map_err(RepositoryError::Executor)?;
224
225        if affected == 0 {
226            return Err(RepositoryError::Runtime(
227                RuntimeError::OptimisticLockConflict {
228                    entity: command.entity.clone(),
229                    id: format!("{:?}", command.id),
230                },
231            ));
232        }
233
234        Ok(affected)
235    }
236
237    pub fn insert_many(
238        &self,
239        commands: &[InsertCommand],
240    ) -> Result<u64, RepositoryError<E::Error>> {
241        let mut total = 0;
242        for command in commands {
243            total += self.insert(command)?;
244        }
245        Ok(total)
246    }
247
248    pub fn update_many(
249        &self,
250        commands: &[UpdateCommand],
251    ) -> Result<u64, RepositoryError<E::Error>> {
252        let mut total = 0;
253        for command in commands {
254            total += self.update(command)?;
255        }
256        Ok(total)
257    }
258
259    pub fn delete_many(
260        &self,
261        commands: &[DeleteCommand],
262    ) -> Result<u64, RepositoryError<E::Error>> {
263        let mut total = 0;
264        for command in commands {
265            total += self.delete(command)?;
266        }
267        Ok(total)
268    }
269
270    pub fn recover_many(
271        &self,
272        commands: &[RecoverCommand],
273    ) -> Result<u64, RepositoryError<E::Error>> {
274        let mut total = 0;
275        for command in commands {
276            total += self.recover(command)?;
277        }
278        Ok(total)
279    }
280}