use crate::{
fail::{Fail, Fault},
lane::{Denied, Fatal, FatalKind, Transient, TransientKind},
};
fn lane_table(e: &::reqwest::Error) -> Fault<crate::lanes!(Denied, Transient, Fatal)> {
if e.is_timeout() {
return Transient::new(TransientKind::UpstreamUnavailable).into();
}
if e.is_connect() {
return Transient::new(TransientKind::ConnectionLost).into();
}
if let Some(status) = e.status() {
if status.as_u16() == 429 {
return Transient::new(TransientKind::Congestion).into();
}
if status.is_server_error() {
return Transient::new(TransientKind::UpstreamUnavailable).into();
}
if status.as_u16() == 401 || status.as_u16() == 403 {
return Denied::default().into();
}
if status.is_client_error() {
return Fatal::new(FatalKind::Invariant).into();
}
}
if e.is_builder() || e.is_request() {
return Fatal::new(FatalKind::Invariant).into();
}
Fatal::new(FatalKind::Dependency).into()
}
fn classify_reqwest_fault(e: ::reqwest::Error) -> Fault<crate::lanes!(Denied, Transient, Fatal)> {
match lane_table(&e) {
Fault::Denied(d) => d.with_source(e).into(),
Fault::Transient(t) => t.with_source(e).into(),
Fault::Fatal(f) => f.with_source(e).into(),
}
}
pub(crate) fn classify_reqwest_ref(
e: &::reqwest::Error,
) -> Fault<crate::lanes!(Denied, Transient, Fatal)> {
match lane_table(e) {
Fault::Denied(d) => d.with_context(e.to_string()).into(),
Fault::Transient(t) => t.with_context(e.to_string()).into(),
Fault::Fatal(f) => f.with_context(e.to_string()).into(),
}
}
impl crate::Classify for ::reqwest::Error {
type Rejected = core::convert::Infallible;
type Lanes = crate::lanes!(Denied, Transient, Fatal);
fn classify(self) -> Fail<Self::Rejected, Self::Lanes> {
match classify_reqwest_fault(self) {
Fault::Denied(d) => Fail::Denied(d),
Fault::Transient(t) => Fail::Transient(t),
Fault::Fatal(x) => Fail::Fatal(x),
}
}
}
#[cfg(test)]
mod tests {
use crate::lane::Lane;
fn lane_for_status(code: u16) -> Lane {
let status = ::reqwest::StatusCode::from_u16(code).unwrap();
if status.as_u16() == 429 || status.is_server_error() {
Lane::Transient
} else if status.as_u16() == 401 || status.as_u16() == 403 {
Lane::Denied
} else {
Lane::Fatal
}
}
#[test]
fn server_errors_are_transient() {
assert_eq!(lane_for_status(500), Lane::Transient);
assert_eq!(lane_for_status(503), Lane::Transient);
}
#[test]
fn too_many_requests_is_transient_congestion() {
assert_eq!(lane_for_status(429), Lane::Transient);
}
#[test]
fn unauthorized_and_forbidden_are_denied() {
assert_eq!(lane_for_status(401), Lane::Denied);
assert_eq!(lane_for_status(403), Lane::Denied);
}
#[test]
fn other_client_errors_are_fatal_invariant() {
assert_eq!(lane_for_status(404), Lane::Fatal);
}
}