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