hyper_old_types/
method.rs

1//! The HTTP request method
2use std::fmt;
3use std::str::FromStr;
4use std::convert::AsRef;
5
6#[cfg(feature = "compat")]
7use http;
8
9use error::Error;
10use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
11                   Extension};
12
13
14/// The Request Method (VERB)
15///
16/// Currently includes 8 variants representing the 8 methods defined in
17/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
18/// and an Extension variant for all extensions.
19///
20/// It may make sense to grow this to include all variants currently
21/// registered with IANA, if they are at all common to use.
22#[derive(Clone, PartialEq, Eq, Hash, Debug)]
23pub enum Method {
24    /// OPTIONS
25    Options,
26    /// GET
27    Get,
28    /// POST
29    Post,
30    /// PUT
31    Put,
32    /// DELETE
33    Delete,
34    /// HEAD
35    Head,
36    /// TRACE
37    Trace,
38    /// CONNECT
39    Connect,
40    /// PATCH
41    Patch,
42    /// Method extensions. An example would be `let m = Extension("FOO".to_string())`.
43    Extension(String)
44}
45
46impl AsRef<str> for Method {
47    fn as_ref(&self) -> &str {
48        match *self {
49            Options => "OPTIONS",
50            Get => "GET",
51            Post => "POST",
52            Put => "PUT",
53            Delete => "DELETE",
54            Head => "HEAD",
55            Trace => "TRACE",
56            Connect => "CONNECT",
57            Patch => "PATCH",
58            Extension(ref s) => s.as_ref()
59        }
60    }
61}
62
63impl Method {
64    /// Whether a method is considered "safe", meaning the request is
65    /// essentially read-only.
66    ///
67    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
68    /// for more words.
69    pub fn safe(&self) -> bool {
70        match *self {
71            Get | Head | Options | Trace => true,
72            _ => false
73        }
74    }
75
76    /// Whether a method is considered "idempotent", meaning the request has
77    /// the same result if executed multiple times.
78    ///
79    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
80    /// more words.
81    pub fn idempotent(&self) -> bool {
82        if self.safe() {
83            true
84        } else {
85            match *self {
86                Put | Delete => true,
87                _ => false
88            }
89        }
90    }
91}
92
93macro_rules! from_str {
94    ($s:ident, { $($n:pat => { $($text:pat => $var:ident,)* },)* }) => ({
95        let s = $s;
96        match s.len() {
97            $(
98            $n => match s {
99                $(
100                $text => return Ok($var),
101                )*
102                _ => {},
103            },
104            )*
105            0 => return Err(::Error::Method),
106            _ => {},
107        }
108        Ok(Extension(s.to_owned()))
109    })
110}
111
112impl FromStr for Method {
113    type Err = Error;
114    fn from_str(s: &str) -> Result<Method, Error> {
115        from_str!(s, {
116            3 => {
117                "GET" => Get,
118                "PUT" => Put,
119            },
120            4 => {
121                "HEAD" => Head,
122                "POST" => Post,
123            },
124            5 => {
125                "PATCH" => Patch,
126                "TRACE" => Trace,
127            },
128            6 => {
129                "DELETE" => Delete,
130            },
131            7 => {
132                "OPTIONS" => Options,
133                "CONNECT" => Connect,
134            },
135        })
136    }
137}
138
139impl fmt::Display for Method {
140    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
141        fmt.write_str(match *self {
142            Options => "OPTIONS",
143            Get => "GET",
144            Post => "POST",
145            Put => "PUT",
146            Delete => "DELETE",
147            Head => "HEAD",
148            Trace => "TRACE",
149            Connect => "CONNECT",
150            Patch => "PATCH",
151            Extension(ref s) => s.as_ref()
152        })
153    }
154}
155
156impl Default for Method {
157    fn default() -> Method {
158        Method::Get
159    }
160}
161
162#[cfg(feature = "compat")]
163impl From<http::Method> for Method {
164    fn from(method: http::Method) -> Method {
165        match method {
166            http::Method::GET =>
167                Method::Get,
168            http::Method::POST =>
169                Method::Post,
170            http::Method::PUT =>
171                Method::Put,
172            http::Method::DELETE =>
173                Method::Delete,
174            http::Method::HEAD =>
175                Method::Head,
176            http::Method::OPTIONS =>
177                Method::Options,
178            http::Method::CONNECT =>
179                Method::Connect,
180            http::Method::PATCH =>
181                Method::Patch,
182            http::Method::TRACE =>
183                Method::Trace,
184            _ => {
185                method.as_ref().parse()
186                    .expect("attempted to convert invalid method")
187            }
188        }
189    }
190}
191
192#[cfg(feature = "compat")]
193impl From<Method> for http::Method {
194    fn from(method: Method) -> http::Method {
195        use http::HttpTryFrom;
196
197        match method {
198            Method::Get =>
199                http::Method::GET,
200            Method::Post =>
201                http::Method::POST,
202            Method::Put =>
203                http::Method::PUT,
204            Method::Delete =>
205                http::Method::DELETE,
206            Method::Head =>
207                http::Method::HEAD,
208            Method::Options =>
209                http::Method::OPTIONS,
210            Method::Connect =>
211                http::Method::CONNECT,
212            Method::Patch =>
213                http::Method::PATCH,
214            Method::Trace =>
215                http::Method::TRACE,
216            Method::Extension(s) => {
217                HttpTryFrom::try_from(s.as_str())
218                    .expect("attempted to convert invalid method")
219            }
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use std::collections::HashMap;
227    use std::str::FromStr;
228    use error::Error;
229    use super::Method;
230    use super::Method::{Get, Post, Put, Extension};
231
232    #[test]
233    fn test_safe() {
234        assert_eq!(true, Get.safe());
235        assert_eq!(false, Post.safe());
236    }
237
238    #[test]
239    fn test_idempotent() {
240        assert_eq!(true, Get.idempotent());
241        assert_eq!(true, Put.idempotent());
242        assert_eq!(false, Post.idempotent());
243    }
244
245    #[test]
246    fn test_from_str() {
247        assert_eq!(Get, FromStr::from_str("GET").unwrap());
248        assert_eq!(Extension("MOVE".to_owned()),
249                   FromStr::from_str("MOVE").unwrap());
250        let x: Result<Method, _> = FromStr::from_str("");
251        if let Err(Error::Method) = x {
252        } else {
253            panic!("An empty method is invalid!")
254        }
255    }
256
257    #[test]
258    fn test_fmt() {
259        assert_eq!("GET".to_owned(), format!("{}", Get));
260        assert_eq!("MOVE".to_owned(),
261                   format!("{}", Extension("MOVE".to_owned())));
262    }
263
264    #[test]
265    fn test_hashable() {
266        let mut counter: HashMap<Method,usize> = HashMap::new();
267        counter.insert(Get, 1);
268        assert_eq!(Some(&1), counter.get(&Get));
269    }
270
271    #[test]
272    fn test_as_str() {
273        assert_eq!(Get.as_ref(), "GET");
274        assert_eq!(Post.as_ref(), "POST");
275        assert_eq!(Put.as_ref(), "PUT");
276        assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE");
277    }
278
279    #[test]
280    #[cfg(feature = "compat")]
281    fn test_compat() {
282        use http::{self, HttpTryFrom};
283
284        let methods = vec![
285            "GET",
286            "POST",
287            "PUT",
288            "MOVE"
289        ];
290        for method in methods {
291            let orig_hyper_method = Method::from_str(method).unwrap();
292            let orig_http_method = http::Method::try_from(method).unwrap();
293            let conv_hyper_method: Method = orig_http_method.clone().into();
294            let conv_http_method: http::Method = orig_hyper_method.clone().into();
295            assert_eq!(orig_hyper_method, conv_hyper_method);
296            assert_eq!(orig_http_method, conv_http_method);
297        }
298    }
299}