use crate::{HeaderMap, Request, Response, StatusCode};
use std::{convert::Infallible, fmt, marker::PhantomData};
pub(crate) mod grpc_errors_as_failures;
mod map_failure_class;
mod status_in_range_is_error;
pub use self::{
grpc_errors_as_failures::{
GrpcCode, GrpcEosErrorsAsFailures, GrpcErrorsAsFailures, GrpcFailureClass,
},
map_failure_class::MapFailureClass,
status_in_range_is_error::{StatusInRangeAsFailures, StatusInRangeFailureClass},
};
pub trait MakeClassifier: Send + Sync + 'static {
type Classifier: ClassifyResponse<FailureClass = Self::FailureClass, ClassifyEos = Self::ClassifyEos>;
type FailureClass: Send + Sync + 'static;
type ClassifyEos: ClassifyEos<FailureClass = Self::FailureClass> + Send + Sync + 'static;
fn make_classifier<B>(&self, req: &Request<B>) -> Self::Classifier;
}
#[derive(Debug, Clone)]
pub struct SharedClassifier<C> {
classifier: C,
}
impl<C> SharedClassifier<C> {
pub const fn new(classifier: C) -> Self
where
C: ClassifyResponse + Clone,
{
Self { classifier }
}
}
impl<C> MakeClassifier for SharedClassifier<C>
where
C: ClassifyResponse + Clone,
{
type FailureClass = C::FailureClass;
type ClassifyEos = C::ClassifyEos;
type Classifier = C;
fn make_classifier<B>(&self, _req: &Request<B>) -> Self::Classifier {
self.classifier.clone()
}
}
pub trait ClassifyResponse: Send + Sync + 'static {
type FailureClass: Send + Sync + 'static;
type ClassifyEos: ClassifyEos<FailureClass = Self::FailureClass> + Send + Sync + 'static;
fn classify_response<B>(
self,
res: &Response<B>,
) -> ClassifiedResponse<Self::FailureClass, Self::ClassifyEos>;
fn classify_error<E>(self, error: &E) -> Self::FailureClass
where
E: fmt::Display;
fn map_failure_class<F, NewClass>(self, f: F) -> MapFailureClass<Self, F>
where
Self: Sized,
F: FnOnce(Self::FailureClass) -> NewClass,
{
MapFailureClass::new(self, f)
}
}
pub trait ClassifyEos {
type FailureClass;
fn classify_eos(self, trailers: Option<&HeaderMap>) -> Result<(), Self::FailureClass>;
fn classify_error<E>(self, error: &E) -> Self::FailureClass
where
E: fmt::Display;
fn map_failure_class<F, NewClass>(self, f: F) -> MapFailureClass<Self, F>
where
Self: Sized,
F: FnOnce(Self::FailureClass) -> NewClass,
{
MapFailureClass::new(self, f)
}
}
#[derive(Debug)]
pub enum ClassifiedResponse<FailureClass, ClassifyEos> {
Ready(Result<(), FailureClass>),
RequiresEos(ClassifyEos),
}
pub struct NeverClassifyEos<T> {
_output_ty: PhantomData<fn() -> T>,
_never: Infallible,
}
impl<T> ClassifyEos for NeverClassifyEos<T> {
type FailureClass = T;
fn classify_eos(self, _trailers: Option<&HeaderMap>) -> Result<(), Self::FailureClass> {
unreachable!()
}
fn classify_error<E>(self, _error: &E) -> Self::FailureClass
where
E: fmt::Display,
{
unreachable!()
}
}
impl<T> fmt::Debug for NeverClassifyEos<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NeverClassifyEos").finish()
}
}
#[derive(Clone, Debug, Default)]
pub struct ServerErrorsAsFailures {
_priv: (),
}
impl ServerErrorsAsFailures {
pub fn new() -> Self {
Self::default()
}
pub fn make_classifier() -> SharedClassifier<Self> {
SharedClassifier::new(Self::new())
}
}
impl ClassifyResponse for ServerErrorsAsFailures {
type FailureClass = ServerErrorsFailureClass;
type ClassifyEos = NeverClassifyEos<ServerErrorsFailureClass>;
fn classify_response<B>(
self,
res: &Response<B>,
) -> ClassifiedResponse<Self::FailureClass, Self::ClassifyEos> {
if res.status().is_server_error() {
ClassifiedResponse::Ready(Err(ServerErrorsFailureClass::StatusCode(res.status())))
} else {
ClassifiedResponse::Ready(Ok(()))
}
}
fn classify_error<E>(self, error: &E) -> Self::FailureClass
where
E: fmt::Display,
{
ServerErrorsFailureClass::Error(error.to_string())
}
}
#[derive(Debug)]
pub enum ServerErrorsFailureClass {
StatusCode(StatusCode),
Error(String),
}
impl fmt::Display for ServerErrorsFailureClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::StatusCode(code) => write!(f, "Status code: {}", code),
Self::Error(error) => write!(f, "Error: {}", error),
}
}
}