1use std::fmt;
3use std::str::FromStr;
4use std::convert::{AsRef, TryFrom};
5
6use http;
7
8use error::Error;
9use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
10 Extension};
11
12#[derive(Clone, PartialEq, Eq, Hash, Debug)]
21pub enum Method {
22 Options,
24 Get,
26 Post,
28 Put,
30 Delete,
32 Head,
34 Trace,
36 Connect,
38 Patch,
40 Extension(String)
42}
43
44impl AsRef<str> for Method {
45 fn as_ref(&self) -> &str {
46 match *self {
47 Options => "OPTIONS",
48 Get => "GET",
49 Post => "POST",
50 Put => "PUT",
51 Delete => "DELETE",
52 Head => "HEAD",
53 Trace => "TRACE",
54 Connect => "CONNECT",
55 Patch => "PATCH",
56 Extension(ref s) => s.as_ref()
57 }
58 }
59}
60
61impl Method {
62 pub fn safe(&self) -> bool {
68 match *self {
69 Get | Head | Options | Trace => true,
70 _ => false
71 }
72 }
73
74 pub fn idempotent(&self) -> bool {
80 if self.safe() {
81 true
82 } else {
83 match *self {
84 Put | Delete => true,
85 _ => false
86 }
87 }
88 }
89}
90
91macro_rules! from_str {
92 ($s:ident, { $($n:pat => { $($text:pat => $var:ident,)* },)* }) => ({
93 let s = $s;
94 match s.len() {
95 $(
96 $n => match s {
97 $(
98 $text => return Ok($var),
99 )*
100 _ => {},
101 },
102 )*
103 0 => return Err(::Error::Method),
104 _ => {},
105 }
106 Ok(Extension(s.to_owned()))
107 })
108}
109
110impl FromStr for Method {
111 type Err = Error;
112 fn from_str(s: &str) -> Result<Method, Error> {
113 from_str!(s, {
114 3 => {
115 "GET" => Get,
116 "PUT" => Put,
117 },
118 4 => {
119 "HEAD" => Head,
120 "POST" => Post,
121 },
122 5 => {
123 "PATCH" => Patch,
124 "TRACE" => Trace,
125 },
126 6 => {
127 "DELETE" => Delete,
128 },
129 7 => {
130 "OPTIONS" => Options,
131 "CONNECT" => Connect,
132 },
133 })
134 }
135}
136
137impl fmt::Display for Method {
138 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
139 fmt.write_str(match *self {
140 Options => "OPTIONS",
141 Get => "GET",
142 Post => "POST",
143 Put => "PUT",
144 Delete => "DELETE",
145 Head => "HEAD",
146 Trace => "TRACE",
147 Connect => "CONNECT",
148 Patch => "PATCH",
149 Extension(ref s) => s.as_ref()
150 })
151 }
152}
153
154impl Default for Method {
155 fn default() -> Method {
156 Method::Get
157 }
158}
159
160impl From<http::Method> for Method {
161 fn from(method: http::Method) -> Method {
162 match method {
163 http::Method::GET =>
164 Method::Get,
165 http::Method::POST =>
166 Method::Post,
167 http::Method::PUT =>
168 Method::Put,
169 http::Method::DELETE =>
170 Method::Delete,
171 http::Method::HEAD =>
172 Method::Head,
173 http::Method::OPTIONS =>
174 Method::Options,
175 http::Method::CONNECT =>
176 Method::Connect,
177 http::Method::PATCH =>
178 Method::Patch,
179 http::Method::TRACE =>
180 Method::Trace,
181 _ => {
182 method.as_ref().parse()
183 .expect("attempted to convert invalid method")
184 }
185 }
186 }
187}
188
189impl From<Method> for http::Method {
190 fn from(method: Method) -> http::Method {
191
192 match method {
193 Method::Get =>
194 http::Method::GET,
195 Method::Post =>
196 http::Method::POST,
197 Method::Put =>
198 http::Method::PUT,
199 Method::Delete =>
200 http::Method::DELETE,
201 Method::Head =>
202 http::Method::HEAD,
203 Method::Options =>
204 http::Method::OPTIONS,
205 Method::Connect =>
206 http::Method::CONNECT,
207 Method::Patch =>
208 http::Method::PATCH,
209 Method::Trace =>
210 http::Method::TRACE,
211 Method::Extension(s) => {
212 http::Method::try_from(s.as_str())
213 .expect("attempted to convert invalid method")
214 }
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use std::collections::HashMap;
222 use std::convert::TryFrom;
223 use std::str::FromStr;
224 use error::Error;
225 use super::Method;
226 use super::Method::{Get, Post, Put, Extension};
227
228 #[test]
229 fn test_safe() {
230 assert_eq!(true, Get.safe());
231 assert_eq!(false, Post.safe());
232 }
233
234 #[test]
235 fn test_idempotent() {
236 assert_eq!(true, Get.idempotent());
237 assert_eq!(true, Put.idempotent());
238 assert_eq!(false, Post.idempotent());
239 }
240
241 #[test]
242 fn test_from_str() {
243 assert_eq!(Get, FromStr::from_str("GET").unwrap());
244 assert_eq!(Extension("MOVE".to_owned()),
245 FromStr::from_str("MOVE").unwrap());
246 let x: Result<Method, _> = FromStr::from_str("");
247 if let Err(Error::Method) = x {
248 } else {
249 panic!("An empty method is invalid!")
250 }
251 }
252
253 #[test]
254 fn test_fmt() {
255 assert_eq!("GET".to_owned(), format!("{}", Get));
256 assert_eq!("MOVE".to_owned(),
257 format!("{}", Extension("MOVE".to_owned())));
258 }
259
260 #[test]
261 fn test_hashable() {
262 let mut counter: HashMap<Method,usize> = HashMap::new();
263 counter.insert(Get, 1);
264 assert_eq!(Some(&1), counter.get(&Get));
265 }
266
267 #[test]
268 fn test_as_str() {
269 assert_eq!(Get.as_ref(), "GET");
270 assert_eq!(Post.as_ref(), "POST");
271 assert_eq!(Put.as_ref(), "PUT");
272 assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE");
273 }
274
275 #[test]
276 fn test_compat() {
277 let methods = vec![
278 "GET",
279 "POST",
280 "PUT",
281 "MOVE"
282 ];
283 for method in methods {
284 let orig_hyper_method = Method::from_str(method).unwrap();
285 let orig_http_method = http::Method::try_from(method).unwrap();
286 let conv_hyper_method: Method = orig_http_method.clone().into();
287 let conv_http_method: http::Method = orig_hyper_method.clone().into();
288 assert_eq!(orig_hyper_method, conv_hyper_method);
289 assert_eq!(orig_http_method, conv_http_method);
290 }
291 }
292}