1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum Error {
9 #[error("io error: {0}")]
11 Io(#[from] std::io::Error),
12
13 #[error("protocol error: {0}")]
15 Protocol(String),
16
17 #[error("{0}")]
19 Server(Box<ServerError>),
20
21 #[error("authentication failed: {0}")]
23 Auth(String),
24
25 #[error("tls error: {0}")]
27 Tls(String),
28
29 #[error("pool error: {0}")]
31 Pool(String),
32
33 #[error("config error: {0}")]
35 Config(String),
36
37 #[error("encode error: {0}")]
39 Encode(String),
40
41 #[error("decode error: {0}")]
43 Decode(String),
44
45 #[error("column not found: {0}")]
47 ColumnNotFound(String),
48
49 #[error("column index {index} out of bounds (row has {count} columns)")]
51 ColumnIndex { index: usize, count: usize },
52
53 #[error("unexpected null in column {0}")]
55 UnexpectedNull(usize),
56
57 #[error("timeout: {0}")]
59 Timeout(String),
60
61 #[error("connection closed")]
63 ConnectionClosed,
64
65 #[error("copy error: {0}")]
67 Copy(String),
68
69 #[error("transaction already completed")]
71 TransactionCompleted,
72}
73
74#[derive(Debug, Clone)]
76pub struct ServerError {
77 pub severity: String,
78 pub code: String,
79 pub message: String,
80 pub detail: Option<String>,
81 pub hint: Option<String>,
82 pub position: Option<u32>,
83}
84
85impl fmt::Display for ServerError {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(
88 f,
89 "{}: {} (SQLSTATE {})",
90 self.severity, self.message, self.code
91 )
92 }
93}
94
95impl Error {
96 pub fn code(&self) -> Option<&str> {
98 match self {
99 Error::Server(e) => Some(&e.code),
100 _ => None,
101 }
102 }
103
104 pub fn server_error(&self) -> Option<&ServerError> {
106 match self {
107 Error::Server(e) => Some(e),
108 _ => None,
109 }
110 }
111
112 pub fn is_unique_violation(&self) -> bool {
114 self.code() == Some("23505")
115 }
116
117 pub fn is_foreign_key_violation(&self) -> bool {
119 self.code() == Some("23503")
120 }
121
122 pub fn is_fatal(&self) -> bool {
124 matches!(self, Error::Io(_) | Error::ConnectionClosed | Error::Tls(_))
125 }
126}
127
128impl Error {
129 pub(crate) fn protocol(msg: impl Into<String>) -> Self {
131 Error::Protocol(msg.into())
132 }
133
134 pub(crate) fn server(
136 severity: String,
137 code: String,
138 message: String,
139 detail: Option<String>,
140 hint: Option<String>,
141 position: Option<u32>,
142 ) -> Self {
143 Error::Server(Box::new(ServerError {
144 severity,
145 code,
146 message,
147 detail,
148 hint,
149 position,
150 }))
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum Severity {
157 Error,
158 Fatal,
159 Panic,
160 Warning,
161 Notice,
162 Debug,
163 Info,
164 Log,
165}
166
167impl fmt::Display for Severity {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 Severity::Error => write!(f, "ERROR"),
171 Severity::Fatal => write!(f, "FATAL"),
172 Severity::Panic => write!(f, "PANIC"),
173 Severity::Warning => write!(f, "WARNING"),
174 Severity::Notice => write!(f, "NOTICE"),
175 Severity::Debug => write!(f, "DEBUG"),
176 Severity::Info => write!(f, "INFO"),
177 Severity::Log => write!(f, "LOG"),
178 }
179 }
180}