gotham/router/route/matcher/
mod.rs

1//! Defines the type `RouteMatcher` and default implementations.
2
3mod accept;
4mod access_control_request_method;
5mod and;
6mod any;
7mod content_type;
8
9pub use self::accept::AcceptHeaderRouteMatcher;
10pub use self::access_control_request_method::AccessControlRequestMethodMatcher;
11pub use self::and::AndRouteMatcher;
12pub use self::any::AnyRouteMatcher;
13pub use self::content_type::ContentTypeHeaderRouteMatcher;
14
15mod lookup_table;
16use self::lookup_table::{LookupTable, LookupTableFromTypes};
17
18use std::panic::RefUnwindSafe;
19
20use hyper::{Method, StatusCode};
21use log::trace;
22
23use crate::router::non_match::RouteNonMatch;
24use crate::state::{request_id, FromState, State};
25
26/// Determines if conditions required for the associated `Route` to be invoked by the `Router` have
27/// been met.
28pub trait RouteMatcher: RefUnwindSafe + Clone {
29    /// Determines if the `Request` meets pre-defined conditions.
30    fn is_match(&self, state: &State) -> Result<(), RouteNonMatch>;
31}
32
33/// Allow various types to represent themselves as a `RouteMatcher`
34pub trait IntoRouteMatcher {
35    /// The concrete RouteMatcher each implementation will provide.
36    type Output: RouteMatcher;
37
38    /// Transform into a `RouteMatcher` of the the associated type identified by `Output`.
39    fn into_route_matcher(self) -> Self::Output;
40}
41
42impl IntoRouteMatcher for Vec<Method> {
43    type Output = MethodOnlyRouteMatcher;
44
45    fn into_route_matcher(self) -> Self::Output {
46        MethodOnlyRouteMatcher::new(self)
47    }
48}
49
50impl<M> IntoRouteMatcher for M
51where
52    M: RouteMatcher + Send + Sync + 'static,
53{
54    type Output = M;
55
56    fn into_route_matcher(self) -> Self::Output {
57        self
58    }
59}
60
61/// A `RouteMatcher` that succeeds when the `Request` has been made with an accepted HTTP request
62/// method.
63///
64/// # Examples
65///
66/// ```rust
67/// # extern crate gotham;
68/// # extern crate hyper;
69/// # fn main() {
70/// #   use hyper::Method;
71/// #   use gotham::state::State;
72/// #   use gotham::router::route::matcher::{RouteMatcher, MethodOnlyRouteMatcher};
73/// #
74/// #   State::with_new(|state| {
75/// #
76/// let methods = vec![Method::GET, Method::HEAD];
77/// let matcher = MethodOnlyRouteMatcher::new(methods);
78///
79/// state.put(Method::GET);
80/// assert!(matcher.is_match(&state).is_ok());
81///
82/// state.put(Method::POST);
83/// assert!(matcher.is_match(&state).is_err());
84/// #   });
85/// # }
86/// ```
87#[derive(Clone)]
88pub struct MethodOnlyRouteMatcher {
89    methods: Vec<Method>,
90}
91
92impl MethodOnlyRouteMatcher {
93    /// Creates a new `MethodOnlyRouteMatcher`.
94    pub fn new(methods: Vec<Method>) -> Self {
95        MethodOnlyRouteMatcher { methods }
96    }
97}
98
99impl RouteMatcher for MethodOnlyRouteMatcher {
100    /// Determines if the `Request` was made using a `Method` the instance contains.
101    fn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {
102        let method = Method::borrow_from(state);
103        if self.methods.iter().any(|m| m == method) {
104            trace!(
105                "[{}] matched request method {} to permitted method",
106                request_id(state),
107                method
108            );
109            Ok(())
110        } else {
111            trace!(
112                "[{}] did not match request method {}",
113                request_id(state),
114                method
115            );
116            Err(RouteNonMatch::new(StatusCode::METHOD_NOT_ALLOWED)
117                .with_allow_list(self.methods.as_slice()))
118        }
119    }
120}