use crate::{Method, Request};
use rama_core::extensions::Extensions;
use std::{
fmt,
fmt::{Debug, Formatter},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MethodMatcher(u16);
impl MethodMatcher {
pub const CONNECT: Self = Self::from_bits(0b0_0000_0001);
pub const DELETE: Self = Self::from_bits(0b0_0000_0010);
pub const GET: Self = Self::from_bits(0b0_0000_0100);
pub const HEAD: Self = Self::from_bits(0b0_0000_1000);
pub const OPTIONS: Self = Self::from_bits(0b0_0001_0000);
pub const PATCH: Self = Self::from_bits(0b0_0010_0000);
pub const POST: Self = Self::from_bits(0b0_0100_0000);
pub const PUT: Self = Self::from_bits(0b0_1000_0000);
pub const QUERY: Self = Self::from_bits(0b1_0000_0000);
pub const TRACE: Self = Self::from_bits(0b10_0000_0000);
const fn bits(self) -> u16 {
let bits = self;
bits.0
}
const fn from_bits(bits: u16) -> Self {
Self(bits)
}
pub(crate) const NONE: Self = Self::from_bits(0);
pub(crate) const ALL_KNOWN: Self = Self::CONNECT
.or_method(Self::DELETE)
.or_method(Self::GET)
.or_method(Self::HEAD)
.or_method(Self::OPTIONS)
.or_method(Self::PATCH)
.or_method(Self::POST)
.or_method(Self::PUT)
.or_method(Self::QUERY)
.or_method(Self::TRACE);
pub const fn contains(self, other: Self) -> bool {
self.bits() & other.bits() == other.bits()
}
#[must_use]
pub const fn or_method(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn and_method(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[must_use]
pub const fn complement(self) -> Self {
Self(Self::ALL_KNOWN.bits() & !self.bits())
}
pub fn iter(self) -> impl Iterator<Item = Method> {
[
(Self::CONNECT, Method::CONNECT),
(Self::DELETE, Method::DELETE),
(Self::GET, Method::GET),
(Self::HEAD, Method::HEAD),
(Self::OPTIONS, Method::OPTIONS),
(Self::PATCH, Method::PATCH),
(Self::POST, Method::POST),
(Self::PUT, Method::PUT),
(Self::QUERY, Method::QUERY),
(Self::TRACE, Method::TRACE),
]
.into_iter()
.filter_map(move |(m, method)| self.contains(m).then_some(method))
}
}
impl<Body> rama_core::matcher::Matcher<Request<Body>> for MethodMatcher {
fn matches(&self, _ext: Option<&Extensions>, req: &Request<Body>) -> bool {
Self::try_from(req.method())
.ok()
.map(|method| self.contains(method))
.unwrap_or_default()
}
}
#[derive(Debug)]
pub struct NoMatchingMethodMatcher {
method: Method,
}
impl NoMatchingMethodMatcher {
pub fn method(&self) -> &Method {
&self.method
}
}
impl fmt::Display for NoMatchingMethodMatcher {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "no `MethodMatcher` for `{}`", self.method.as_str())
}
}
impl std::error::Error for NoMatchingMethodMatcher {}
impl TryFrom<&Method> for MethodMatcher {
type Error = NoMatchingMethodMatcher;
fn try_from(m: &Method) -> Result<Self, Self::Error> {
match m {
&Method::CONNECT => Ok(Self::CONNECT),
&Method::DELETE => Ok(Self::DELETE),
&Method::GET => Ok(Self::GET),
&Method::HEAD => Ok(Self::HEAD),
&Method::OPTIONS => Ok(Self::OPTIONS),
&Method::PATCH => Ok(Self::PATCH),
&Method::POST => Ok(Self::POST),
&Method::PUT => Ok(Self::PUT),
&Method::QUERY => Ok(Self::QUERY),
&Method::TRACE => Ok(Self::TRACE),
other => Err(Self::Error {
method: other.clone(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_http_method() {
assert_eq!(
MethodMatcher::try_from(&Method::CONNECT).unwrap(),
MethodMatcher::CONNECT
);
assert_eq!(
MethodMatcher::try_from(&Method::DELETE).unwrap(),
MethodMatcher::DELETE
);
assert_eq!(
MethodMatcher::try_from(&Method::GET).unwrap(),
MethodMatcher::GET
);
assert_eq!(
MethodMatcher::try_from(&Method::HEAD).unwrap(),
MethodMatcher::HEAD
);
assert_eq!(
MethodMatcher::try_from(&Method::OPTIONS).unwrap(),
MethodMatcher::OPTIONS
);
assert_eq!(
MethodMatcher::try_from(&Method::PATCH).unwrap(),
MethodMatcher::PATCH
);
assert_eq!(
MethodMatcher::try_from(&Method::POST).unwrap(),
MethodMatcher::POST
);
assert_eq!(
MethodMatcher::try_from(&Method::PUT).unwrap(),
MethodMatcher::PUT
);
assert_eq!(
MethodMatcher::try_from(&Method::QUERY).unwrap(),
MethodMatcher::QUERY
);
assert_eq!(
MethodMatcher::try_from(&Method::TRACE).unwrap(),
MethodMatcher::TRACE
);
}
#[test]
fn query_is_a_known_method() {
assert!(MethodMatcher::ALL_KNOWN.contains(MethodMatcher::QUERY));
let methods: Vec<_> = MethodMatcher::QUERY.iter().collect();
assert_eq!(methods, vec![Method::QUERY]);
assert!(MethodMatcher::ALL_KNOWN.iter().any(|m| m == Method::QUERY));
}
}