1pub mod utilities;
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum Error {
7 #[error("Authentication error: {0}")]
8 Auth(#[from] AuthError),
9
10 #[error("Storage error: {0}")]
11 Storage(#[from] StorageError),
12
13 #[error("Validation error: {0}")]
14 Validation(#[from] ValidationError),
15
16 #[error("Event error: {0}")]
17 Event(#[from] EventError),
18
19 #[error("Session error: {0}")]
20 Session(#[from] SessionError),
21
22 #[error("Cryptographic error: {0}")]
23 Crypto(#[from] CryptoError),
24}
25
26#[derive(Debug, Error)]
27pub enum AuthError {
28 #[error("Invalid credentials")]
29 InvalidCredentials,
30
31 #[error("User not found")]
32 UserNotFound,
33
34 #[error("User already exists")]
35 UserAlreadyExists,
36
37 #[error("Email not verified")]
38 EmailNotVerified,
39
40 #[error("Unsupported authentication method: {0}")]
41 UnsupportedMethod(String),
42
43 #[error("Account already linked")]
44 AccountAlreadyLinked,
45
46 #[error("Password hash error: {0}")]
47 PasswordHashError(String),
48}
49
50#[derive(Debug, Error)]
51pub enum SessionError {
52 #[error("Session not found")]
53 NotFound,
54
55 #[error("Session expired")]
56 Expired,
57
58 #[error("Session already exists")]
59 AlreadyExists,
60
61 #[error("Invalid token: {0}")]
62 InvalidToken(String),
63}
64
65#[derive(Debug, Error)]
66pub enum StorageError {
67 #[error("Database error: {0}")]
68 Database(String),
69
70 #[error("Migration error: {0}")]
71 Migration(String),
72
73 #[error("Connection error: {0}")]
74 Connection(String),
75
76 #[error("Record not found")]
77 NotFound,
78
79 #[error("Constraint violation: {0}")]
80 Constraint(String),
81}
82
83#[derive(Debug, Error)]
84pub enum ValidationError {
85 #[error("Invalid email format: {0}")]
86 InvalidEmail(String),
87
88 #[error("Invalid password: {0}")]
89 InvalidPassword(String),
90
91 #[error("Weak password")]
92 WeakPassword,
93
94 #[error("Invalid name: {0}")]
95 InvalidName(String),
96
97 #[error("Invalid user ID: {0}")]
98 InvalidUserId(String),
99
100 #[error("Invalid provider: {0}")]
101 InvalidProvider(String),
102
103 #[error("Invalid field: {0}")]
104 InvalidField(String),
105
106 #[error("Missing required field: {0}")]
107 MissingField(String),
108}
109
110#[derive(Debug, Error)]
111pub enum EventError {
112 #[error("Event bus error: {0}")]
113 BusError(String),
114
115 #[error("Event handler error: {0}")]
116 HandlerError(String),
117}
118
119#[derive(Debug, Error)]
120pub enum CryptoError {
121 #[error("JWT signing failed: {0}")]
122 JwtSigning(String),
123
124 #[error("JWT verification failed: {0}")]
125 JwtVerification(String),
126
127 #[error("Password hashing failed: {0}")]
128 PasswordHash(String),
129
130 #[error("Passkey operation failed: {0}")]
131 Passkey(String),
132}
133
134impl Error {
135 pub fn is_auth_error(&self) -> bool {
136 matches!(
137 self,
138 Error::Auth(AuthError::InvalidCredentials)
139 | Error::Auth(AuthError::UserNotFound)
140 | Error::Auth(AuthError::UserAlreadyExists)
141 )
142 }
143
144 pub fn is_validation_error(&self) -> bool {
145 matches!(
146 self,
147 Error::Validation(ValidationError::InvalidEmail(_))
148 | Error::Validation(ValidationError::WeakPassword)
149 | Error::Validation(ValidationError::InvalidField(_))
150 | Error::Validation(ValidationError::MissingField(_))
151 )
152 }
153
154 pub fn is_storage_error(&self) -> bool {
155 matches!(self, Error::Storage(_))
156 }
157
158 pub fn is_session_error(&self) -> bool {
159 matches!(self, Error::Session(_))
160 }
161
162 pub fn is_crypto_error(&self) -> bool {
163 matches!(self, Error::Crypto(_))
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_error_display() {
173 let auth_error = Error::Auth(AuthError::InvalidCredentials);
174 assert_eq!(
175 auth_error.to_string(),
176 "Authentication error: Invalid credentials"
177 );
178
179 let validation_error =
180 Error::Validation(ValidationError::InvalidEmail("test@".to_string()));
181 assert_eq!(
182 validation_error.to_string(),
183 "Validation error: Invalid email format: test@"
184 );
185
186 let storage_error = Error::Storage(StorageError::NotFound);
187 assert_eq!(storage_error.to_string(), "Storage error: Record not found");
188 }
189
190 #[test]
191 fn test_auth_error_variants() {
192 let invalid_creds = AuthError::InvalidCredentials;
193 assert_eq!(invalid_creds.to_string(), "Invalid credentials");
194
195 let user_not_found = AuthError::UserNotFound;
196 assert_eq!(user_not_found.to_string(), "User not found");
197
198 let user_exists = AuthError::UserAlreadyExists;
199 assert_eq!(user_exists.to_string(), "User already exists");
200
201 let unsupported = AuthError::UnsupportedMethod("WebAuthn".to_string());
202 assert_eq!(
203 unsupported.to_string(),
204 "Unsupported authentication method: WebAuthn"
205 );
206 }
207
208 #[test]
209 fn test_is_auth_error() {
210 assert!(Error::Auth(AuthError::InvalidCredentials).is_auth_error());
211 assert!(Error::Auth(AuthError::UserNotFound).is_auth_error());
212 assert!(Error::Auth(AuthError::UserAlreadyExists).is_auth_error());
213 assert!(!Error::Auth(AuthError::EmailNotVerified).is_auth_error());
214 assert!(!Error::Storage(StorageError::NotFound).is_auth_error());
215 }
216
217 #[test]
218 fn test_is_validation_error() {
219 assert!(
220 Error::Validation(ValidationError::InvalidEmail("test".to_string()))
221 .is_validation_error()
222 );
223 assert!(Error::Validation(ValidationError::WeakPassword).is_validation_error());
224 assert!(
225 Error::Validation(ValidationError::InvalidField("name".to_string()))
226 .is_validation_error()
227 );
228 assert!(
229 Error::Validation(ValidationError::MissingField("email".to_string()))
230 .is_validation_error()
231 );
232 assert!(!Error::Auth(AuthError::InvalidCredentials).is_validation_error());
233 }
234
235 #[test]
236 fn test_session_error_variants() {
237 let not_found = SessionError::NotFound;
238 assert_eq!(not_found.to_string(), "Session not found");
239
240 let expired = SessionError::Expired;
241 assert_eq!(expired.to_string(), "Session expired");
242
243 let invalid_token = SessionError::InvalidToken("malformed".to_string());
244 assert_eq!(invalid_token.to_string(), "Invalid token: malformed");
245 }
246
247 #[test]
248 fn test_storage_error_variants() {
249 let db_error = StorageError::Database("connection failed".to_string());
250 assert_eq!(db_error.to_string(), "Database error: connection failed");
251
252 let not_found = StorageError::NotFound;
253 assert_eq!(not_found.to_string(), "Record not found");
254 }
255
256 #[test]
257 fn test_validation_error_variants() {
258 let invalid_email = ValidationError::InvalidEmail("bad@".to_string());
259 assert_eq!(invalid_email.to_string(), "Invalid email format: bad@");
260
261 let weak_password = ValidationError::WeakPassword;
262 assert_eq!(weak_password.to_string(), "Weak password");
263
264 let missing_field = ValidationError::MissingField("username".to_string());
265 assert_eq!(
266 missing_field.to_string(),
267 "Missing required field: username"
268 );
269 }
270
271 #[test]
272 fn test_event_error_variants() {
273 let bus_error = EventError::BusError("dispatcher failed".to_string());
274 assert_eq!(bus_error.to_string(), "Event bus error: dispatcher failed");
275
276 let handler_error = EventError::HandlerError("timeout".to_string());
277 assert_eq!(handler_error.to_string(), "Event handler error: timeout");
278 }
279
280 #[test]
281 fn test_error_from_conversions() {
282 let auth_error = AuthError::InvalidCredentials;
283 let error: Error = auth_error.into();
284 assert!(matches!(error, Error::Auth(AuthError::InvalidCredentials)));
285
286 let validation_error = ValidationError::WeakPassword;
287 let error: Error = validation_error.into();
288 assert!(matches!(
289 error,
290 Error::Validation(ValidationError::WeakPassword)
291 ));
292 }
293}