1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug, thiserror::Error)]
30#[non_exhaustive]
31pub enum Error {
32 #[error("io error: {0}")]
34 Io(#[from] std::io::Error),
35
36 #[error("protocol error: {0}")]
38 Protocol(String),
39
40 #[error("{0}")]
42 Server(Box<ServerError>),
43
44 #[error("authentication failed: {0}")]
46 Auth(String),
47
48 #[error("tls error: {0}")]
50 Tls(String),
51
52 #[error("pool error: {0}")]
54 Pool(String),
55
56 #[error("config error: {0}")]
58 Config(String),
59
60 #[error("encode error: {0}")]
62 Encode(String),
63
64 #[error("decode error: {0}")]
66 Decode(String),
67
68 #[error("column not found: {0}")]
70 ColumnNotFound(String),
71
72 #[error("column index {index} out of bounds (row has {count} columns)")]
74 ColumnIndex { index: usize, count: usize },
75
76 #[error("unexpected null in column {0}")]
78 UnexpectedNull(usize),
79
80 #[error("timeout: {0}")]
82 Timeout(String),
83
84 #[error("connection closed")]
86 ConnectionClosed,
87
88 #[error("copy error: {0}")]
90 Copy(String),
91
92 #[error("transaction already completed")]
94 TransactionCompleted,
95
96 #[error("all hosts failed: {0}")]
98 AllHostsFailed(String),
99
100 #[error("wrong session attributes: {0}")]
102 WrongSessionAttrs(String),
103}
104
105#[derive(Debug, Clone)]
107#[non_exhaustive]
108pub struct ServerError {
109 pub severity: String,
110 pub code: String,
111 pub message: String,
112 pub detail: Option<String>,
113 pub hint: Option<String>,
114 pub position: Option<u32>,
115}
116
117impl fmt::Display for ServerError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(
120 f,
121 "{}: {} (SQLSTATE {})",
122 self.severity, self.message, self.code
123 )
124 }
125}
126
127impl Error {
128 pub fn code(&self) -> Option<&str> {
130 match self {
131 Error::Server(e) => Some(&e.code),
132 _ => None,
133 }
134 }
135
136 pub fn server_error(&self) -> Option<&ServerError> {
138 match self {
139 Error::Server(e) => Some(e),
140 _ => None,
141 }
142 }
143
144 pub fn is_unique_violation(&self) -> bool {
146 self.code() == Some("23505")
147 }
148
149 pub fn is_foreign_key_violation(&self) -> bool {
151 self.code() == Some("23503")
152 }
153
154 pub fn is_fatal(&self) -> bool {
156 matches!(self, Error::Io(_) | Error::ConnectionClosed | Error::Tls(_))
157 }
158}
159
160impl Error {
161 pub(crate) fn protocol(msg: impl Into<String>) -> Self {
163 Error::Protocol(msg.into())
164 }
165
166 pub(crate) fn server(
168 severity: String,
169 code: String,
170 message: String,
171 detail: Option<String>,
172 hint: Option<String>,
173 position: Option<u32>,
174 ) -> Self {
175 Error::Server(Box::new(ServerError {
176 severity,
177 code,
178 message,
179 detail,
180 hint,
181 position,
182 }))
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188#[non_exhaustive]
189pub enum Severity {
190 Error,
191 Fatal,
192 Panic,
193 Warning,
194 Notice,
195 Debug,
196 Info,
197 Log,
198}
199
200impl fmt::Display for Severity {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 match self {
203 Severity::Error => write!(f, "ERROR"),
204 Severity::Fatal => write!(f, "FATAL"),
205 Severity::Panic => write!(f, "PANIC"),
206 Severity::Warning => write!(f, "WARNING"),
207 Severity::Notice => write!(f, "NOTICE"),
208 Severity::Debug => write!(f, "DEBUG"),
209 Severity::Info => write!(f, "INFO"),
210 Severity::Log => write!(f, "LOG"),
211 }
212 }
213}