hyper_sync/
method.rs

1//! The HTTP request method
2use std::fmt;
3use std::str::FromStr;
4use std::convert::AsRef;
5
6use error::Error;
7use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
8                   Extension};
9
10
11/// The Request Method (VERB)
12///
13/// Currently includes 8 variants representing the 8 methods defined in
14/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
15/// and an Extension variant for all extensions.
16///
17/// It may make sense to grow this to include all variants currently
18/// registered with IANA, if they are at all common to use.
19#[derive(Clone, PartialEq, Eq, Hash, Debug)]
20pub enum Method {
21    /// OPTIONS
22    Options,
23    /// GET
24    Get,
25    /// POST
26    Post,
27    /// PUT
28    Put,
29    /// DELETE
30    Delete,
31    /// HEAD
32    Head,
33    /// TRACE
34    Trace,
35    /// CONNECT
36    Connect,
37    /// PATCH
38    Patch,
39    /// Method extensions. An example would be `let m = Extension("FOO".to_string())`.
40    Extension(String)
41}
42
43impl AsRef<str> for Method {
44    fn as_ref(&self) -> &str {
45        match *self {
46            Options => "OPTIONS",
47            Get => "GET",
48            Post => "POST",
49            Put => "PUT",
50            Delete => "DELETE",
51            Head => "HEAD",
52            Trace => "TRACE",
53            Connect => "CONNECT",
54            Patch => "PATCH",
55            Extension(ref s) => s.as_ref()
56        }
57    }
58}
59
60impl Method {
61    /// Whether a method is considered "safe", meaning the request is
62    /// essentially read-only.
63    ///
64    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
65    /// for more words.
66    pub fn safe(&self) -> bool {
67        match *self {
68            Get | Head | Options | Trace => true,
69            _ => false
70        }
71    }
72
73    /// Whether a method is considered "idempotent", meaning the request has
74    /// the same result is executed multiple times.
75    ///
76    /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
77    /// more words.
78    pub fn idempotent(&self) -> bool {
79        if self.safe() {
80            true
81        } else {
82            match *self {
83                Put | Delete => true,
84                _ => false
85            }
86        }
87    }
88}
89
90impl FromStr for Method {
91    type Err = Error;
92    fn from_str(s: &str) -> Result<Method, Error> {
93        if s == "" {
94            Err(Error::Method)
95        } else {
96            Ok(match s {
97                "OPTIONS" => Options,
98                "GET" => Get,
99                "POST" => Post,
100                "PUT" => Put,
101                "DELETE" => Delete,
102                "HEAD" => Head,
103                "TRACE" => Trace,
104                "CONNECT" => Connect,
105                "PATCH" => Patch,
106                _ => Extension(s.to_owned())
107            })
108        }
109    }
110}
111
112impl fmt::Display for Method {
113    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
114        fmt.write_str(match *self {
115            Options => "OPTIONS",
116            Get => "GET",
117            Post => "POST",
118            Put => "PUT",
119            Delete => "DELETE",
120            Head => "HEAD",
121            Trace => "TRACE",
122            Connect => "CONNECT",
123            Patch => "PATCH",
124            Extension(ref s) => s.as_ref()
125        })
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use std::collections::HashMap;
132    use std::str::FromStr;
133    use error::Error;
134    use super::Method;
135    use super::Method::{Get, Post, Put, Extension};
136
137    #[test]
138    fn test_safe() {
139        assert_eq!(true, Get.safe());
140        assert_eq!(false, Post.safe());
141    }
142
143    #[test]
144    fn test_idempotent() {
145        assert_eq!(true, Get.idempotent());
146        assert_eq!(true, Put.idempotent());
147        assert_eq!(false, Post.idempotent());
148    }
149
150    #[test]
151    fn test_from_str() {
152        assert_eq!(Get, FromStr::from_str("GET").unwrap());
153        assert_eq!(Extension("MOVE".to_owned()),
154                   FromStr::from_str("MOVE").unwrap());
155        let x: Result<Method, _> = FromStr::from_str("");
156        if let Err(Error::Method) = x {
157        } else {
158            panic!("An empty method is invalid!")
159        }
160    }
161
162    #[test]
163    fn test_fmt() {
164        assert_eq!("GET".to_owned(), format!("{}", Get));
165        assert_eq!("MOVE".to_owned(),
166                   format!("{}", Extension("MOVE".to_owned())));
167    }
168
169    #[test]
170    fn test_hashable() {
171        let mut counter: HashMap<Method,usize> = HashMap::new();
172        counter.insert(Get, 1);
173        assert_eq!(Some(&1), counter.get(&Get));
174    }
175
176    #[test]
177    fn test_as_str() {
178        assert_eq!(Get.as_ref(), "GET");
179        assert_eq!(Post.as_ref(), "POST");
180        assert_eq!(Put.as_ref(), "PUT");
181        assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE");
182    }
183}