use alloc::boxed::Box;
use core::{convert::Infallible, time::Duration};
#[cfg(feature = "zenwave")]
pub mod zenwave;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FailureKind {
Network,
Transient,
Quota,
Permanent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Failure {
pub kind: FailureKind,
pub retry_after: Option<Duration>,
}
impl Failure {
#[must_use]
pub const fn new(kind: FailureKind) -> Self {
Self {
kind,
retry_after: None,
}
}
#[must_use]
pub const fn network() -> Self {
Self::new(FailureKind::Network)
}
#[must_use]
pub const fn transient() -> Self {
Self::new(FailureKind::Transient)
}
#[must_use]
pub const fn quota() -> Self {
Self::new(FailureKind::Quota)
}
#[must_use]
pub const fn permanent() -> Self {
Self::new(FailureKind::Permanent)
}
#[must_use]
pub const fn with_retry_after(mut self, retry_after: Option<Duration>) -> Self {
self.retry_after = retry_after;
self
}
#[must_use]
pub const fn is_retryable(&self) -> bool {
matches!(self.kind, FailureKind::Network | FailureKind::Transient)
}
}
pub trait Classify {
fn failure(&self) -> Failure;
}
impl<T: Classify + ?Sized> Classify for &T {
fn failure(&self) -> Failure {
T::failure(self)
}
}
impl<T: Classify + ?Sized> Classify for Box<T> {
fn failure(&self) -> Failure {
T::failure(self)
}
}
impl Classify for Infallible {
fn failure(&self) -> Failure {
Failure::permanent()
}
}
impl<E: Classify> Classify for super::GenerateError<E> {
fn failure(&self) -> Failure {
match self {
Self::Provider(error) => error.failure(),
Self::Parse { .. } => Failure::permanent(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn retryable_kinds() {
assert!(Failure::network().is_retryable());
assert!(Failure::transient().is_retryable());
assert!(!Failure::quota().is_retryable());
assert!(!Failure::permanent().is_retryable());
}
#[test]
fn retry_after_is_carried() {
let failure = Failure::transient().with_retry_after(Some(Duration::from_secs(3)));
assert_eq!(failure.retry_after, Some(Duration::from_secs(3)));
}
}