rama_http/matcher/
method.rs

1use crate::{Method, Request};
2use rama_core::{context::Extensions, Context};
3use std::{
4    fmt,
5    fmt::{Debug, Formatter},
6};
7
8/// A matcher that matches one or more HTTP methods.
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
10pub struct MethodMatcher(u16);
11
12impl MethodMatcher {
13    /// Match `CONNECT` requests.
14    pub const CONNECT: Self = Self::from_bits(0b0_0000_0001);
15    /// Match `DELETE` requests.
16    pub const DELETE: Self = Self::from_bits(0b0_0000_0010);
17    /// Match `GET` requests.
18    pub const GET: Self = Self::from_bits(0b0_0000_0100);
19    /// Match `HEAD` requests.
20    pub const HEAD: Self = Self::from_bits(0b0_0000_1000);
21    /// Match `OPTIONS` requests.
22    pub const OPTIONS: Self = Self::from_bits(0b0_0001_0000);
23    /// Match `PATCH` requests.
24    pub const PATCH: Self = Self::from_bits(0b0_0010_0000);
25    /// Match `POST` requests.
26    pub const POST: Self = Self::from_bits(0b0_0100_0000);
27    /// Match `PUT` requests.
28    pub const PUT: Self = Self::from_bits(0b0_1000_0000);
29    /// Match `TRACE` requests.
30    pub const TRACE: Self = Self::from_bits(0b1_0000_0000);
31
32    const fn bits(&self) -> u16 {
33        let bits = self;
34        bits.0
35    }
36
37    const fn from_bits(bits: u16) -> Self {
38        Self(bits)
39    }
40
41    pub(crate) const fn contains(&self, other: Self) -> bool {
42        self.bits() & other.bits() == other.bits()
43    }
44
45    /// Performs the OR operation between the [`MethodMatcher`] in `self` with `other`.
46    pub const fn or(self, other: Self) -> Self {
47        Self(self.0 | other.0)
48    }
49}
50
51impl<State, Body> rama_core::matcher::Matcher<State, Request<Body>> for MethodMatcher {
52    /// returns true on a match, false otherwise
53    fn matches(
54        &self,
55        _ext: Option<&mut Extensions>,
56        _ctx: &Context<State>,
57        req: &Request<Body>,
58    ) -> bool {
59        MethodMatcher::try_from(req.method())
60            .ok()
61            .map(|method| self.contains(method))
62            .unwrap_or_default()
63    }
64}
65
66/// Error type used when converting a [`Method`] to a [`MethodMatcher`] fails.
67#[derive(Debug)]
68pub struct NoMatchingMethodMatcher {
69    method: Method,
70}
71
72impl NoMatchingMethodMatcher {
73    /// Get the [`Method`] that couldn't be converted to a [`MethodMatcher`].
74    pub fn method(&self) -> &Method {
75        &self.method
76    }
77}
78
79impl fmt::Display for NoMatchingMethodMatcher {
80    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
81        write!(f, "no `MethodMatcher` for `{}`", self.method.as_str())
82    }
83}
84
85impl std::error::Error for NoMatchingMethodMatcher {}
86
87impl TryFrom<&Method> for MethodMatcher {
88    type Error = NoMatchingMethodMatcher;
89
90    fn try_from(m: &Method) -> Result<Self, Self::Error> {
91        match m {
92            &Method::CONNECT => Ok(MethodMatcher::CONNECT),
93            &Method::DELETE => Ok(MethodMatcher::DELETE),
94            &Method::GET => Ok(MethodMatcher::GET),
95            &Method::HEAD => Ok(MethodMatcher::HEAD),
96            &Method::OPTIONS => Ok(MethodMatcher::OPTIONS),
97            &Method::PATCH => Ok(MethodMatcher::PATCH),
98            &Method::POST => Ok(MethodMatcher::POST),
99            &Method::PUT => Ok(MethodMatcher::PUT),
100            &Method::TRACE => Ok(MethodMatcher::TRACE),
101            other => Err(Self::Error {
102                method: other.clone(),
103            }),
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn from_http_method() {
114        assert_eq!(
115            MethodMatcher::try_from(&Method::CONNECT).unwrap(),
116            MethodMatcher::CONNECT
117        );
118
119        assert_eq!(
120            MethodMatcher::try_from(&Method::DELETE).unwrap(),
121            MethodMatcher::DELETE
122        );
123
124        assert_eq!(
125            MethodMatcher::try_from(&Method::GET).unwrap(),
126            MethodMatcher::GET
127        );
128
129        assert_eq!(
130            MethodMatcher::try_from(&Method::HEAD).unwrap(),
131            MethodMatcher::HEAD
132        );
133
134        assert_eq!(
135            MethodMatcher::try_from(&Method::OPTIONS).unwrap(),
136            MethodMatcher::OPTIONS
137        );
138
139        assert_eq!(
140            MethodMatcher::try_from(&Method::PATCH).unwrap(),
141            MethodMatcher::PATCH
142        );
143
144        assert_eq!(
145            MethodMatcher::try_from(&Method::POST).unwrap(),
146            MethodMatcher::POST
147        );
148
149        assert_eq!(
150            MethodMatcher::try_from(&Method::PUT).unwrap(),
151            MethodMatcher::PUT
152        );
153
154        assert_eq!(
155            MethodMatcher::try_from(&Method::TRACE).unwrap(),
156            MethodMatcher::TRACE
157        );
158    }
159}