Skip to main content

topcoat_router/
methods.rs

1use std::borrow::Cow;
2
3use http::Method;
4
5/// The HTTP methods a [`Route`](crate::Route) responds to, as returned by
6/// [`Route::methods`](crate::Route::methods).
7///
8/// Most routes respond to a fixed set of methods, usually a single one.
9/// [`Methods::Any`] marks a route that accepts every method at its path, like
10/// an adapter forwarding requests to an external service. A route registered
11/// for a specific method takes precedence over an any-method route at the
12/// same path.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Methods<'a> {
15    /// The route accepts every HTTP method.
16    Any,
17    /// The route accepts exactly the listed methods.
18    Only(&'a [Method]),
19}
20
21/// An owned counterpart to [`Methods`], stored by routes like
22/// [`RouteFn`](crate::RouteFn).
23///
24/// Rarely constructed directly: [`RouteFn::new`](crate::RouteFn::new) accepts
25/// anything convertible into it, like a [`Method`], a `&'static [Method]`, a
26/// `Vec<Method>`, or a [`Methods`] value (so [`Methods::Any`] expresses an
27/// any-method route). The common single-method case is stored without
28/// allocating.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum OwnedMethods {
31    /// Responds to a single method.
32    One(Method),
33    /// Responds to each method in the set.
34    Set(Cow<'static, [Method]>),
35    /// Responds to every HTTP method.
36    Any,
37}
38
39impl OwnedMethods {
40    /// Borrows this set as a [`Methods`] value, as returned by
41    /// [`Route::methods`](crate::Route::methods).
42    #[must_use]
43    pub fn as_methods(&self) -> Methods<'_> {
44        match self {
45            Self::One(method) => Methods::Only(std::slice::from_ref(method)),
46            Self::Set(methods) => Methods::Only(methods),
47            Self::Any => Methods::Any,
48        }
49    }
50}
51
52impl From<Method> for OwnedMethods {
53    fn from(method: Method) -> Self {
54        Self::One(method)
55    }
56}
57
58impl From<&'static [Method]> for OwnedMethods {
59    fn from(methods: &'static [Method]) -> Self {
60        Self::Set(Cow::Borrowed(methods))
61    }
62}
63
64impl<const N: usize> From<&'static [Method; N]> for OwnedMethods {
65    fn from(methods: &'static [Method; N]) -> Self {
66        Self::Set(Cow::Borrowed(methods))
67    }
68}
69
70impl From<Vec<Method>> for OwnedMethods {
71    fn from(methods: Vec<Method>) -> Self {
72        Self::Set(Cow::Owned(methods))
73    }
74}
75
76impl From<Cow<'static, [Method]>> for OwnedMethods {
77    fn from(methods: Cow<'static, [Method]>) -> Self {
78        Self::Set(methods)
79    }
80}
81
82impl From<Methods<'static>> for OwnedMethods {
83    fn from(methods: Methods<'static>) -> Self {
84        match methods {
85            Methods::Any => Self::Any,
86            Methods::Only(methods) => Self::Set(Cow::Borrowed(methods)),
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn a_single_method_converts_without_allocating() {
97        let methods = OwnedMethods::from(Method::GET);
98        assert_eq!(methods, OwnedMethods::One(Method::GET));
99        assert_eq!(methods.as_methods(), Methods::Only(&[Method::GET]));
100    }
101
102    #[test]
103    fn slices_arrays_and_vectors_convert_to_sets() {
104        let expected = Methods::Only(&[Method::GET, Method::POST][..]);
105
106        let slice: &'static [Method] = &[Method::GET, Method::POST];
107        assert_eq!(OwnedMethods::from(slice).as_methods(), expected);
108        assert_eq!(
109            OwnedMethods::from(&[Method::GET, Method::POST]).as_methods(),
110            expected
111        );
112        assert_eq!(
113            OwnedMethods::from(vec![Method::GET, Method::POST]).as_methods(),
114            expected
115        );
116    }
117
118    #[test]
119    fn methods_values_convert_losslessly() {
120        assert_eq!(OwnedMethods::from(Methods::Any), OwnedMethods::Any);
121        assert_eq!(OwnedMethods::Any.as_methods(), Methods::Any);
122        assert_eq!(
123            OwnedMethods::from(Methods::Only(&[Method::PUT])).as_methods(),
124            Methods::Only(&[Method::PUT])
125        );
126    }
127}