use std::convert::Infallible;
use std::time::Duration;
use crate::crawler::Tag;
use crate::{BoxError, Error};
#[must_use]
#[derive(Debug, Default)]
pub enum Outcome {
#[default]
Continue,
Retry {
after: Option<Duration>,
},
Backoff {
scope: Scope,
duration: Duration,
},
Abort(Scope),
Stop,
Fail(Error),
}
impl Outcome {
pub const fn retry() -> Self {
Self::Retry { after: None }
}
pub const fn retry_after(delay: Duration) -> Self {
Self::Retry { after: Some(delay) }
}
pub const fn backoff(duration: Duration) -> Self {
Self::Backoff {
scope: Scope::SameTag,
duration,
}
}
pub const fn abort() -> Self {
Self::Abort(Scope::SameTag)
}
pub fn abort_tag(tag: impl Into<Tag>) -> Self {
Self::Abort(Scope::Tag(tag.into()))
}
pub fn backoff_tag(tag: impl Into<Tag>, duration: Duration) -> Self {
Self::Backoff {
scope: Scope::Tag(tag.into()),
duration,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum Scope {
#[default]
SameTag,
Tag(Tag),
}
pub trait IntoOutcome {
fn into_outcome(self) -> Outcome;
}
impl IntoOutcome for Outcome {
fn into_outcome(self) -> Outcome {
self
}
}
impl IntoOutcome for () {
fn into_outcome(self) -> Outcome {
Outcome::Continue
}
}
impl IntoOutcome for Infallible {
fn into_outcome(self) -> Outcome {
match self {}
}
}
impl IntoOutcome for Error {
fn into_outcome(self) -> Outcome {
Outcome::Fail(self)
}
}
impl IntoOutcome for BoxError {
fn into_outcome(self) -> Outcome {
Outcome::Fail(Error::from_boxed(crate::ErrorKind::Worker, self))
}
}
impl<T> IntoOutcome for Option<T>
where
T: IntoOutcome,
{
fn into_outcome(self) -> Outcome {
self.map_or(Outcome::Continue, IntoOutcome::into_outcome)
}
}
impl<T, E> IntoOutcome for Result<T, E>
where
T: IntoOutcome,
E: IntoOutcome,
{
fn into_outcome(self) -> Outcome {
match self {
Ok(value) => value.into_outcome(),
Err(error) => error.into_outcome(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ErrorKind, Result};
#[test]
fn conversions() {
assert!(matches!(().into_outcome(), Outcome::Continue));
assert!(matches!(None::<Outcome>.into_outcome(), Outcome::Continue));
let ok: Result<()> = Ok(());
assert!(matches!(ok.into_outcome(), Outcome::Continue));
let err: Result<()> = Err(Error::new(ErrorKind::Http));
assert!(matches!(err.into_outcome(), Outcome::Fail(_)));
let retry: Result<(), Outcome> = Err(Outcome::retry());
assert!(matches!(
retry.into_outcome(),
Outcome::Retry { after: None }
));
}
}