1use std::fmt;
4use std::sync::Arc;
5
6use crate::db::DbRecord;
7use crate::error::RustAuthError;
8use crate::plugin::PluginDatabaseHookContext;
9
10#[derive(Debug, PartialEq)]
12pub enum InitDatabaseBeforeAction {
13 Continue,
14 Cancel(RustAuthError),
15 Replace(DbRecord),
16}
17
18pub trait InitDatabaseBeforeHook: Send + Sync + 'static {
20 fn before(
21 &self,
22 context: &PluginDatabaseHookContext<'_>,
23 record: &mut DbRecord,
24 ) -> Result<InitDatabaseBeforeAction, RustAuthError>;
25}
26
27impl<F> InitDatabaseBeforeHook for F
28where
29 F: Fn(
30 &PluginDatabaseHookContext<'_>,
31 &mut DbRecord,
32 ) -> Result<InitDatabaseBeforeAction, RustAuthError>
33 + Send
34 + Sync
35 + 'static,
36{
37 fn before(
38 &self,
39 context: &PluginDatabaseHookContext<'_>,
40 record: &mut DbRecord,
41 ) -> Result<InitDatabaseBeforeAction, RustAuthError> {
42 self(context, record)
43 }
44}
45
46pub trait InitDatabaseAfterHook: Send + Sync + 'static {
48 fn after(
49 &self,
50 context: &PluginDatabaseHookContext<'_>,
51 record: &DbRecord,
52 ) -> Result<(), RustAuthError>;
53}
54
55impl<F> InitDatabaseAfterHook for F
56where
57 F: Fn(&PluginDatabaseHookContext<'_>, &DbRecord) -> Result<(), RustAuthError>
58 + Send
59 + Sync
60 + 'static,
61{
62 fn after(
63 &self,
64 context: &PluginDatabaseHookContext<'_>,
65 record: &DbRecord,
66 ) -> Result<(), RustAuthError> {
67 self(context, record)
68 }
69}
70
71#[derive(Clone, Default)]
73pub struct DatabaseOperationHooks {
74 pub before: Option<Arc<dyn InitDatabaseBeforeHook>>,
75 pub after: Option<Arc<dyn InitDatabaseAfterHook>>,
76}
77
78impl fmt::Debug for DatabaseOperationHooks {
79 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80 formatter
81 .debug_struct("DatabaseOperationHooks")
82 .field(
83 "before",
84 &self.before.as_ref().map(|_| "<init-database-before>"),
85 )
86 .field(
87 "after",
88 &self.after.as_ref().map(|_| "<init-database-after>"),
89 )
90 .finish()
91 }
92}
93
94impl DatabaseOperationHooks {
95 pub fn new() -> Self {
96 Self::default()
97 }
98
99 #[must_use]
100 pub fn before<H>(mut self, hook: H) -> Self
101 where
102 H: InitDatabaseBeforeHook,
103 {
104 self.before = Some(Arc::new(hook));
105 self
106 }
107
108 #[must_use]
109 pub fn after<H>(mut self, hook: H) -> Self
110 where
111 H: InitDatabaseAfterHook,
112 {
113 self.after = Some(Arc::new(hook));
114 self
115 }
116}
117
118#[derive(Clone, Debug, Default)]
120pub struct DatabaseModelHooks {
121 pub create: DatabaseOperationHooks,
122 pub update: DatabaseOperationHooks,
123}
124
125impl DatabaseModelHooks {
126 pub fn new() -> Self {
127 Self::default()
128 }
129}
130
131#[derive(Clone, Debug, Default)]
133pub struct InitDatabaseHooksOptions {
134 pub user: DatabaseModelHooks,
135 pub session: DatabaseModelHooks,
136 pub account: DatabaseModelHooks,
137 pub verification: DatabaseModelHooks,
138}
139
140impl InitDatabaseHooksOptions {
141 pub fn new() -> Self {
142 Self::default()
143 }
144}
145
146pub fn plugin_database_hooks_from_init(
147 options: &InitDatabaseHooksOptions,
148) -> Vec<crate::plugin::PluginDatabaseHook> {
149 let mut hooks = Vec::new();
150 append_model_hooks(&mut hooks, "user", &options.user);
151 append_model_hooks(&mut hooks, "session", &options.session);
152 append_model_hooks(&mut hooks, "account", &options.account);
153 append_model_hooks(&mut hooks, "verification", &options.verification);
154 hooks
155}
156
157fn append_model_hooks(
158 hooks: &mut Vec<crate::plugin::PluginDatabaseHook>,
159 model: &str,
160 model_hooks: &DatabaseModelHooks,
161) {
162 if let Some(before) = model_hooks.create.before.clone() {
163 append_create_before(hooks, model, before);
164 }
165 if let Some(after) = model_hooks.create.after.clone() {
166 append_create_after(hooks, model, after);
167 }
168 if let Some(before) = model_hooks.update.before.clone() {
169 append_update_before(hooks, model, before);
170 }
171 if let Some(after) = model_hooks.update.after.clone() {
172 append_update_after(hooks, model, after);
173 }
174}
175
176fn append_create_before(
177 hooks: &mut Vec<crate::plugin::PluginDatabaseHook>,
178 model: &str,
179 hook: Arc<dyn InitDatabaseBeforeHook>,
180) {
181 use crate::plugin::{
182 PluginDatabaseBeforeAction, PluginDatabaseBeforeInput, PluginDatabaseHook,
183 };
184
185 let model = model.to_owned();
186 hooks.push(PluginDatabaseHook::before_create(
187 format!("{model}-create-before"),
188 move |context, query| {
189 if query.model != model {
190 return Ok(PluginDatabaseBeforeAction::Continue(
191 PluginDatabaseBeforeInput::Create(query),
192 ));
193 }
194 let mut query = query;
195 match hook.before(context, &mut query.data)? {
196 InitDatabaseBeforeAction::Continue => Ok(PluginDatabaseBeforeAction::Continue(
197 PluginDatabaseBeforeInput::Create(query),
198 )),
199 InitDatabaseBeforeAction::Cancel(error) => {
200 Ok(PluginDatabaseBeforeAction::Cancel(error))
201 }
202 InitDatabaseBeforeAction::Replace(record) => {
203 query.data = record;
204 Ok(PluginDatabaseBeforeAction::Continue(
205 PluginDatabaseBeforeInput::Create(query),
206 ))
207 }
208 }
209 },
210 ));
211}
212
213fn append_create_after(
214 hooks: &mut Vec<crate::plugin::PluginDatabaseHook>,
215 model: &str,
216 hook: Arc<dyn InitDatabaseAfterHook>,
217) {
218 use crate::plugin::PluginDatabaseHook;
219
220 let model = model.to_owned();
221 hooks.push(PluginDatabaseHook::after_create(
222 format!("{model}-create-after"),
223 move |context, query, result| {
224 if query.model != model {
225 return Ok(());
226 }
227 hook.after(context, result)
228 },
229 ));
230}
231
232fn append_update_before(
233 hooks: &mut Vec<crate::plugin::PluginDatabaseHook>,
234 model: &str,
235 hook: Arc<dyn InitDatabaseBeforeHook>,
236) {
237 use crate::plugin::{
238 PluginDatabaseBeforeAction, PluginDatabaseBeforeInput, PluginDatabaseHook,
239 };
240
241 let model = model.to_owned();
242 hooks.push(PluginDatabaseHook::before_update(
243 format!("{model}-update-before"),
244 move |context, query| {
245 if query.model != model {
246 return Ok(PluginDatabaseBeforeAction::Continue(
247 PluginDatabaseBeforeInput::Update(query),
248 ));
249 }
250 let mut query = query;
251 match hook.before(context, &mut query.data)? {
252 InitDatabaseBeforeAction::Continue => Ok(PluginDatabaseBeforeAction::Continue(
253 PluginDatabaseBeforeInput::Update(query),
254 )),
255 InitDatabaseBeforeAction::Cancel(error) => {
256 Ok(PluginDatabaseBeforeAction::Cancel(error))
257 }
258 InitDatabaseBeforeAction::Replace(record) => {
259 query.data = record;
260 Ok(PluginDatabaseBeforeAction::Continue(
261 PluginDatabaseBeforeInput::Update(query),
262 ))
263 }
264 }
265 },
266 ));
267}
268
269fn append_update_after(
270 hooks: &mut Vec<crate::plugin::PluginDatabaseHook>,
271 model: &str,
272 hook: Arc<dyn InitDatabaseAfterHook>,
273) {
274 use crate::plugin::PluginDatabaseHook;
275
276 let model = model.to_owned();
277 hooks.push(PluginDatabaseHook::after_update(
278 format!("{model}-update-after"),
279 move |context, query, result| {
280 if query.model != model {
281 return Ok(());
282 }
283 if let Some(record) = result {
284 hook.after(context, record)?;
285 }
286 Ok(())
287 },
288 ));
289}