use std::sync::Arc;
use super::{Context, context::Extensions};
use crate::Service;
use rama_macros::paste;
use rama_utils::macros::all_the_tuples_no_last_special_case;
mod op_or;
#[doc(inline)]
pub use op_or::Or;
mod op_and;
#[doc(inline)]
pub use op_and::And;
mod op_not;
#[doc(inline)]
pub use op_not::Not;
mod mfn;
#[doc(inline)]
pub use mfn::{MatchFn, match_fn};
mod iter;
#[doc(inline)]
pub use iter::IteratorMatcherExt;
mod ext;
#[doc(inline)]
pub use ext::ExtensionMatcher;
pub trait Matcher<State, Request>: Send + Sync + 'static {
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool;
fn or<M>(self, other: M) -> impl Matcher<State, Request>
where
Self: Sized,
M: Matcher<State, Request>,
{
Or::new((self, other))
}
fn and<M>(self, other: M) -> impl Matcher<State, Request>
where
Self: Sized,
M: Matcher<State, Request>,
{
And::new((self, other))
}
fn not(self) -> impl Matcher<State, Request>
where
Self: Sized,
{
Not::new(self)
}
}
impl<State, Request, T> Matcher<State, Request> for Arc<T>
where
T: Matcher<State, Request>,
{
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool {
(**self).matches(ext, ctx, req)
}
}
impl<State, Request, T> Matcher<State, Request> for &'static T
where
T: Matcher<State, Request>,
{
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool {
(**self).matches(ext, ctx, req)
}
}
impl<State, Request, T> Matcher<State, Request> for Option<T>
where
T: Matcher<State, Request>,
{
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool {
match self {
Some(inner) => inner.matches(ext, ctx, req),
None => false,
}
}
}
impl<State, Request, T> Matcher<State, Request> for Box<T>
where
T: Matcher<State, Request>,
{
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool {
(**self).matches(ext, ctx, req)
}
}
impl<State, Request> Matcher<State, Request> for Box<(dyn Matcher<State, Request> + 'static)>
where
State: Clone + Send + Sync + 'static,
Request: Send + 'static,
{
fn matches(&self, ext: Option<&mut Extensions>, ctx: &Context<State>, req: &Request) -> bool {
(**self).matches(ext, ctx, req)
}
}
impl<State, Request> Matcher<State, Request> for bool {
fn matches(&self, _: Option<&mut Extensions>, _: &Context<State>, _: &Request) -> bool {
*self
}
}
macro_rules! impl_matcher_either {
($id:ident, $($param:ident),+ $(,)?) => {
impl<$($param),+, State, Request> Matcher<State, Request> for crate::combinators::$id<$($param),+>
where
$($param: Matcher<State, Request>),+,
Request: Send + 'static,
State: Clone + Send + Sync + 'static,
{
fn matches(
&self,
ext: Option<&mut Extensions>,
ctx: &Context<State>,
req: &Request
) -> bool{
match self {
$(
crate::combinators::$id::$param(layer) => layer.matches(ext, ctx, req),
)+
}
}
}
};
}
crate::combinators::impl_either!(impl_matcher_either);
pub struct MatcherRouter<N>(pub N);
impl<N: std::fmt::Debug> std::fmt::Debug for MatcherRouter<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("MatcherRouter").field(&self.0).finish()
}
}
impl<N: Clone> Clone for MatcherRouter<N> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
macro_rules! impl_matcher_service_tuple {
($($T:ident),+ $(,)?) => {
paste!{
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
impl<State, $([<M_ $T>], $T),+, S, Request, Response, Error> Service<State, Request> for MatcherRouter<($(([<M_ $T>], $T)),+, S)>
where
State: Clone + Send + Sync + 'static,
Request: Send + 'static,
Response: Send + 'static,
$(
[<M_ $T>]: Matcher<State, Request>,
$T: Service<State, Request, Response = Response, Error = Error>,
)+
S: Service<State, Request, Response = Response, Error = Error>,
Error: Send + Sync + 'static,
{
type Response = Response;
type Error = Error;
async fn serve(
&self,
mut ctx: Context<State>,
req: Request,
) -> Result<Self::Response, Self::Error> {
let ($(([<M_ $T>], $T)),+, S) = &self.0;
let mut ext = Extensions::new();
$(
if [<M_ $T>].matches(Some(&mut ext), &ctx, &req) {
ctx.extend(ext);
return $T.serve(ctx, req).await;
}
ext.clear();
)+
S.serve(ctx, req).await
}
}
}
};
}
all_the_tuples_no_last_special_case!(impl_matcher_service_tuple);
#[cfg(test)]
mod test;