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_str = e.code().map(|c| c.code().to_owned());
94 let msg = e.to_string();
95 let mapped = match code_str.as_deref() {
96 Some("23505") => QueryError::constraint_violation("", msg),
98 Some("23503") => QueryError::constraint_violation("", msg),
100 Some("23502") => QueryError::invalid_input("", msg),
102 _ => QueryError::database(msg),
103 };
104 mapped.with_source(e)
105 }
106 PgError::Config(msg) => QueryError::connection(msg),
107 PgError::Connection(msg) => QueryError::connection(msg),
108 PgError::Query(msg) => QueryError::database(msg),
109 PgError::Deserialization(msg) => QueryError::serialization(msg),
110 PgError::TypeConversion(msg) => QueryError::serialization(msg),
111 PgError::Timeout(ms) => QueryError::timeout(ms),
112 PgError::Internal(msg) => QueryError::internal(msg),
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_error_creation() {
123 let err = PgError::config("invalid URL");
124 assert!(matches!(err, PgError::Config(_)));
125
126 let err = PgError::connection("connection refused");
127 assert!(err.is_connection_error());
128
129 let err = PgError::Timeout(5000);
130 assert!(err.is_timeout());
131 }
132
133 #[test]
134 fn test_into_query_error() {
135 let pg_err = PgError::Timeout(1000);
136 let query_err: QueryError = pg_err.into();
137 assert!(query_err.is_timeout());
138 }
139}