use std::fmt;
use std::time::Duration;
use serde_json::Value;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("{0}")]
InvalidArgument(String),
#[error(transparent)]
Api(#[from] ApiError),
#[error("Request to {url} timed out.")]
Timeout {
url: String,
#[source]
source: reqwest::Error,
},
#[error("Could not reach Dregs at {url}: {source}")]
Connection {
url: String,
#[source]
source: reqwest::Error,
},
#[error("{0}")]
WebhookVerification(String),
}
impl Error {
pub fn api_error(&self) -> Option<&ApiError> {
match self {
Error::Api(error) => Some(error),
_ => None,
}
}
pub fn kind(&self) -> Option<ApiErrorKind> {
self.api_error().map(|error| error.kind)
}
pub fn status(&self) -> Option<u16> {
self.api_error().map(|error| error.status)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ApiErrorKind {
BadRequest,
Authentication,
QuotaExceeded,
PermissionDenied,
NotFound,
RateLimited,
Server,
Other,
}
impl ApiErrorKind {
pub fn from_status(status: u16) -> Self {
match status {
400 => ApiErrorKind::BadRequest,
401 => ApiErrorKind::Authentication,
402 => ApiErrorKind::QuotaExceeded,
403 => ApiErrorKind::PermissionDenied,
404 => ApiErrorKind::NotFound,
429 => ApiErrorKind::RateLimited,
500.. => ApiErrorKind::Server,
_ => ApiErrorKind::Other,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ApiError {
pub kind: ApiErrorKind,
pub status: u16,
pub message: String,
pub body: Option<Value>,
pub request_id: Option<String>,
pub retry_after: Option<Duration>,
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HTTP {}: {}", self.status, self.message)?;
if let Some(request_id) = &self.request_id {
write!(f, " (request {request_id})")?;
}
Ok(())
}
}
impl std::error::Error for ApiError {}