use std::{error::Error as StdError, sync::Arc};
use http::{Method, StatusCode, Uri};
use super::{Req, Res};
use crate::error::BoxError;
pub trait Classify: Send + Sync + 'static {
fn classify(&self, req_rep: ReqRep<'_>) -> Action;
}
pub struct ClassifyFn<F>(pub(crate) F);
impl<F> Classify for ClassifyFn<F>
where
F: Fn(ReqRep<'_>) -> Action + Send + Sync + 'static,
{
fn classify(&self, req_rep: ReqRep<'_>) -> Action {
(self.0)(req_rep)
}
}
#[derive(Debug)]
pub struct ReqRep<'a>(&'a Req, Result<StatusCode, &'a BoxError>);
impl ReqRep<'_> {
pub fn method(&self) -> &Method {
self.0.method()
}
pub fn uri(&self) -> &Uri {
self.0.uri()
}
pub fn status(&self) -> Option<StatusCode> {
self.1.ok()
}
pub fn error(&self) -> Option<&(dyn StdError + 'static)> {
self.1.as_ref().err().map(|&e| &**e as _)
}
pub fn retryable(self) -> Action {
Action::Retryable
}
pub fn success(self) -> Action {
Action::Success
}
}
#[must_use]
pub enum Action {
Success,
Retryable,
}
#[derive(Clone)]
pub(crate) enum Classifier {
Never,
ProtocolNacks,
Dyn(Arc<dyn Classify>),
}
impl Classifier {
pub(super) fn classify(&mut self, req: &Req, res: &Result<Res, BoxError>) -> Action {
let req_rep = ReqRep(req, res.as_ref().map(|r| r.status()));
match self {
Classifier::Never => Action::Success,
Classifier::ProtocolNacks => {
let is_protocol_nack = req_rep
.error()
.map(super::is_retryable_error)
.unwrap_or(false);
if is_protocol_nack {
Action::Retryable
} else {
Action::Success
}
}
Classifier::Dyn(c) => c.classify(req_rep),
}
}
}