1#![forbid(unsafe_code)]
40#![deny(clippy::all)]
41
42pub mod arena;
43pub mod codec;
44pub mod pool;
45pub(crate) mod types;
46
47mod auth;
48mod conn;
49mod proto;
50mod stmt_cache;
51mod sync_io;
52#[cfg(feature = "tls")]
53mod tls_sync;
54
55#[cfg(feature = "async")]
56pub mod async_conn;
57#[cfg(feature = "async")]
58mod async_io;
59
60pub use arena::Arena;
61#[cfg(feature = "async")]
62pub use async_conn::AsyncConnection;
63pub use codec::Encode;
64pub use conn::Connection;
65pub use conn::release_resp_buf;
66pub use pool::{Pool, PoolBuilder, PoolGuard, PoolStatus, Transaction};
67pub use types::{
68 ColumnDesc, Config, Notification, PgDataRow, PrepareResult, QueryResult, Row, SimpleRow,
69 SslMode, hash_sql,
70};
71
72#[derive(Debug)]
97pub enum DriverError {
98 Io(std::io::Error),
100 Auth(String),
102 Protocol(String),
104 Server {
106 code: Box<str>,
108 message: Box<str>,
110 detail: Option<Box<str>>,
112 hint: Option<Box<str>>,
114 position: Option<u32>,
116 },
117 Pool(String),
119}
120
121impl std::fmt::Display for DriverError {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 Self::Io(e) => write!(f, "I/O error: {e}"),
125 Self::Auth(msg) => write!(f, "auth error: {msg}"),
126 Self::Protocol(msg) => write!(f, "protocol error: {msg}"),
127 Self::Server {
128 code,
129 message,
130 detail,
131 hint,
132 position,
133 } => {
134 write!(f, "server error [{code}]: {message}")?;
135 if let Some(pos) = position {
136 write!(f, " (at position {pos})")?;
137 }
138 if let Some(d) = detail {
139 write!(f, " DETAIL: {d}")?;
140 }
141 if let Some(h) = hint {
142 write!(f, " HINT: {h}")?;
143 }
144 Ok(())
145 }
146 Self::Pool(msg) => write!(f, "pool error: {msg}"),
147 }
148 }
149}
150
151impl std::error::Error for DriverError {
152 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
153 match self {
154 Self::Io(e) => Some(e),
155 _ => None,
156 }
157 }
158}
159
160impl From<std::io::Error> for DriverError {
161 fn from(e: std::io::Error) -> Self {
162 Self::Io(e)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn driver_error_display_io() {
172 let e = DriverError::Io(std::io::Error::new(
173 std::io::ErrorKind::ConnectionRefused,
174 "refused",
175 ));
176 assert!(e.to_string().contains("I/O error"));
177 assert!(e.to_string().contains("refused"));
178 }
179
180 #[test]
181 fn driver_error_display_auth() {
182 let e = DriverError::Auth("wrong password".into());
183 assert_eq!(e.to_string(), "auth error: wrong password");
184 }
185
186 #[test]
187 fn driver_error_display_protocol() {
188 let e = DriverError::Protocol("unexpected message".into());
189 assert_eq!(e.to_string(), "protocol error: unexpected message");
190 }
191
192 #[test]
193 fn driver_error_display_server() {
194 let e = DriverError::Server {
195 code: "42P01".into(),
196 message: "relation does not exist".into(),
197 detail: Some("table was dropped".into()),
198 hint: None,
199 position: None,
200 };
201 let s = e.to_string();
202 assert!(s.contains("42P01"));
203 assert!(s.contains("relation does not exist"));
204 assert!(s.contains("table was dropped"));
205 }
206
207 #[test]
208 fn driver_error_display_server_no_detail() {
209 let e = DriverError::Server {
210 code: Box::from("23505"),
211 message: Box::from("duplicate key"),
212 detail: None,
213 hint: None,
214 position: None,
215 };
216 assert_eq!(e.to_string(), "server error [23505]: duplicate key");
217 }
218
219 #[test]
220 fn driver_error_display_server_with_position() {
221 let e = DriverError::Server {
222 code: Box::from("42601"),
223 message: Box::from("syntax error"),
224 detail: None,
225 hint: None,
226 position: Some(8),
227 };
228 let s = e.to_string();
229 assert!(s.contains("(at position 8)"));
230 }
231
232 #[test]
233 fn driver_error_display_pool() {
234 let e = DriverError::Pool("exhausted".into());
235 assert_eq!(e.to_string(), "pool error: exhausted");
236 }
237
238 #[test]
239 fn driver_error_source_io() {
240 let inner = std::io::Error::other("test");
241 let e = DriverError::Io(inner);
242 assert!(std::error::Error::source(&e).is_some());
243 }
244
245 #[test]
246 fn driver_error_source_non_io() {
247 let e = DriverError::Auth("test".into());
248 assert!(std::error::Error::source(&e).is_none());
249 }
250
251 #[test]
252 fn driver_error_from_io() {
253 let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
254 let e: DriverError = io_err.into();
255 assert!(matches!(e, DriverError::Io(_)));
256 }
257
258 #[test]
259 fn forbid_unsafe_code() {
260 }
263
264 #[test]
269 fn driver_error_display_server_all_none() {
270 let e = DriverError::Server {
271 code: "00000".into(),
272 message: "successful completion".into(),
273 detail: None,
274 hint: None,
275 position: None,
276 };
277 let s = e.to_string();
278 assert_eq!(s, "server error [00000]: successful completion");
279 assert!(!s.contains("DETAIL"));
281 assert!(!s.contains("HINT"));
282 assert!(!s.contains("position"));
283 }
284
285 #[test]
286 fn driver_error_display_server_detail_only() {
287 let e = DriverError::Server {
288 code: "23505".into(),
289 message: "duplicate key".into(),
290 detail: Some("Key (id)=(1) exists.".into()),
291 hint: None,
292 position: None,
293 };
294 let s = e.to_string();
295 assert!(s.contains("DETAIL: Key (id)=(1) exists."));
296 assert!(!s.contains("HINT"));
297 }
298
299 #[test]
300 fn driver_error_display_server_hint_only() {
301 let e = DriverError::Server {
302 code: "42601".into(),
303 message: "syntax error".into(),
304 detail: None,
305 hint: Some("check SQL".into()),
306 position: None,
307 };
308 let s = e.to_string();
309 assert!(s.contains("HINT: check SQL"));
310 assert!(!s.contains("DETAIL"));
311 }
312
313 #[test]
314 fn driver_error_display_server_position_only() {
315 let e = DriverError::Server {
316 code: "42601".into(),
317 message: "syntax error".into(),
318 detail: None,
319 hint: None,
320 position: Some(15),
321 };
322 let s = e.to_string();
323 assert!(s.contains("(at position 15)"));
324 }
325
326 #[test]
327 fn driver_error_display_server_all_fields() {
328 let e = DriverError::Server {
329 code: "42P01".into(),
330 message: "relation does not exist".into(),
331 detail: Some("table was dropped".into()),
332 hint: Some("recreate the table".into()),
333 position: Some(42),
334 };
335 let s = e.to_string();
336 assert!(s.contains("[42P01]"));
337 assert!(s.contains("relation does not exist"));
338 assert!(s.contains("(at position 42)"));
339 assert!(s.contains("DETAIL: table was dropped"));
340 assert!(s.contains("HINT: recreate the table"));
341 }
342
343 #[test]
344 fn driver_error_io_preserves_kind() {
345 let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
346 let e = DriverError::Io(io_err);
347 match &e {
348 DriverError::Io(inner) => {
349 assert_eq!(inner.kind(), std::io::ErrorKind::ConnectionRefused);
350 }
351 _ => panic!("expected Io variant"),
352 }
353 }
354
355 #[test]
356 fn driver_error_io_timeout() {
357 let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "connection timed out");
358 let e = DriverError::Io(io_err);
359 let s = e.to_string();
360 assert!(s.contains("timed out"));
361 }
362
363 #[test]
364 fn driver_error_io_unexpected_eof() {
365 let io_err = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "connection closed");
366 let e: DriverError = io_err.into();
367 let s = e.to_string();
368 assert!(s.contains("connection closed"));
369 }
370
371 #[test]
372 fn driver_error_auth_empty() {
373 let e = DriverError::Auth(String::new());
374 assert_eq!(e.to_string(), "auth error: ");
375 }
376
377 #[test]
378 fn driver_error_protocol_empty() {
379 let e = DriverError::Protocol(String::new());
380 assert_eq!(e.to_string(), "protocol error: ");
381 }
382
383 #[test]
384 fn driver_error_pool_empty() {
385 let e = DriverError::Pool(String::new());
386 assert_eq!(e.to_string(), "pool error: ");
387 }
388
389 #[test]
390 fn driver_error_source_protocol_is_none() {
391 let e = DriverError::Protocol("test".into());
392 assert!(std::error::Error::source(&e).is_none());
393 }
394
395 #[test]
396 fn driver_error_source_server_is_none() {
397 let e = DriverError::Server {
398 code: "42601".into(),
399 message: "err".into(),
400 detail: None,
401 hint: None,
402 position: None,
403 };
404 assert!(std::error::Error::source(&e).is_none());
405 }
406
407 #[test]
408 fn driver_error_source_pool_is_none() {
409 let e = DriverError::Pool("test".into());
410 assert!(std::error::Error::source(&e).is_none());
411 }
412
413 #[test]
414 fn driver_error_debug_all_variants() {
415 let variants: Vec<DriverError> = vec![
416 DriverError::Io(std::io::Error::other("io")),
417 DriverError::Auth("auth".into()),
418 DriverError::Protocol("proto".into()),
419 DriverError::Server {
420 code: "00000".into(),
421 message: "ok".into(),
422 detail: None,
423 hint: None,
424 position: None,
425 },
426 DriverError::Pool("pool".into()),
427 ];
428 for v in &variants {
429 let dbg = format!("{v:?}");
430 assert!(!dbg.is_empty());
431 }
432 }
433}