use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Auth,
Forbidden,
NotFound,
Conflict,
TooLarge,
Invalid,
Busy,
Other,
}
impl Kind {
pub fn from_status(status: u16) -> Kind {
match status {
401 => Kind::Auth,
403 => Kind::Forbidden,
404 => Kind::NotFound,
409 => Kind::Conflict,
413 => Kind::TooLarge,
422 => Kind::Invalid,
429 | 503 => Kind::Busy,
_ => Kind::Other,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{status} {kind:?}: {message}")]
Api {
status: u16,
kind: Kind,
message: String,
detail: serde_json::Value,
},
#[error("{0}")]
Transport(String),
#[error("timed out after {0:?}")]
Timeout(Duration),
}
impl Error {
pub fn status(&self) -> Option<u16> {
match self {
Error::Api { status, .. } => Some(*status),
_ => None,
}
}
pub fn kind(&self) -> Option<Kind> {
match self {
Error::Api { kind, .. } => Some(*kind),
_ => None,
}
}
fn is(&self, want: Kind) -> bool {
self.kind() == Some(want)
}
pub fn is_auth(&self) -> bool {
self.is(Kind::Auth)
}
pub fn is_forbidden(&self) -> bool {
self.is(Kind::Forbidden)
}
pub fn is_not_found(&self) -> bool {
self.is(Kind::NotFound)
}
pub fn is_conflict(&self) -> bool {
self.is(Kind::Conflict)
}
pub fn is_too_large(&self) -> bool {
self.is(Kind::TooLarge)
}
pub fn is_invalid(&self) -> bool {
self.is(Kind::Invalid)
}
pub fn is_busy(&self) -> bool {
self.is(Kind::Busy)
}
pub fn is_timeout(&self) -> bool {
matches!(self, Error::Timeout(_))
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;