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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crateResult;
use crate::;
use Future;
/// Repository abstraction for persisting and retrieving [`Account`] entities.
///
/// This trait is implemented by storage backends (e.g. in‑memory, SurrealDB, SeaORM).
/// It deliberately uses an `Option<Account<..>>` in results for operations where
/// absence is a normal outcome (delete / update / query) so callers can
/// distinguish “not found” from actual errors (`Result::Err`).
///
/// # Semantics
///
/// | Method | Success (`Ok`) Return Value | Typical `None` Meaning | Error (`Err`) Meaning |
/// |-----------------------|----------------------------------------------------|-------------------------------------------|--------------------------------------------------|
/// | `store_account` | `Some(Account)` if stored | `None` only if backend chooses (rare) | Persistence / connectivity / constraint failure |
/// | `delete_account` | `Some(Account)` = deleted & returned | `None` = no account with that user id | Backend / IO failure |
/// | `update_account` | `Some(Account)` = updated | `None` = no existing account to update | Backend / IO / optimistic concurrency failure |
/// | `query_account_by_user_id` | `Some(Account)` = found | `None` = not found | Backend / IO failure |
///
/// Backends SHOULD:
/// - Treat `user_id` as a logical unique key
/// - Enforce uniqueness at storage level where possible
/// - Return **identical timing characteristics** for “found” vs “not found” where feasible
/// (helps upstream login logic resist user enumeration timing attacks)
///
/// # Concurrency & Consistency
///
/// This trait does not prescribe isolation semantics. Implementations should document:
/// - Whether updates are last‑write‑wins
/// - Whether optimistic locking / versioning is applied
///
/// # Example (generic usage)
/// ```rust
///
/// use axum_gate::accounts::Account;
/// use axum_gate::prelude::{Role, Group};
/// use axum_gate::accounts::AccountRepository;
/// use axum_gate::repositories::memory::MemoryAccountRepository;
///
/// # #[tokio::test]
/// async fn load_or_create(
/// repo: &MemoryAccountRepository<Role, Group>,
/// template: Account<Role, Group>
/// ) -> axum_gate::errors::Result<Account<Role, Group>> {
/// if let Some(existing) = repo.query_account_by_user_id(&template.user_id).await? {
/// Ok(existing)
/// } else {
/// Ok(repo.store_account(template).await?.expect("store returned None"))
/// }
/// }
/// ```
///
/// # Error Handling
///
/// Return `Err` only for exceptional backend failures (connectivity, serialization,
/// constraint violation, etc.). Use `Ok(None)` for “not found” / “no-op” outcomes.
///
/// # Extensibility
///
/// If you add methods (e.g. pagination, search), prefer separate traits to avoid forcing
/// all backends to implement optional features.