use std::fmt;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Http {
status: u16,
body: String,
method: String,
path: String,
},
Transport(reqwest::Error),
Timeout {
kind: String,
id: String,
secs: f64,
},
Failed {
kind: String,
id: String,
error: Option<String>,
},
Invalid(String),
Api(String),
Json(serde_json::Error),
Io(std::io::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Http {
status,
body,
method,
path,
} => write!(f, "{method} {path} failed with {status}: {body}"),
Error::Transport(e) => write!(f, "request failed: {e}"),
Error::Timeout { kind, id, secs } => {
write!(f, "{kind} {id} still running after {secs}s")
}
Error::Failed { kind, id, error } => write!(
f,
"{kind} {id} failed: {}",
error.as_deref().unwrap_or("None")
),
Error::Invalid(m) | Error::Api(m) => write!(f, "{m}"),
Error::Json(e) => write!(f, "invalid JSON from the API: {e}"),
Error::Io(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Transport(e) => Some(e),
Error::Json(e) => Some(e),
Error::Io(e) => Some(e),
_ => None,
}
}
}
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Error::Transport(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Json(e)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl Error {
pub(crate) fn is_transient(&self) -> bool {
match self {
Error::Transport(_) => true,
Error::Http { status, .. } => crate::client::RETRYABLE_STATUS.contains(status),
_ => false,
}
}
pub fn status(&self) -> Option<u16> {
match self {
Error::Http { status, .. } => Some(*status),
_ => None,
}
}
}
pub type Result<T> = std::result::Result<T, Error>;