rocket_http/
method.rs

1use std::fmt;
2use std::str::FromStr;
3
4use self::Method::*;
5
6use crate::hyper;
7
8// TODO: Support non-standard methods, here and in codegen?
9
10/// Representation of HTTP methods.
11///
12/// # (De)serialization
13///
14/// `Method` is both `Serialize` and `Deserialize`, represented as an
15/// [uncased](crate::uncased) string. For example, [`Method::Get`] serializes to
16/// `"GET"` and deserializes from any casing of `"GET"` including `"get"`,
17/// `"GeT"`, and `"GET"`.
18///
19/// ```rust
20/// # #[cfg(feature = "serde")] mod serde {
21/// # use serde_ as serde;
22/// use serde::{Serialize, Deserialize};
23/// use rocket::http::Method;
24///
25/// #[derive(Deserialize, Serialize)]
26/// # #[serde(crate = "serde_")]
27/// struct Foo {
28///     method: Method,
29/// }
30/// # }
31/// ```
32#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
33pub enum Method {
34    /// The `GET` variant.
35    Get,
36    /// The `PUT` variant.
37    Put,
38    /// The `POST` variant.
39    Post,
40    /// The `DELETE` variant.
41    Delete,
42    /// The `OPTIONS` variant.
43    Options,
44    /// The `HEAD` variant.
45    Head,
46    /// The `TRACE` variant.
47    Trace,
48    /// The `CONNECT` variant.
49    Connect,
50    /// The `PATCH` variant.
51    Patch
52}
53
54impl Method {
55    /// WARNING: This is unstable! Do not use this method outside of Rocket!
56    #[doc(hidden)]
57    pub fn from_hyp(method: &hyper::Method) -> Option<Method> {
58        match *method {
59            hyper::Method::GET => Some(Get),
60            hyper::Method::PUT => Some(Put),
61            hyper::Method::POST => Some(Post),
62            hyper::Method::DELETE => Some(Delete),
63            hyper::Method::OPTIONS => Some(Options),
64            hyper::Method::HEAD => Some(Head),
65            hyper::Method::TRACE => Some(Trace),
66            hyper::Method::CONNECT => Some(Connect),
67            hyper::Method::PATCH => Some(Patch),
68            _ => None,
69        }
70    }
71
72    /// Returns `true` if an HTTP request with the method represented by `self`
73    /// always supports a payload.
74    ///
75    /// The following methods always support payloads:
76    ///
77    ///   * `PUT`, `POST`, `DELETE`, `PATCH`
78    ///
79    /// The following methods _do not_ always support payloads:
80    ///
81    ///   * `GET`, `HEAD`, `CONNECT`, `TRACE`, `OPTIONS`
82    ///
83    /// # Example
84    ///
85    /// ```rust
86    /// # extern crate rocket;
87    /// use rocket::http::Method;
88    ///
89    /// assert_eq!(Method::Get.supports_payload(), false);
90    /// assert_eq!(Method::Post.supports_payload(), true);
91    /// ```
92    #[inline]
93    pub fn supports_payload(self) -> bool {
94        match self {
95            Put | Post | Delete | Patch => true,
96            Get | Head | Connect | Trace | Options => false,
97        }
98    }
99
100    /// Returns the string representation of `self`.
101    ///
102    /// # Example
103    ///
104    /// ```rust
105    /// # extern crate rocket;
106    /// use rocket::http::Method;
107    ///
108    /// assert_eq!(Method::Get.as_str(), "GET");
109    /// ```
110    #[inline]
111    pub fn as_str(self) -> &'static str {
112        match self {
113            Get => "GET",
114            Put => "PUT",
115            Post => "POST",
116            Delete => "DELETE",
117            Options => "OPTIONS",
118            Head => "HEAD",
119            Trace => "TRACE",
120            Connect => "CONNECT",
121            Patch => "PATCH",
122        }
123    }
124}
125
126impl FromStr for Method {
127    type Err = ();
128
129    // According to the RFC, method names are case-sensitive. But some old
130    // clients don't follow this, so we just do a case-insensitive match here.
131    fn from_str(s: &str) -> Result<Method, ()> {
132        match s {
133            x if uncased::eq(x, Get.as_str()) => Ok(Get),
134            x if uncased::eq(x, Put.as_str()) => Ok(Put),
135            x if uncased::eq(x, Post.as_str()) => Ok(Post),
136            x if uncased::eq(x, Delete.as_str()) => Ok(Delete),
137            x if uncased::eq(x, Options.as_str()) => Ok(Options),
138            x if uncased::eq(x, Head.as_str()) => Ok(Head),
139            x if uncased::eq(x, Trace.as_str()) => Ok(Trace),
140            x if uncased::eq(x, Connect.as_str()) => Ok(Connect),
141            x if uncased::eq(x, Patch.as_str()) => Ok(Patch),
142            _ => Err(()),
143        }
144    }
145}
146
147impl fmt::Display for Method {
148    #[inline(always)]
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        self.as_str().fmt(f)
151    }
152}
153
154#[cfg(feature = "serde")]
155mod serde {
156    use std::fmt;
157    use super::*;
158
159    use serde_::ser::{Serialize, Serializer};
160    use serde_::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
161
162    impl<'a> Serialize for Method {
163        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
164            serializer.serialize_str(self.as_str())
165        }
166    }
167
168    struct DeVisitor;
169
170    impl<'de> Visitor<'de> for DeVisitor {
171        type Value = Method;
172
173        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
174            write!(formatter, "valid HTTP method string")
175        }
176
177        fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
178            Method::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
179        }
180    }
181
182    impl<'de> Deserialize<'de> for Method {
183        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
184            deserializer.deserialize_str(DeVisitor)
185        }
186    }
187}