1use std::fmt;
3
4pub type NythosResult<T> = Result<T, AuthError>;
6
7#[non_exhaustive]
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum AuthError {
14 UserNotFound,
15 InvalidCredentials,
16 AccountLocked,
17 SessionRevoked,
18 SessionExpired,
19 TenantNotFound,
20 PermissionDenied,
21 OAuthIdentityAlreadyLinked,
22 OAuthIdentityAlreadyLinkedToSelf,
23 UserNotFoundOrInactive,
24 ValidationError(String),
25 Internal(String),
26}
27
28impl fmt::Display for AuthError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 AuthError::UserNotFound => f.write_str("user not found"),
32 AuthError::InvalidCredentials => f.write_str("invalid credentials"),
33 AuthError::AccountLocked => f.write_str("account locked"),
34 AuthError::SessionRevoked => f.write_str("session revoked"),
35 AuthError::SessionExpired => f.write_str("session expired"),
36 AuthError::TenantNotFound => f.write_str("tenant not found"),
37 AuthError::PermissionDenied => f.write_str("permission denied"),
38 AuthError::OAuthIdentityAlreadyLinked => f.write_str("OAuth identity already linked"),
39 AuthError::OAuthIdentityAlreadyLinkedToSelf => {
40 f.write_str("OAuth identity already linked to this user")
41 }
42 AuthError::UserNotFoundOrInactive => f.write_str("user not found or inactive"),
43 AuthError::ValidationError(msg) => write!(f, "validation error: {}", msg),
44 AuthError::Internal(msg) => write!(f, "internal error: {}", msg),
45 }
46 }
47}
48
49impl std::error::Error for AuthError {}
50
51#[cfg(test)]
52mod tests {
53 use super::{AuthError, NythosResult};
54
55 #[test]
56 fn string_payload_variants_preserve_messages() {
57 let validation_error = AuthError::ValidationError("invalid email".to_owned());
58 let internal_error = AuthError::Internal("signer unavailable".to_owned());
59
60 assert_eq!(
61 validation_error.to_string(),
62 "validation error: invalid email"
63 );
64
65 assert_eq!(
66 internal_error.to_string(),
67 "internal error: signer unavailable"
68 );
69 }
70
71 #[test]
72 fn unit_variants_match_as_expected() {
73 let err = AuthError::InvalidCredentials;
74
75 assert!(matches!(err, AuthError::InvalidCredentials));
76 assert_ne!(err, AuthError::UserNotFound);
77 }
78
79 #[test]
80 fn display_messages_are_transport_agnostic() {
81 assert_eq!(AuthError::UserNotFound.to_string(), "user not found");
82 assert_eq!(
83 AuthError::ValidationError("bad input".to_owned()).to_string(),
84 "validation error: bad input"
85 );
86 assert_eq!(
87 AuthError::Internal("store failed".to_owned()).to_string(),
88 "internal error: store failed"
89 );
90 }
91
92 #[test]
93 fn nythos_result_alias_uses_auth_error() {
94 fn fails() -> NythosResult<()> {
95 Err(AuthError::SessionExpired)
96 }
97
98 let result = fails();
99
100 assert!(matches!(result, Err(AuthError::SessionExpired)));
101 }
102}