1use crate::{
16 Error,
17 error::ValidationError,
18 id::{generate_prefixed_id, validate_prefixed_id},
19 storage::NewUser,
20};
21use async_trait::async_trait;
22use chrono::{DateTime, Utc};
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
28pub struct UserId(String);
29
30impl UserId {
31 pub fn new(id: &str) -> Self {
32 UserId(id.to_string())
33 }
34
35 pub fn new_random() -> Self {
36 UserId(generate_prefixed_id("usr"))
37 }
38
39 pub fn into_inner(self) -> String {
40 self.0
41 }
42
43 pub fn as_str(&self) -> &str {
44 &self.0
45 }
46
47 pub fn is_valid(&self) -> bool {
49 validate_prefixed_id(&self.0, "usr")
50 }
51}
52
53impl Default for UserId {
54 fn default() -> Self {
55 Self::new_random()
56 }
57}
58
59impl From<String> for UserId {
60 fn from(s: String) -> Self {
61 Self(s)
62 }
63}
64
65impl From<&str> for UserId {
66 fn from(s: &str) -> Self {
67 Self(s.to_string())
68 }
69}
70
71impl std::fmt::Display for UserId {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 write!(f, "{}", self.0)
74 }
75}
76
77#[async_trait]
82pub trait UserManager: Send + Sync + 'static {
83 async fn create_user(&self, user: &NewUser) -> Result<User, Error>;
85
86 async fn get_user(&self, id: &UserId) -> Result<Option<User>, Error>;
88
89 async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, Error>;
91
92 async fn get_or_create_user_by_email(&self, email: &str) -> Result<User, Error>;
94
95 async fn update_user(&self, user: &User) -> Result<User, Error>;
97
98 async fn delete_user(&self, id: &UserId) -> Result<(), Error>;
100
101 async fn set_user_email_verified(&self, user_id: &UserId) -> Result<(), Error>;
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct User {
111 pub id: UserId,
113
114 pub name: Option<String>,
116
117 pub email: String,
119
120 pub email_verified_at: Option<DateTime<Utc>>,
122
123 pub created_at: DateTime<Utc>,
125
126 pub updated_at: DateTime<Utc>,
128}
129
130impl User {
131 pub fn builder() -> UserBuilder {
132 UserBuilder::default()
133 }
134
135 pub fn is_email_verified(&self) -> bool {
137 self.email_verified_at.is_some()
138 }
139}
140
141#[derive(Default)]
142pub struct UserBuilder {
143 id: Option<UserId>,
144 name: Option<String>,
145 email: Option<String>,
146 email_verified_at: Option<DateTime<Utc>>,
147 created_at: Option<DateTime<Utc>>,
148 updated_at: Option<DateTime<Utc>>,
149}
150
151impl UserBuilder {
152 pub fn id(mut self, id: UserId) -> Self {
153 self.id = Some(id);
154 self
155 }
156
157 pub fn name(mut self, name: Option<String>) -> Self {
158 self.name = name;
159 self
160 }
161
162 pub fn email(mut self, email: String) -> Self {
163 self.email = Some(email);
164 self
165 }
166
167 pub fn email_verified_at(mut self, email_verified_at: Option<DateTime<Utc>>) -> Self {
168 self.email_verified_at = email_verified_at;
169 self
170 }
171
172 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
173 self.created_at = Some(created_at);
174 self
175 }
176
177 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
178 self.updated_at = Some(updated_at);
179 self
180 }
181
182 pub fn build(self) -> Result<User, Error> {
183 let now = Utc::now();
184 Ok(User {
185 id: self.id.unwrap_or_default(),
186 name: self.name,
187 email: self.email.ok_or(ValidationError::InvalidField(
188 "Email is required".to_string(),
189 ))?,
190 email_verified_at: self.email_verified_at,
191 created_at: self.created_at.unwrap_or(now),
192 updated_at: self.updated_at.unwrap_or(now),
193 })
194 }
195}
196
197pub struct DefaultUserManager<S>
202where
203 S: crate::storage::UserStorage,
204{
205 storage: std::sync::Arc<S>,
206}
207
208impl<S> DefaultUserManager<S>
209where
210 S: crate::storage::UserStorage,
211{
212 pub fn new(storage: std::sync::Arc<S>) -> Self {
214 Self { storage }
215 }
216}
217
218#[async_trait]
219impl<S> UserManager for DefaultUserManager<S>
220where
221 S: crate::storage::UserStorage,
222{
223 async fn create_user(&self, user: &NewUser) -> Result<User, Error> {
224 self.storage
225 .create_user(user)
226 .await
227 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
228 }
229
230 async fn get_user(&self, id: &UserId) -> Result<Option<User>, Error> {
231 self.storage
232 .get_user(id)
233 .await
234 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
235 }
236
237 async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, Error> {
238 self.storage
239 .get_user_by_email(email)
240 .await
241 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
242 }
243
244 async fn get_or_create_user_by_email(&self, email: &str) -> Result<User, Error> {
245 self.storage
246 .get_or_create_user_by_email(email)
247 .await
248 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
249 }
250
251 async fn update_user(&self, user: &User) -> Result<User, Error> {
252 self.storage
253 .update_user(user)
254 .await
255 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
256 }
257
258 async fn delete_user(&self, id: &UserId) -> Result<(), Error> {
259 self.storage
260 .delete_user(id)
261 .await
262 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
263 }
264
265 async fn set_user_email_verified(&self, user_id: &UserId) -> Result<(), Error> {
266 self.storage
267 .set_user_email_verified(user_id)
268 .await
269 .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
270 }
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct OAuthAccount {
275 pub user_id: UserId,
276 pub provider: String,
277 pub subject: String,
278 pub created_at: DateTime<Utc>,
279 pub updated_at: DateTime<Utc>,
280}
281
282impl OAuthAccount {
283 pub fn builder() -> OAuthAccountBuilder {
284 OAuthAccountBuilder::default()
285 }
286}
287
288#[derive(Default)]
289pub struct OAuthAccountBuilder {
290 user_id: Option<UserId>,
291 provider: Option<String>,
292 subject: Option<String>,
293 created_at: Option<DateTime<Utc>>,
294 updated_at: Option<DateTime<Utc>>,
295}
296
297impl OAuthAccountBuilder {
298 pub fn user_id(mut self, user_id: UserId) -> Self {
299 self.user_id = Some(user_id);
300 self
301 }
302
303 pub fn provider(mut self, provider: String) -> Self {
304 self.provider = Some(provider);
305 self
306 }
307
308 pub fn subject(mut self, subject: String) -> Self {
309 self.subject = Some(subject);
310 self
311 }
312
313 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
314 self.created_at = Some(created_at);
315 self
316 }
317
318 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
319 self.updated_at = Some(updated_at);
320 self
321 }
322
323 pub fn build(self) -> Result<OAuthAccount, Error> {
324 let now = Utc::now();
325 Ok(OAuthAccount {
326 user_id: self.user_id.ok_or(ValidationError::MissingField(
327 "User ID is required".to_string(),
328 ))?,
329 provider: self.provider.ok_or(ValidationError::MissingField(
330 "Provider is required".to_string(),
331 ))?,
332 subject: self.subject.ok_or(ValidationError::MissingField(
333 "Subject is required".to_string(),
334 ))?,
335 created_at: self.created_at.unwrap_or(now),
336 updated_at: self.updated_at.unwrap_or(now),
337 })
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_user_id() {
347 let user_id = UserId::new("test");
348 assert_eq!(user_id.as_str(), "test");
349
350 let user_id_from_str = UserId::from(user_id.as_str());
351 assert_eq!(user_id_from_str, user_id);
352
353 let user_id_random = UserId::new_random();
354 assert_ne!(user_id_random, user_id);
355 }
356
357 #[test]
358 fn test_user_id_prefixed() {
359 let user_id = UserId::new_random();
360 assert!(user_id.as_str().starts_with("usr_"));
361 assert!(user_id.is_valid());
362
363 let user_id2 = UserId::new_random();
365 assert_ne!(user_id, user_id2);
366
367 let invalid_id = UserId::new("invalid");
369 assert!(!invalid_id.is_valid());
370
371 let valid_id = UserId::new("usr_dGVzdA"); assert!(!valid_id.is_valid()); }
375}