1use prax_query::QueryError;
4use thiserror::Error;
5
6pub type SqlxResult<T> = Result<T, SqlxError>;
8
9#[derive(Error, Debug)]
11pub enum SqlxError {
12 #[error("Database error: {0}")]
14 Sqlx(#[from] sqlx::Error),
15
16 #[error("Configuration error: {0}")]
18 Config(String),
19
20 #[error("Connection error: {0}")]
22 Connection(String),
23
24 #[error("Query error: {0}")]
26 Query(String),
27
28 #[error("Deserialization error: {0}")]
30 Deserialization(String),
31
32 #[error("Type conversion error: {0}")]
34 TypeConversion(String),
35
36 #[error("Pool error: {0}")]
38 Pool(String),
39
40 #[error("Operation timed out after {0}ms")]
42 Timeout(u64),
43
44 #[error("Migration error: {0}")]
46 Migration(String),
47
48 #[error("Internal error: {0}")]
50 Internal(String),
51}
52
53impl From<SqlxError> for QueryError {
54 fn from(err: SqlxError) -> Self {
55 match err {
56 SqlxError::Sqlx(e) => {
57 let msg = e.to_string();
58 if msg.contains("connection") {
59 QueryError::connection(msg)
60 } else if msg.contains("constraint") || msg.contains("duplicate") {
61 QueryError::constraint_violation("unknown", msg)
62 } else {
63 QueryError::database(msg)
64 }
65 }
66 SqlxError::Config(msg) => QueryError::connection(msg),
67 SqlxError::Connection(msg) => QueryError::connection(msg),
68 SqlxError::Query(msg) => QueryError::database(msg),
69 SqlxError::Deserialization(msg) => QueryError::serialization(msg),
70 SqlxError::TypeConversion(msg) => QueryError::serialization(msg),
71 SqlxError::Pool(msg) => QueryError::connection(msg),
72 SqlxError::Timeout(ms) => QueryError::timeout(ms),
73 SqlxError::Migration(msg) => QueryError::database(msg),
74 SqlxError::Internal(msg) => QueryError::internal(msg),
75 }
76 }
77}
78
79impl SqlxError {
80 pub fn config(msg: impl Into<String>) -> Self {
82 Self::Config(msg.into())
83 }
84
85 pub fn connection(msg: impl Into<String>) -> Self {
87 Self::Connection(msg.into())
88 }
89
90 pub fn query(msg: impl Into<String>) -> Self {
92 Self::Query(msg.into())
93 }
94
95 pub fn pool(msg: impl Into<String>) -> Self {
97 Self::Pool(msg.into())
98 }
99
100 pub fn timeout(ms: u64) -> Self {
102 Self::Timeout(ms)
103 }
104
105 pub fn type_conversion(msg: impl Into<String>) -> Self {
107 Self::TypeConversion(msg.into())
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_error_creation() {
117 let err = SqlxError::config("test config error");
118 assert!(matches!(err, SqlxError::Config(_)));
119
120 let err = SqlxError::connection("test connection error");
121 assert!(matches!(err, SqlxError::Connection(_)));
122
123 let err = SqlxError::timeout(5000);
124 assert!(matches!(err, SqlxError::Timeout(5000)));
125 }
126
127 #[test]
128 fn test_error_to_query_error() {
129 let err = SqlxError::connection("connection failed");
130 let query_err: QueryError = err.into();
131 assert!(query_err.to_string().contains("connection"));
132
133 let err = SqlxError::timeout(1000);
134 let query_err: QueryError = err.into();
135 assert!(
136 query_err.to_string().contains("timeout") || query_err.to_string().contains("1000")
137 );
138 }
139}