use std::error::Error as StdError;
use std::fmt;
use crate::provider::ProviderId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
Auth,
InvalidRequest,
Declined,
NotFound,
RateLimited,
Transport,
Malformed,
Unsupported,
Provider,
}
impl ErrorKind {
#[must_use]
pub const fn is_retryable(self) -> bool {
matches!(self, Self::RateLimited | Self::Transport | Self::Provider)
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
Self::Auth => "authentication",
Self::InvalidRequest => "invalid request",
Self::Declined => "declined",
Self::NotFound => "not found",
Self::RateLimited => "rate limited",
Self::Transport => "transport",
Self::Malformed => "malformed response",
Self::Unsupported => "unsupported",
Self::Provider => "provider failure",
};
f.write_str(text)
}
}
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
provider: ProviderId,
message: Box<str>,
code: Option<Box<str>>,
source: Option<Box<dyn StdError + Send + Sync>>,
}
impl Error {
pub fn new(kind: ErrorKind, provider: ProviderId, message: impl Into<Box<str>>) -> Self {
Self {
kind,
provider,
message: message.into(),
code: None,
source: None,
}
}
#[must_use]
pub fn with_code(mut self, code: impl Into<Box<str>>) -> Self {
self.code = Some(code.into());
self
}
#[must_use]
pub fn with_source(mut self, source: impl StdError + Send + Sync + 'static) -> Self {
self.source = Some(Box::new(source));
self
}
#[must_use]
pub const fn kind(&self) -> ErrorKind {
self.kind
}
#[must_use]
pub const fn provider(&self) -> ProviderId {
self.provider
}
#[must_use]
pub fn code(&self) -> Option<&str> {
self.code.as_deref()
}
#[must_use]
pub const fn is_retryable(&self) -> bool {
self.kind.is_retryable()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {} ({})", self.provider, self.message, self.kind)?;
if let Some(code) = &self.code {
write!(f, " [{code}]")?;
}
Ok(())
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source
.as_ref()
.map(|e| &**e as &(dyn StdError + 'static))
}
}