1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! The [`UserStore`] trait — async storage abstraction for users.
use uuid::Uuid;
use crate::entities::{NewUser, Page, User};
use crate::store::StoreFuture;
/// Async storage abstraction for users.
///
/// All methods return a [`StoreFuture`] (boxed future) for object safety,
/// allowing the store to be used as `Arc<dyn UserStore>`.
pub trait UserStore: Send + Sync {
/// Create a new user.
///
/// If this is the first user in the store, `is_admin` is automatically
/// set to `true` regardless of the input, making them the superadmin.
///
/// # Errors
///
/// Returns [`StoreError::DuplicateEmail`] or [`StoreError::DuplicateUsername`]
/// if the email or username is already taken.
fn create_user(&self, req: NewUser) -> StoreFuture<'_, User>;
/// Find a user by email. Returns `None` if not found.
fn find_user_by_email(&self, email: &str) -> StoreFuture<'_, Option<User>>;
/// Find a user by username. Returns `None` if not found.
fn find_user_by_username(&self, username: &str) -> StoreFuture<'_, Option<User>>;
/// Find a user by ID. Returns `None` if not found.
fn find_user_by_id(&self, id: Uuid) -> StoreFuture<'_, Option<User>>;
/// Count all users in the store.
fn count_users(&self) -> StoreFuture<'_, u64>;
/// List users with pagination, ordered by `created_at` descending.
fn list_users(&self, page: u32, per_page: u32) -> StoreFuture<'_, Page<User>>;
/// Delete a user by ID.
///
/// # Errors
///
/// Returns [`StoreError::UserNotFound`] if the user does not exist.
fn delete_user(&self, id: Uuid) -> StoreFuture<'_, ()>;
/// Update a user's admin role.
///
/// # Errors
///
/// Returns [`StoreError::UserNotFound`] if the user does not exist.
fn update_user_role(&self, id: Uuid, is_admin: bool) -> StoreFuture<'_, User>;
/// Update a user's password hash.
///
/// # Errors
///
/// Returns [`StoreError::UserNotFound`] if the user does not exist.
fn update_user_password(&self, id: Uuid, password_hash: String) -> StoreFuture<'_, ()>;
/// List the groups a user belongs to, sorted by name.
///
/// Returns an empty list for an unknown user.
///
/// # Errors
///
/// Returns [`StoreError::Database`](crate::error::StoreError::Database) on
/// a storage failure.
fn list_user_groups(&self, user_id: Uuid) -> StoreFuture<'_, Vec<String>>;
/// Replace the groups a user belongs to.
///
/// Returns the new membership, sorted and deduplicated. An empty list
/// removes the user from every group.
///
/// # Errors
///
/// Returns [`StoreError::UserNotFound`](crate::error::StoreError::UserNotFound)
/// if the user does not exist.
fn set_user_groups(&self, user_id: Uuid, groups: Vec<String>) -> StoreFuture<'_, Vec<String>>;
}