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