1use prax_query::QueryError;
4use thiserror::Error;
5
6pub type PgResult<T> = Result<T, PgError>;
8
9#[derive(Error, Debug)]
11pub enum PgError {
12 #[error("pool error: {0}")]
14 Pool(#[from] deadpool_postgres::PoolError),
15
16 #[error("postgres error: {0}")]
18 Postgres(#[from] tokio_postgres::Error),
19
20 #[error("configuration error: {0}")]
22 Config(String),
23
24 #[error("connection error: {0}")]
26 Connection(String),
27
28 #[error("query error: {0}")]
30 Query(String),
31
32 #[error("deserialization error: {0}")]
34 Deserialization(String),
35
36 #[error("type conversion error: {0}")]
38 TypeConversion(String),
39
40 #[error("operation timed out after {0}ms")]
42 Timeout(u64),
43
44 #[error("internal error: {0}")]
46 Internal(String),
47}
48
49impl PgError {
50 pub fn config(message: impl Into<String>) -> Self {
52 Self::Config(message.into())
53 }
54
55 pub fn connection(message: impl Into<String>) -> Self {
57 Self::Connection(message.into())
58 }
59
60 pub fn query(message: impl Into<String>) -> Self {
62 Self::Query(message.into())
63 }
64
65 pub fn deserialization(message: impl Into<String>) -> Self {
67 Self::Deserialization(message.into())
68 }
69
70 pub fn type_conversion(message: impl Into<String>) -> Self {
72 Self::TypeConversion(message.into())
73 }
74
75 pub fn is_connection_error(&self) -> bool {
77 matches!(self, Self::Pool(_) | Self::Connection(_))
78 }
79
80 pub fn is_timeout(&self) -> bool {
82 matches!(self, Self::Timeout(_))
83 }
84}
85
86impl From<PgError> for QueryError {
87 fn from(err: PgError) -> Self {
88 match err {
89 PgError::Pool(e) => QueryError::connection(e.to_string()),
90 PgError::Postgres(e) => {
91 let code = e.code();
93 if let Some(code) = code {
94 let code_str = code.code();
95 if code_str == "23505" {
97 return QueryError::constraint_violation("", e.to_string());
98 }
99 if code_str == "23503" {
101 return QueryError::constraint_violation("", e.to_string());
102 }
103 if code_str == "23502" {
105 return QueryError::invalid_input("", e.to_string());
106 }
107 }
108 QueryError::database(e.to_string())
109 }
110 PgError::Config(msg) => QueryError::connection(msg),
111 PgError::Connection(msg) => QueryError::connection(msg),
112 PgError::Query(msg) => QueryError::database(msg),
113 PgError::Deserialization(msg) => QueryError::serialization(msg),
114 PgError::TypeConversion(msg) => QueryError::serialization(msg),
115 PgError::Timeout(ms) => QueryError::timeout(ms),
116 PgError::Internal(msg) => QueryError::internal(msg),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_error_creation() {
127 let err = PgError::config("invalid URL");
128 assert!(matches!(err, PgError::Config(_)));
129
130 let err = PgError::connection("connection refused");
131 assert!(err.is_connection_error());
132
133 let err = PgError::Timeout(5000);
134 assert!(err.is_timeout());
135 }
136
137 #[test]
138 fn test_into_query_error() {
139 let pg_err = PgError::Timeout(1000);
140 let query_err: QueryError = pg_err.into();
141 assert!(query_err.is_timeout());
142 }
143}