Skip to main content

rustauth_core/options/
user.rs

1use std::collections::BTreeMap;
2use std::fmt;
3use std::sync::Arc;
4
5use http::Request;
6use time::Duration;
7
8use super::model_schema::ModelSchemaOptions;
9use crate::db::{DbFieldType, DbValue, User};
10use crate::error::RustAuthError;
11
12/// User lifecycle configuration.
13#[derive(Debug, Clone, Default)]
14pub struct UserOptions {
15    pub schema: ModelSchemaOptions,
16    pub change_email: ChangeEmailOptions,
17    pub delete_user: DeleteUserOptions,
18    pub additional_fields: BTreeMap<String, UserAdditionalField>,
19}
20
21impl UserOptions {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn builder() -> Self {
27        Self::new()
28    }
29
30    #[must_use]
31    pub fn schema(mut self, schema: ModelSchemaOptions) -> Self {
32        self.schema = schema;
33        self
34    }
35
36    #[must_use]
37    pub fn change_email(mut self, change_email: ChangeEmailOptions) -> Self {
38        self.change_email = change_email;
39        self
40    }
41
42    #[must_use]
43    pub fn delete_user(mut self, delete_user: DeleteUserOptions) -> Self {
44        self.delete_user = delete_user;
45        self
46    }
47
48    #[must_use]
49    pub fn additional_field(mut self, name: impl Into<String>, field: UserAdditionalField) -> Self {
50        self.additional_fields.insert(name.into(), field);
51        self
52    }
53}
54
55/// Runtime metadata for custom user fields accepted by user-writing endpoints.
56#[derive(Debug, Clone, PartialEq)]
57pub struct UserAdditionalField {
58    pub field_type: DbFieldType,
59    pub required: bool,
60    pub input: bool,
61    pub returned: bool,
62    pub default_value: Option<DbValue>,
63    pub db_name: Option<String>,
64}
65
66impl UserAdditionalField {
67    pub fn new(field_type: DbFieldType) -> Self {
68        Self {
69            field_type,
70            required: true,
71            input: true,
72            returned: true,
73            default_value: None,
74            db_name: None,
75        }
76    }
77
78    #[must_use]
79    pub fn optional(mut self) -> Self {
80        self.required = false;
81        self
82    }
83
84    #[must_use]
85    pub fn generated(mut self) -> Self {
86        self.input = false;
87        self
88    }
89
90    #[must_use]
91    pub fn hidden(mut self) -> Self {
92        self.returned = false;
93        self
94    }
95
96    #[must_use]
97    pub fn default_value(mut self, value: DbValue) -> Self {
98        self.default_value = Some(value);
99        self
100    }
101
102    #[must_use]
103    pub fn db_name(mut self, db_name: impl Into<String>) -> Self {
104        self.db_name = Some(db_name.into());
105        self
106    }
107}
108
109/// Email change behavior.
110#[derive(Clone, Default)]
111pub struct ChangeEmailOptions {
112    pub enabled: bool,
113    pub update_email_without_verification: bool,
114    pub send_change_email_confirmation: Option<Arc<dyn SendChangeEmailConfirmation>>,
115}
116
117impl fmt::Debug for ChangeEmailOptions {
118    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119        formatter
120            .debug_struct("ChangeEmailOptions")
121            .field("enabled", &self.enabled)
122            .field(
123                "update_email_without_verification",
124                &self.update_email_without_verification,
125            )
126            .field(
127                "send_change_email_confirmation",
128                &self
129                    .send_change_email_confirmation
130                    .as_ref()
131                    .map(|_| "<send-change-email-confirmation>"),
132            )
133            .finish()
134    }
135}
136
137impl ChangeEmailOptions {
138    pub fn new() -> Self {
139        Self::default()
140    }
141
142    pub fn builder() -> Self {
143        Self::new()
144    }
145
146    #[must_use]
147    pub fn enabled(mut self, enabled: bool) -> Self {
148        self.enabled = enabled;
149        self
150    }
151
152    #[must_use]
153    pub fn update_email_without_verification(mut self, enabled: bool) -> Self {
154        self.update_email_without_verification = enabled;
155        self
156    }
157
158    #[must_use]
159    pub fn send_change_email_confirmation<S>(mut self, sender: S) -> Self
160    where
161        S: SendChangeEmailConfirmation,
162    {
163        self.send_change_email_confirmation = Some(Arc::new(sender));
164        self
165    }
166}
167
168/// Payload for notifying the current email address about a pending change.
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct ChangeEmailConfirmation {
171    pub user: User,
172    pub new_email: String,
173    pub url: String,
174    pub token: String,
175}
176
177/// Notifies the user's current email that a change was requested.
178pub trait SendChangeEmailConfirmation: Send + Sync + 'static {
179    fn send_change_email_confirmation(
180        &self,
181        payload: ChangeEmailConfirmation,
182        request: Option<&Request<Vec<u8>>>,
183    ) -> Result<(), RustAuthError>;
184}
185
186impl<F> SendChangeEmailConfirmation for F
187where
188    F: for<'a> Fn(
189            ChangeEmailConfirmation,
190            Option<&'a Request<Vec<u8>>>,
191        ) -> Result<(), RustAuthError>
192        + Send
193        + Sync
194        + 'static,
195{
196    fn send_change_email_confirmation(
197        &self,
198        payload: ChangeEmailConfirmation,
199        request: Option<&Request<Vec<u8>>>,
200    ) -> Result<(), RustAuthError> {
201        self(payload, request)
202    }
203}
204
205/// User deletion behavior.
206#[derive(Clone, Default)]
207pub struct DeleteUserOptions {
208    pub enabled: bool,
209    pub send_delete_account_verification: Option<Arc<dyn SendDeleteAccountVerification>>,
210    pub before_delete: Option<Arc<dyn BeforeDeleteUser>>,
211    pub after_delete: Option<Arc<dyn AfterDeleteUser>>,
212    pub delete_token_expires_in: Option<Duration>,
213}
214
215impl fmt::Debug for DeleteUserOptions {
216    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
217        formatter
218            .debug_struct("DeleteUserOptions")
219            .field("enabled", &self.enabled)
220            .field(
221                "send_delete_account_verification",
222                &self
223                    .send_delete_account_verification
224                    .as_ref()
225                    .map(|_| "<send-delete-account-verification>"),
226            )
227            .field(
228                "before_delete",
229                &self.before_delete.as_ref().map(|_| "<before-delete>"),
230            )
231            .field(
232                "after_delete",
233                &self.after_delete.as_ref().map(|_| "<after-delete>"),
234            )
235            .field("delete_token_expires_in", &self.delete_token_expires_in)
236            .finish()
237    }
238}
239
240impl DeleteUserOptions {
241    pub fn new() -> Self {
242        Self::default()
243    }
244
245    pub fn builder() -> Self {
246        Self::new()
247    }
248
249    #[must_use]
250    pub fn enabled(mut self, enabled: bool) -> Self {
251        self.enabled = enabled;
252        self
253    }
254
255    #[must_use]
256    pub fn send_delete_account_verification<S>(mut self, sender: S) -> Self
257    where
258        S: SendDeleteAccountVerification,
259    {
260        self.send_delete_account_verification = Some(Arc::new(sender));
261        self
262    }
263
264    #[must_use]
265    pub fn before_delete<B>(mut self, hook: B) -> Self
266    where
267        B: BeforeDeleteUser,
268    {
269        self.before_delete = Some(Arc::new(hook));
270        self
271    }
272
273    #[must_use]
274    pub fn after_delete<A>(mut self, hook: A) -> Self
275    where
276        A: AfterDeleteUser,
277    {
278        self.after_delete = Some(Arc::new(hook));
279        self
280    }
281
282    #[must_use]
283    pub fn delete_token_expires_in(mut self, expires_in: Duration) -> Self {
284        self.delete_token_expires_in = Some(expires_in);
285        self
286    }
287}
288
289/// Payload for delete-account verification emails.
290#[derive(Debug, Clone, PartialEq, Eq)]
291pub struct DeleteAccountVerificationEmail {
292    pub user: User,
293    pub url: String,
294    pub token: String,
295}
296
297/// Sends a verification email before deleting the account.
298pub trait SendDeleteAccountVerification: Send + Sync + 'static {
299    fn send_delete_account_verification(
300        &self,
301        payload: DeleteAccountVerificationEmail,
302        request: Option<&Request<Vec<u8>>>,
303    ) -> Result<(), RustAuthError>;
304}
305
306impl<F> SendDeleteAccountVerification for F
307where
308    F: for<'a> Fn(
309            DeleteAccountVerificationEmail,
310            Option<&'a Request<Vec<u8>>>,
311        ) -> Result<(), RustAuthError>
312        + Send
313        + Sync
314        + 'static,
315{
316    fn send_delete_account_verification(
317        &self,
318        payload: DeleteAccountVerificationEmail,
319        request: Option<&Request<Vec<u8>>>,
320    ) -> Result<(), RustAuthError> {
321        self(payload, request)
322    }
323}
324
325/// Hook invoked before a user is deleted.
326pub trait BeforeDeleteUser: Send + Sync + 'static {
327    fn before_delete(
328        &self,
329        user: &User,
330        request: Option<&Request<Vec<u8>>>,
331    ) -> Result<(), RustAuthError>;
332}
333
334impl<F> BeforeDeleteUser for F
335where
336    F: for<'a> Fn(&User, Option<&'a Request<Vec<u8>>>) -> Result<(), RustAuthError>
337        + Send
338        + Sync
339        + 'static,
340{
341    fn before_delete(
342        &self,
343        user: &User,
344        request: Option<&Request<Vec<u8>>>,
345    ) -> Result<(), RustAuthError> {
346        self(user, request)
347    }
348}
349
350/// Hook invoked after a user is deleted.
351pub trait AfterDeleteUser: Send + Sync + 'static {
352    fn after_delete(
353        &self,
354        user: &User,
355        request: Option<&Request<Vec<u8>>>,
356    ) -> Result<(), RustAuthError>;
357}
358
359impl<F> AfterDeleteUser for F
360where
361    F: for<'a> Fn(&User, Option<&'a Request<Vec<u8>>>) -> Result<(), RustAuthError>
362        + Send
363        + Sync
364        + 'static,
365{
366    fn after_delete(
367        &self,
368        user: &User,
369        request: Option<&Request<Vec<u8>>>,
370    ) -> Result<(), RustAuthError> {
371        self(user, request)
372    }
373}