1use std::error::Error;
2use std::fmt;
3
4#[derive(Debug, Clone)]
6#[non_exhaustive]
7pub enum RullstError {
8 NotInitialized,
10 AlreadyInitialized,
12 RecordNotFound,
14 DatabaseError(String),
16 SerializationError(String),
18 CacheError(String),
20 PostCommit(String),
22 Validation(String),
24 Internal(String),
26}
27
28impl fmt::Display for RullstError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 RullstError::NotInitialized => {
32 write!(
33 f,
34 "Orm is not initialized. Call Orm::init() before querying."
35 )
36 }
37 RullstError::AlreadyInitialized => write!(f, "Orm has already been initialized"),
38 RullstError::RecordNotFound => write!(f, "Record not found"),
39 RullstError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
40 RullstError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
41 RullstError::CacheError(msg) => write!(f, "Cache error: {}", msg),
42 RullstError::PostCommit(msg) => {
43 write!(
44 f,
45 "Database committed, but a post-commit effect failed: {}",
46 msg
47 )
48 }
49 RullstError::Validation(msg) => write!(f, "Validation error: {}", msg),
50 RullstError::Internal(msg) => write!(f, "Internal error: {}", msg),
51 }
52 }
53}
54
55impl Error for RullstError {}
56
57impl From<sqlx::Error> for RullstError {
58 fn from(err: sqlx::Error) -> Self {
59 match err {
60 sqlx::Error::RowNotFound => RullstError::RecordNotFound,
61 _ => RullstError::DatabaseError(err.to_string()),
62 }
63 }
64}
65
66impl From<serde_json::Error> for RullstError {
67 fn from(err: serde_json::Error) -> Self {
68 RullstError::SerializationError(err.to_string())
69 }
70}
71
72impl From<crate::privacy::PrivacyError> for RullstError {
73 fn from(err: crate::privacy::PrivacyError) -> Self {
74 RullstError::Internal(err.to_string())
75 }
76}
77
78#[cfg(feature = "redis")]
79impl From<redis::RedisError> for RullstError {
80 #[cfg_attr(test, mutants::skip)]
81 fn from(err: redis::RedisError) -> Self {
82 RullstError::CacheError(err.to_string())
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_rullst_error_display() {
92 assert_eq!(
93 RullstError::NotInitialized.to_string(),
94 "Orm is not initialized. Call Orm::init() before querying."
95 );
96 assert_eq!(
97 RullstError::AlreadyInitialized.to_string(),
98 "Orm has already been initialized"
99 );
100 assert_eq!(RullstError::RecordNotFound.to_string(), "Record not found");
101 assert_eq!(
102 RullstError::DatabaseError("msg".to_string()).to_string(),
103 "Database error: msg"
104 );
105 assert_eq!(
106 RullstError::SerializationError("msg".to_string()).to_string(),
107 "Serialization error: msg"
108 );
109 assert_eq!(
110 RullstError::PostCommit("msg".to_string()).to_string(),
111 "Database committed, but a post-commit effect failed: msg"
112 );
113 assert_eq!(
114 RullstError::Validation("msg".to_string()).to_string(),
115 "Validation error: msg"
116 );
117 assert_eq!(
118 RullstError::Internal("msg".to_string()).to_string(),
119 "Internal error: msg"
120 );
121 }
122
123 #[test]
124 fn test_rullst_error_from() {
125 let sqlx_err = sqlx::Error::RowNotFound;
126 let err: RullstError = sqlx_err.into();
127 assert!(matches!(err, RullstError::RecordNotFound));
128
129 let sqlx_other_err = sqlx::Error::PoolClosed;
130 let err2: RullstError = sqlx_other_err.into();
131 assert!(matches!(err2, RullstError::DatabaseError(_)));
132 }
133
134 #[test]
135 fn test_serde_json_error_from() {
136 let json_err = serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err();
137 let err: RullstError = json_err.into();
138 assert!(matches!(err, RullstError::SerializationError(_)));
139 assert!(err.to_string().contains("Serialization error:"));
140 }
141
142 #[cfg(feature = "redis")]
143 #[test]
144 fn test_redis_error_from() {
145 let redis_err = redis::RedisError::from((redis::ErrorKind::Io, "redis io error"));
146 let err: RullstError = redis_err.into();
147 assert!(matches!(err, RullstError::CacheError(_)));
148 assert_eq!(err.to_string(), "Cache error: redis io error - Io");
149 }
150}