1use {std::time::Duration, thiserror::Error};
2
3#[derive(Debug, Error)]
4pub enum PGError {
5 #[error(transparent)]
6 Postgres(tokio_postgres::Error),
7 #[error("Query timed out after {0:?}")]
8 Timeout(Duration),
9 #[error("Failed to reconnect after {0} attempts")]
10 FailedToReconnect(u32),
11 #[error(transparent)]
12 Other(Box<dyn std::error::Error + 'static>),
13}
14
15impl PGError {
16 pub fn is_pg_connection_issue(&self) -> bool {
17 matches!(self, PGError::Postgres(e) if is_pg_connection_issue(e))
18 }
19 pub fn is_timeout(&self) -> bool {
20 matches!(self, PGError::Timeout(_))
21 }
22
23 pub fn other(err: impl std::error::Error + 'static) -> Self {
24 PGError::Other(Box::new(err))
25 }
26}
27
28impl From<tokio_postgres::Error> for PGError {
29 fn from(e: tokio_postgres::Error) -> Self {
30 PGError::Postgres(e)
31 }
32}
33
34pub(crate) fn is_pg_connection_issue(err: &tokio_postgres::Error) -> bool {
39 if err.is_closed() {
40 return true;
41 }
42 let code = err.code().map(|state| state.code()).unwrap_or_default();
43 if code.starts_with("08") || code.starts_with("57") {
44 return true;
45 }
46
47 let msg = err.to_string();
48 msg.starts_with("error connecting to server")
49 || msg.starts_with("timeout waiting for server")
50 || msg.starts_with("error communicating with the server")
51}