1use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10
11use vantage_types::{EmptyEntity, Entity, Record};
12
13use crate::table::Table;
14use crate::traits::table_source::TableSource;
15use crate::write_context::WriteContext;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
20pub enum Phase {
21 Normalize,
23 Populate,
25 Validate,
27}
28
29pub enum HookReturn {
31 Proceed,
33 Handled,
36}
37
38pub type BoxFuture<'r, X> = Pin<Box<dyn Future<Output = X> + Send + 'r>>;
40
41#[non_exhaustive]
53pub struct BeforeCx<'r, T: TableSource> {
54 pub record: &'r mut Record<T::Value>,
55 pub id: Option<&'r T::Id>,
56 pub stored: Option<&'r Record<T::Value>>,
57 pub patch: bool,
58 pub context: Option<&'r WriteContext>,
59 pub table: &'r Table<T, EmptyEntity>,
60}
61
62#[non_exhaustive]
66pub struct AfterCx<'r, T: TableSource> {
67 pub id: &'r T::Id,
68 pub record: &'r Record<T::Value>,
69 pub before: Option<&'r Record<T::Value>>,
70 pub context: Option<&'r WriteContext>,
71 pub table: &'r Table<T, EmptyEntity>,
72}
73
74#[non_exhaustive]
76pub struct DeleteCx<'r, T: TableSource> {
77 pub id: &'r T::Id,
78 pub record: &'r Record<T::Value>,
79 pub context: Option<&'r WriteContext>,
80 pub table: &'r Table<T, EmptyEntity>,
81}
82
83#[non_exhaustive]
86pub struct DeleteAllCx<'r, T: TableSource> {
87 pub context: Option<&'r WriteContext>,
88 pub table: &'r Table<T, EmptyEntity>,
89}
90
91pub type DeleteAllFn<T> = Arc<
93 dyn for<'r> Fn(DeleteAllCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>> + Send + Sync,
94>;
95
96pub fn delete_all_hook<T, F>(f: F) -> DeleteAllFn<T>
98where
99 T: TableSource,
100 F: for<'r> Fn(DeleteAllCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>>
101 + Send
102 + Sync
103 + 'static,
104{
105 Arc::new(f)
106}
107
108pub type BeforeFn<T> =
111 Arc<dyn for<'r> Fn(BeforeCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>> + Send + Sync>;
112
113pub type BeforeDeleteFn<T> = Arc<
116 dyn for<'r> Fn(DeleteCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<HookReturn>>
117 + Send
118 + Sync,
119>;
120
121pub type AfterFn<T> =
124 Arc<dyn for<'r> Fn(AfterCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>> + Send + Sync>;
125
126pub fn before_hook<T, F>(f: F) -> BeforeFn<T>
128where
129 T: TableSource,
130 F: for<'r> Fn(BeforeCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>>
131 + Send
132 + Sync
133 + 'static,
134{
135 Arc::new(f)
136}
137
138pub fn before_delete_hook<T, F>(f: F) -> BeforeDeleteFn<T>
140where
141 T: TableSource,
142 F: for<'r> Fn(DeleteCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<HookReturn>>
143 + Send
144 + Sync
145 + 'static,
146{
147 Arc::new(f)
148}
149
150pub fn before_hook_sync<T: TableSource>(
154 f: impl Fn(&mut Record<T::Value>, Option<&WriteContext>) -> vantage_core::Result<()>
155 + Send
156 + Sync
157 + 'static,
158) -> BeforeFn<T> {
159 Arc::new(move |cx: BeforeCx<'_, T>| {
160 let outcome = f(cx.record, cx.context);
161 Box::pin(std::future::ready(outcome))
162 })
163}
164
165pub fn before_hook_sync_cx<T: TableSource>(
168 f: impl for<'r> Fn(BeforeCx<'r, T>) -> vantage_core::Result<()> + Send + Sync + 'static,
169) -> BeforeFn<T> {
170 Arc::new(move |cx: BeforeCx<'_, T>| Box::pin(std::future::ready(f(cx))))
171}
172
173pub fn before_delete_hook_sync<T: TableSource>(
176 f: impl Fn(&T::Id, Option<&WriteContext>) -> vantage_core::Result<()> + Send + Sync + 'static,
177) -> BeforeDeleteFn<T> {
178 Arc::new(move |cx: DeleteCx<'_, T>| {
179 let outcome = f(cx.id, cx.context).map(|()| HookReturn::Proceed);
180 Box::pin(std::future::ready(outcome))
181 })
182}
183
184pub fn after_hook<T, F>(f: F) -> AfterFn<T>
186where
187 T: TableSource,
188 F: for<'r> Fn(AfterCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>>
189 + Send
190 + Sync
191 + 'static,
192{
193 Arc::new(f)
194}
195
196#[non_exhaustive]
200pub struct SelectCx<'r, T: TableSource> {
201 pub context: Option<&'r WriteContext>,
202 pub table: &'r Table<T, EmptyEntity>,
203}
204
205pub type SelectFn<T> =
207 Arc<dyn for<'r> Fn(SelectCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>> + Send + Sync>;
208
209pub fn select_hook<T, F>(f: F) -> SelectFn<T>
211where
212 T: TableSource,
213 F: for<'r> Fn(SelectCx<'r, T>) -> BoxFuture<'r, vantage_core::Result<()>>
214 + Send
215 + Sync
216 + 'static,
217{
218 Arc::new(f)
219}
220
221#[non_exhaustive]
225pub enum Hook<T: TableSource> {
226 BeforeSelect(SelectFn<T>),
244 BeforeInsert(Phase, BeforeFn<T>),
245 BeforeUpdate(Phase, BeforeFn<T>),
246 BeforeSave(Phase, BeforeFn<T>),
247 BeforeDelete(BeforeDeleteFn<T>),
248 AfterInsert(AfterFn<T>),
249 AfterUpdate(AfterFn<T>),
250 AfterSave(AfterFn<T>),
251 AfterDelete(AfterFn<T>),
252 BeforeDeleteAll(DeleteAllFn<T>),
254 AfterDeleteAll(DeleteAllFn<T>),
255}
256
257#[derive(Clone)]
260pub struct Hooks<T: TableSource> {
261 pub(crate) before_select: Vec<SelectFn<T>>,
262 pub(crate) before_insert: Vec<(Phase, BeforeFn<T>)>,
263 pub(crate) before_update: Vec<(Phase, BeforeFn<T>)>,
264 pub(crate) before_delete: Vec<BeforeDeleteFn<T>>,
265 pub(crate) after_insert: Vec<AfterFn<T>>,
266 pub(crate) after_update: Vec<AfterFn<T>>,
267 pub(crate) after_delete: Vec<AfterFn<T>>,
268 pub(crate) before_delete_all: Vec<DeleteAllFn<T>>,
269 pub(crate) after_delete_all: Vec<DeleteAllFn<T>>,
270}
271
272impl<T: TableSource> Default for Hooks<T> {
273 fn default() -> Self {
274 Self {
275 before_select: Vec::new(),
276 before_insert: Vec::new(),
277 before_update: Vec::new(),
278 before_delete: Vec::new(),
279 after_insert: Vec::new(),
280 after_update: Vec::new(),
281 after_delete: Vec::new(),
282 before_delete_all: Vec::new(),
283 after_delete_all: Vec::new(),
284 }
285 }
286}
287
288impl<T: TableSource, E: Entity<T::Value>> Table<T, E> {
289 pub(crate) fn before_insert_hooks(&self) -> &[(Phase, BeforeFn<T>)] {
290 &self.hooks.before_insert
291 }
292 pub(crate) fn before_update_hooks(&self) -> &[(Phase, BeforeFn<T>)] {
293 &self.hooks.before_update
294 }
295 pub(crate) fn before_delete_hooks(&self) -> &[BeforeDeleteFn<T>] {
296 &self.hooks.before_delete
297 }
298 pub(crate) fn after_insert_hooks(&self) -> &[AfterFn<T>] {
299 &self.hooks.after_insert
300 }
301 pub(crate) fn after_update_hooks(&self) -> &[AfterFn<T>] {
302 &self.hooks.after_update
303 }
304 pub(crate) fn after_delete_hooks(&self) -> &[AfterFn<T>] {
305 &self.hooks.after_delete
306 }
307 pub(crate) fn before_delete_all_hooks(&self) -> &[DeleteAllFn<T>] {
308 &self.hooks.before_delete_all
309 }
310 pub(crate) fn after_delete_all_hooks(&self) -> &[DeleteAllFn<T>] {
311 &self.hooks.after_delete_all
312 }
313
314 pub fn add_hook(&mut self, hook: Hook<T>) {
318 let hooks = &mut self.hooks;
319 match hook {
320 Hook::BeforeSelect(f) => hooks.before_select.push(f),
321 Hook::BeforeInsert(phase, f) => push_phased(&mut hooks.before_insert, phase, f),
322 Hook::BeforeUpdate(phase, f) => push_phased(&mut hooks.before_update, phase, f),
323 Hook::BeforeSave(phase, f) => {
324 push_phased(&mut hooks.before_insert, phase, f.clone());
325 push_phased(&mut hooks.before_update, phase, f);
326 }
327 Hook::BeforeDelete(f) => hooks.before_delete.push(f),
328 Hook::AfterInsert(f) => hooks.after_insert.push(f),
329 Hook::AfterUpdate(f) => hooks.after_update.push(f),
330 Hook::AfterSave(f) => {
331 hooks.after_insert.push(f.clone());
332 hooks.after_update.push(f);
333 }
334 Hook::AfterDelete(f) => hooks.after_delete.push(f),
335 Hook::BeforeDeleteAll(f) => hooks.before_delete_all.push(f),
336 Hook::AfterDeleteAll(f) => hooks.after_delete_all.push(f),
337 }
338 }
339
340 pub fn with_hook(mut self, hook: Hook<T>) -> Self {
342 self.add_hook(hook);
343 self
344 }
345
346 pub async fn check_read(&self) -> vantage_core::Result<()> {
351 if self.hooks.before_select.is_empty() {
352 return Ok(());
353 }
354 let context = WriteContext::current();
355 let table = self.as_entity_erased();
356 for hook in &self.hooks.before_select {
357 hook(SelectCx {
358 context: context.as_ref(),
359 table,
360 })
361 .await?;
362 }
363 Ok(())
364 }
365}
366
367fn push_phased<T: TableSource>(
370 hooks: &mut Vec<(Phase, BeforeFn<T>)>,
371 phase: Phase,
372 f: BeforeFn<T>,
373) {
374 hooks.push((phase, f));
375 hooks.sort_by_key(|(p, _)| *p);
376}