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