use super::{body::Body, headers::Headers, request::Request};
use crate::request::Method;
use std::{borrow::Cow, cmp::Ordering};
pub trait Matcher: std::fmt::Debug + Send + Sync + 'static {
fn name(&self) -> &str;
fn matches(&self, req: &Request) -> bool;
}
impl PartialEq for dyn Matcher {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name()
}
}
impl Eq for dyn Matcher {}
impl PartialOrd for dyn Matcher {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for dyn Matcher {
fn cmp(&self, other: &Self) -> Ordering {
self.name().cmp(other.name())
}
}
#[derive(Debug)]
pub struct AnyMatcher;
impl Matcher for AnyMatcher {
fn name(&self) -> &str {
"any"
}
fn matches(&self, _req: &Request) -> bool {
true
}
}
pub fn any() -> AnyMatcher {
AnyMatcher
}
#[derive(Debug)]
pub struct MethodMatcher(Method);
impl Matcher for MethodMatcher {
fn name(&self) -> &str {
"method"
}
fn matches(&self, req: &Request) -> bool {
req.method == self.0
}
}
pub fn method(method: Method) -> MethodMatcher {
MethodMatcher(method)
}
#[derive(Debug)]
pub struct PathMatcher(String);
impl Matcher for PathMatcher {
fn name(&self) -> &str {
"path"
}
fn matches(&self, req: &Request) -> bool {
req.path() == self.0
}
}
pub fn path(path: impl Into<String>) -> PathMatcher {
PathMatcher(path.into())
}
#[derive(Debug)]
pub struct PathPrefixMatcher(String);
impl Matcher for PathPrefixMatcher {
fn name(&self) -> &str {
"path_prefix"
}
fn matches(&self, req: &Request) -> bool {
req.path().starts_with(&self.0)
}
}
pub fn path_prefix(prefix: impl Into<String>) -> PathPrefixMatcher {
PathPrefixMatcher(prefix.into())
}
#[derive(Debug)]
pub struct BodyMatcher(Body);
impl Matcher for BodyMatcher {
fn name(&self) -> &str {
"body"
}
fn matches(&self, req: &Request) -> bool {
self.0 == req.body
}
}
pub fn body(body: Body) -> BodyMatcher {
BodyMatcher(body)
}
#[derive(Debug)]
pub struct HeadersMatcher(Headers);
impl Matcher for HeadersMatcher {
fn name(&self) -> &str {
"headers"
}
fn matches(&self, req: &Request) -> bool {
req.headers.is_superset(&self.0)
}
}
pub fn headers(headers: Headers) -> HeadersMatcher {
HeadersMatcher(headers)
}
#[derive(Debug)]
pub struct HeadersExactMatcher(Headers);
impl Matcher for HeadersExactMatcher {
fn name(&self) -> &str {
"headers_exact"
}
fn matches(&self, req: &Request) -> bool {
req.headers == self.0
}
}
pub fn headers_exact(headers: Headers) -> HeadersExactMatcher {
HeadersExactMatcher(headers)
}
#[derive(Debug)]
pub struct HeaderMatcher(String, String);
impl Matcher for HeaderMatcher {
fn name(&self) -> &str {
"header"
}
fn matches(&self, req: &Request) -> bool {
req.headers.contains(&self.0, &self.1)
}
}
pub fn header(name: impl Into<String>, value: impl Into<String>) -> HeaderMatcher {
HeaderMatcher(name.into(), value.into())
}
#[derive(Debug)]
pub struct HeaderExistsMatcher(String);
impl Matcher for HeaderExistsMatcher {
fn name(&self) -> &str {
"header_exists"
}
fn matches(&self, req: &Request) -> bool {
req.headers.contains_name(&self.0)
}
}
pub fn header_exists(name: impl Into<String>) -> HeaderExistsMatcher {
HeaderExistsMatcher(name.into())
}
#[derive(Debug)]
pub struct QueryParamsMatcher(Vec<(Cow<'static, str>, Cow<'static, str>)>);
impl Matcher for QueryParamsMatcher {
fn name(&self) -> &str {
"query_params"
}
fn matches(&self, req: &Request) -> bool {
let pairs = req.query_pairs().collect::<Vec<_>>();
pairs == self.0
}
}
pub fn query_params(
pairs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> QueryParamsMatcher {
let pairs = pairs
.into_iter()
.map(|(key, value)| (Cow::from(key.into()), Cow::from(value.into())))
.collect::<Vec<_>>();
QueryParamsMatcher(pairs)
}
#[derive(Debug)]
pub struct QueryParamMatcher(String, String);
impl Matcher for QueryParamMatcher {
fn name(&self) -> &str {
"query_param"
}
fn matches(&self, req: &Request) -> bool {
req.query_pairs()
.any(|(key, value)| key == self.0 && value == self.1)
}
}
pub fn query_param(key: impl Into<String>, value: impl Into<String>) -> QueryParamMatcher {
QueryParamMatcher(key.into(), value.into())
}
#[derive(Debug)]
pub struct QueryParamExistsMatcher(String);
impl Matcher for QueryParamExistsMatcher {
fn name(&self) -> &str {
"query_param_exists"
}
fn matches(&self, req: &Request) -> bool {
req.query_pairs().any(|(key, _)| key == self.0)
}
}
pub fn query_param_exists(key: impl Into<String>) -> QueryParamExistsMatcher {
QueryParamExistsMatcher(key.into())
}