1mod errors;
4mod handler;
5mod migration;
6
7use std::fmt;
8use std::sync::Arc;
9
10pub use handler::{
11 PluginDatabaseAfterHookFuture, PluginDatabaseAfterHookHandler, PluginDatabaseBeforeHookFuture,
12 PluginDatabaseBeforeHookHandler, PluginDatabaseHookContext,
13};
14pub use migration::PluginMigration;
15
16use errors::{mismatched_after_input, mismatched_before_input};
17
18use crate::db::{Create, DbRecord, Delete, DeleteMany, Update, UpdateMany};
19use crate::error::OpenAuthError;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum PluginDatabaseOperation {
24 Create,
25 Update,
26 UpdateMany,
27 Delete,
28 DeleteMany,
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub enum PluginDatabaseBeforeInput {
34 Create(Create),
35 Update(Update),
36 UpdateMany(UpdateMany),
37 Delete {
38 query: Delete,
39 snapshots: Vec<DbRecord>,
40 },
41 DeleteMany {
42 query: DeleteMany,
43 snapshots: Vec<DbRecord>,
44 },
45}
46
47impl PluginDatabaseBeforeInput {
48 pub fn operation(&self) -> PluginDatabaseOperation {
49 match self {
50 Self::Create(_) => PluginDatabaseOperation::Create,
51 Self::Update(_) => PluginDatabaseOperation::Update,
52 Self::UpdateMany(_) => PluginDatabaseOperation::UpdateMany,
53 Self::Delete { .. } => PluginDatabaseOperation::Delete,
54 Self::DeleteMany { .. } => PluginDatabaseOperation::DeleteMany,
55 }
56 }
57
58 pub fn model(&self) -> &str {
59 match self {
60 Self::Create(query) => &query.model,
61 Self::Update(query) => &query.model,
62 Self::UpdateMany(query) => &query.model,
63 Self::Delete { query, .. } => &query.model,
64 Self::DeleteMany { query, .. } => &query.model,
65 }
66 }
67}
68
69#[derive(Debug, PartialEq)]
71pub enum PluginDatabaseBeforeAction {
72 Continue(PluginDatabaseBeforeInput),
73 Cancel(OpenAuthError),
74}
75
76#[derive(Debug, Clone, PartialEq)]
78pub enum PluginDatabaseAfterInput {
79 Create {
80 query: Create,
81 result: DbRecord,
82 },
83 Update {
84 query: Update,
85 result: Option<DbRecord>,
86 },
87 UpdateMany {
88 query: UpdateMany,
89 result: u64,
90 },
91 Delete {
92 query: Delete,
93 snapshots: Vec<DbRecord>,
94 },
95 DeleteMany {
96 query: DeleteMany,
97 snapshots: Vec<DbRecord>,
98 result: u64,
99 },
100}
101
102impl PluginDatabaseAfterInput {
103 pub fn operation(&self) -> PluginDatabaseOperation {
104 match self {
105 Self::Create { .. } => PluginDatabaseOperation::Create,
106 Self::Update { .. } => PluginDatabaseOperation::Update,
107 Self::UpdateMany { .. } => PluginDatabaseOperation::UpdateMany,
108 Self::Delete { .. } => PluginDatabaseOperation::Delete,
109 Self::DeleteMany { .. } => PluginDatabaseOperation::DeleteMany,
110 }
111 }
112
113 pub fn model(&self) -> &str {
114 match self {
115 Self::Create { query, .. } => &query.model,
116 Self::Update { query, .. } => &query.model,
117 Self::UpdateMany { query, .. } => &query.model,
118 Self::Delete { query, .. } => &query.model,
119 Self::DeleteMany { query, .. } => &query.model,
120 }
121 }
122}
123
124#[derive(Clone)]
126pub struct PluginDatabaseHook {
127 pub name: String,
128 pub operation: PluginDatabaseOperation,
129 pub before: Option<PluginDatabaseBeforeHookHandler>,
130 pub after: Option<PluginDatabaseAfterHookHandler>,
131 plugin_id: Option<String>,
132}
133
134impl PluginDatabaseHook {
135 pub fn before<F>(
136 name: impl Into<String>,
137 operation: PluginDatabaseOperation,
138 handler: F,
139 ) -> Self
140 where
141 F: Fn(
142 &PluginDatabaseHookContext<'_>,
143 PluginDatabaseBeforeInput,
144 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
145 + Send
146 + Sync
147 + 'static,
148 {
149 Self {
150 name: name.into(),
151 operation,
152 before: Some(Arc::new(move |context, input| {
153 let result = handler(&context, input);
154 Box::pin(async move { result })
155 })),
156 after: None,
157 plugin_id: None,
158 }
159 }
160
161 pub fn before_async<F>(
162 name: impl Into<String>,
163 operation: PluginDatabaseOperation,
164 handler: F,
165 ) -> Self
166 where
167 F: for<'a> Fn(
168 PluginDatabaseHookContext<'a>,
169 PluginDatabaseBeforeInput,
170 ) -> PluginDatabaseBeforeHookFuture<'a>
171 + Send
172 + Sync
173 + 'static,
174 {
175 Self {
176 name: name.into(),
177 operation,
178 before: Some(Arc::new(handler)),
179 after: None,
180 plugin_id: None,
181 }
182 }
183
184 pub fn after<F>(name: impl Into<String>, operation: PluginDatabaseOperation, handler: F) -> Self
185 where
186 F: Fn(
187 &PluginDatabaseHookContext<'_>,
188 &PluginDatabaseAfterInput,
189 ) -> Result<(), OpenAuthError>
190 + Send
191 + Sync
192 + 'static,
193 {
194 Self {
195 name: name.into(),
196 operation,
197 before: None,
198 after: Some(Arc::new(move |context, input| {
199 let result = handler(&context, &input);
200 Box::pin(async move { result })
201 })),
202 plugin_id: None,
203 }
204 }
205
206 pub fn after_async<F>(
207 name: impl Into<String>,
208 operation: PluginDatabaseOperation,
209 handler: F,
210 ) -> Self
211 where
212 F: for<'a> Fn(
213 PluginDatabaseHookContext<'a>,
214 PluginDatabaseAfterInput,
215 ) -> PluginDatabaseAfterHookFuture<'a>
216 + Send
217 + Sync
218 + 'static,
219 {
220 Self {
221 name: name.into(),
222 operation,
223 before: None,
224 after: Some(Arc::new(handler)),
225 plugin_id: None,
226 }
227 }
228
229 pub fn before_create<F>(name: impl Into<String>, handler: F) -> Self
230 where
231 F: Fn(
232 &PluginDatabaseHookContext<'_>,
233 Create,
234 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
235 + Send
236 + Sync
237 + 'static,
238 {
239 Self::before(
240 name,
241 PluginDatabaseOperation::Create,
242 move |context, input| match input {
243 PluginDatabaseBeforeInput::Create(query) => handler(context, query),
244 other => mismatched_before_input(PluginDatabaseOperation::Create, other),
245 },
246 )
247 }
248
249 pub fn before_create_async<F>(name: impl Into<String>, handler: F) -> Self
250 where
251 F: for<'a> Fn(PluginDatabaseHookContext<'a>, Create) -> PluginDatabaseBeforeHookFuture<'a>
252 + Send
253 + Sync
254 + 'static,
255 {
256 Self::before_async(
257 name,
258 PluginDatabaseOperation::Create,
259 move |context, input| match input {
260 PluginDatabaseBeforeInput::Create(query) => handler(context, query),
261 other => Box::pin(async move {
262 mismatched_before_input(PluginDatabaseOperation::Create, other)
263 }),
264 },
265 )
266 }
267
268 pub fn before_update<F>(name: impl Into<String>, handler: F) -> Self
269 where
270 F: Fn(
271 &PluginDatabaseHookContext<'_>,
272 Update,
273 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
274 + Send
275 + Sync
276 + 'static,
277 {
278 Self::before(
279 name,
280 PluginDatabaseOperation::Update,
281 move |context, input| match input {
282 PluginDatabaseBeforeInput::Update(query) => handler(context, query),
283 other => mismatched_before_input(PluginDatabaseOperation::Update, other),
284 },
285 )
286 }
287
288 pub fn before_update_many<F>(name: impl Into<String>, handler: F) -> Self
289 where
290 F: Fn(
291 &PluginDatabaseHookContext<'_>,
292 UpdateMany,
293 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
294 + Send
295 + Sync
296 + 'static,
297 {
298 Self::before(
299 name,
300 PluginDatabaseOperation::UpdateMany,
301 move |context, input| match input {
302 PluginDatabaseBeforeInput::UpdateMany(query) => handler(context, query),
303 other => mismatched_before_input(PluginDatabaseOperation::UpdateMany, other),
304 },
305 )
306 }
307
308 pub fn before_delete<F>(name: impl Into<String>, handler: F) -> Self
309 where
310 F: Fn(
311 &PluginDatabaseHookContext<'_>,
312 Delete,
313 Vec<DbRecord>,
314 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
315 + Send
316 + Sync
317 + 'static,
318 {
319 Self::before(
320 name,
321 PluginDatabaseOperation::Delete,
322 move |context, input| match input {
323 PluginDatabaseBeforeInput::Delete { query, snapshots } => {
324 handler(context, query, snapshots)
325 }
326 other => mismatched_before_input(PluginDatabaseOperation::Delete, other),
327 },
328 )
329 }
330
331 pub fn before_delete_many<F>(name: impl Into<String>, handler: F) -> Self
332 where
333 F: Fn(
334 &PluginDatabaseHookContext<'_>,
335 DeleteMany,
336 Vec<DbRecord>,
337 ) -> Result<PluginDatabaseBeforeAction, OpenAuthError>
338 + Send
339 + Sync
340 + 'static,
341 {
342 Self::before(
343 name,
344 PluginDatabaseOperation::DeleteMany,
345 move |context, input| match input {
346 PluginDatabaseBeforeInput::DeleteMany { query, snapshots } => {
347 handler(context, query, snapshots)
348 }
349 other => mismatched_before_input(PluginDatabaseOperation::DeleteMany, other),
350 },
351 )
352 }
353
354 pub fn after_create<F>(name: impl Into<String>, handler: F) -> Self
355 where
356 F: Fn(&PluginDatabaseHookContext<'_>, &Create, &DbRecord) -> Result<(), OpenAuthError>
357 + Send
358 + Sync
359 + 'static,
360 {
361 Self::after(
362 name,
363 PluginDatabaseOperation::Create,
364 move |context, input| match input {
365 PluginDatabaseAfterInput::Create { query, result } => {
366 handler(context, query, result)
367 }
368 other => mismatched_after_input(PluginDatabaseOperation::Create, other),
369 },
370 )
371 }
372
373 pub fn after_update<F>(name: impl Into<String>, handler: F) -> Self
374 where
375 F: Fn(
376 &PluginDatabaseHookContext<'_>,
377 &Update,
378 &Option<DbRecord>,
379 ) -> Result<(), OpenAuthError>
380 + Send
381 + Sync
382 + 'static,
383 {
384 Self::after(
385 name,
386 PluginDatabaseOperation::Update,
387 move |context, input| match input {
388 PluginDatabaseAfterInput::Update { query, result } => {
389 handler(context, query, result)
390 }
391 other => mismatched_after_input(PluginDatabaseOperation::Update, other),
392 },
393 )
394 }
395
396 pub fn after_update_many<F>(name: impl Into<String>, handler: F) -> Self
397 where
398 F: Fn(&PluginDatabaseHookContext<'_>, &UpdateMany, u64) -> Result<(), OpenAuthError>
399 + Send
400 + Sync
401 + 'static,
402 {
403 Self::after(
404 name,
405 PluginDatabaseOperation::UpdateMany,
406 move |context, input| match input {
407 PluginDatabaseAfterInput::UpdateMany { query, result } => {
408 handler(context, query, *result)
409 }
410 other => mismatched_after_input(PluginDatabaseOperation::UpdateMany, other),
411 },
412 )
413 }
414
415 pub fn after_delete<F>(name: impl Into<String>, handler: F) -> Self
416 where
417 F: Fn(&PluginDatabaseHookContext<'_>, &Delete, &[DbRecord]) -> Result<(), OpenAuthError>
418 + Send
419 + Sync
420 + 'static,
421 {
422 Self::after(
423 name,
424 PluginDatabaseOperation::Delete,
425 move |context, input| match input {
426 PluginDatabaseAfterInput::Delete { query, snapshots } => {
427 handler(context, query, snapshots)
428 }
429 other => mismatched_after_input(PluginDatabaseOperation::Delete, other),
430 },
431 )
432 }
433
434 pub fn after_delete_many<F>(name: impl Into<String>, handler: F) -> Self
435 where
436 F: Fn(
437 &PluginDatabaseHookContext<'_>,
438 &DeleteMany,
439 &[DbRecord],
440 u64,
441 ) -> Result<(), OpenAuthError>
442 + Send
443 + Sync
444 + 'static,
445 {
446 Self::after(
447 name,
448 PluginDatabaseOperation::DeleteMany,
449 move |context, input| match input {
450 PluginDatabaseAfterInput::DeleteMany {
451 query,
452 snapshots,
453 result,
454 } => handler(context, query, snapshots, *result),
455 other => mismatched_after_input(PluginDatabaseOperation::DeleteMany, other),
456 },
457 )
458 }
459
460 pub fn plugin_id(&self) -> Option<&str> {
461 self.plugin_id.as_deref()
462 }
463
464 pub fn with_plugin_id(mut self, plugin_id: impl Into<String>) -> Self {
465 self.plugin_id = Some(plugin_id.into());
466 self
467 }
468
469 pub fn has_overlapping_phase(&self, other: &Self) -> bool {
470 self.name == other.name
471 && self.operation == other.operation
472 && ((self.before.is_some() && other.before.is_some())
473 || (self.after.is_some() && other.after.is_some()))
474 }
475}
476
477impl fmt::Debug for PluginDatabaseHook {
478 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
479 formatter
480 .debug_struct("PluginDatabaseHook")
481 .field("name", &self.name)
482 .field("operation", &self.operation)
483 .field("before", &self.before.as_ref().map(|_| "<before>"))
484 .field("after", &self.after.as_ref().map(|_| "<after>"))
485 .field("plugin_id", &self.plugin_id)
486 .finish()
487 }
488}