#[cfg(feature = "alloc")]
use crate::compat::Box;
use core::ops::{BitAnd, BitOr};
pub trait Predicate<T, E> {
fn should_retry(&self, outcome: &Result<T, E>) -> bool;
#[must_use]
fn or<P: Predicate<T, E>>(self, other: P) -> PredicateAny<Self, P>
where
Self: Sized,
{
PredicateAny::new(self, other)
}
#[must_use]
fn and<P: Predicate<T, E>>(self, other: P) -> PredicateAll<Self, P>
where
Self: Sized,
{
PredicateAll::new(self, other)
}
}
impl<T, E, F> Predicate<T, E> for F
where
F: Fn(&Result<T, E>) -> bool,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
(self)(outcome)
}
}
#[cfg(feature = "alloc")]
impl<T, E> Predicate<T, E> for Box<dyn Predicate<T, E> + '_> {
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
(**self).should_retry(outcome)
}
}
#[cfg(feature = "alloc")]
impl<T, E> Predicate<T, E> for Box<dyn Predicate<T, E> + Send + '_> {
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
(**self).should_retry(outcome)
}
}
#[cfg(feature = "alloc")]
impl<T, E> Predicate<T, E> for Box<dyn Predicate<T, E> + Send + Sync + '_> {
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
(**self).should_retry(outcome)
}
}
#[derive(Debug, Clone)]
pub struct PredicateAnyError;
#[must_use]
pub fn any_error() -> PredicateAnyError {
PredicateAnyError
}
impl<T, E> Predicate<T, E> for PredicateAnyError {
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
outcome.is_err()
}
}
#[derive(Debug, Clone)]
pub struct PredicateError<F> {
matcher: F,
}
#[must_use]
pub fn error<F>(matcher: F) -> PredicateError<F> {
PredicateError { matcher }
}
impl<T, E, F> Predicate<T, E> for PredicateError<F>
where
F: Fn(&E) -> bool,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
match outcome {
Ok(_) => false,
Err(error) => (self.matcher)(error),
}
}
}
#[derive(Debug, Clone)]
pub struct PredicateResult<F> {
matcher: F,
}
#[must_use]
pub fn result<F>(matcher: F) -> PredicateResult<F> {
PredicateResult { matcher }
}
impl<T, E, F> Predicate<T, E> for PredicateResult<F>
where
F: Fn(&Result<T, E>) -> bool,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
(self.matcher)(outcome)
}
}
#[derive(Debug, Clone)]
pub struct PredicateOk<F> {
matcher: F,
}
#[must_use]
pub fn ok<F>(matcher: F) -> PredicateOk<F> {
PredicateOk { matcher }
}
impl<T, E, F> Predicate<T, E> for PredicateOk<F>
where
F: Fn(&T) -> bool,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
match outcome {
Ok(value) => (self.matcher)(value),
Err(_) => false,
}
}
}
#[derive(Debug, Clone)]
pub struct PredicateUntil<P> {
inner: P,
}
#[must_use]
pub fn until<P>(inner: P) -> PredicateUntil<P> {
PredicateUntil { inner }
}
impl<T, E, P> Predicate<T, E> for PredicateUntil<P>
where
P: Predicate<T, E>,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
!self.inner.should_retry(outcome)
}
}
#[derive(Debug, Clone)]
pub struct PredicateAny<A, B> {
left: A,
right: B,
}
impl<A, B> PredicateAny<A, B> {
#[must_use]
pub fn new(left: A, right: B) -> Self {
Self { left, right }
}
}
impl<T, E, A, B> Predicate<T, E> for PredicateAny<A, B>
where
A: Predicate<T, E>,
B: Predicate<T, E>,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
self.left.should_retry(outcome) || self.right.should_retry(outcome)
}
}
#[derive(Debug, Clone)]
pub struct PredicateAll<A, B> {
left: A,
right: B,
}
impl<A, B> PredicateAll<A, B> {
#[must_use]
pub fn new(left: A, right: B) -> Self {
Self { left, right }
}
}
impl<T, E, A, B> Predicate<T, E> for PredicateAll<A, B>
where
A: Predicate<T, E>,
B: Predicate<T, E>,
{
fn should_retry(&self, outcome: &Result<T, E>) -> bool {
self.left.should_retry(outcome) && self.right.should_retry(outcome)
}
}
macro_rules! impl_predicate_ops {
($ty:ty $(, $param:ident)*) => {
impl<$($param,)* Rhs> BitOr<Rhs> for $ty {
type Output = PredicateAny<Self, Rhs>;
fn bitor(self, rhs: Rhs) -> Self::Output {
PredicateAny::new(self, rhs)
}
}
impl<$($param,)* Rhs> BitAnd<Rhs> for $ty {
type Output = PredicateAll<Self, Rhs>;
fn bitand(self, rhs: Rhs) -> Self::Output {
PredicateAll::new(self, rhs)
}
}
};
}
impl_predicate_ops!(PredicateAnyError);
impl_predicate_ops!(PredicateError<F>, F);
impl_predicate_ops!(PredicateResult<F>, F);
impl_predicate_ops!(PredicateOk<F>, F);
impl_predicate_ops!(PredicateAny<A, B>, A, B);
impl_predicate_ops!(PredicateAll<A, B>, A, B);
impl_predicate_ops!(PredicateUntil<P>, P);