http_types_rs/method.rs
1use std::fmt::{self, Display};
2use std::str::FromStr;
3
4/// HTTP request methods.
5///
6/// See also [Mozilla's documentation][Mozilla docs], the [RFC7231, Section 4][] and
7/// [IANA's Hypertext Transfer Protocol (HTTP) Method Registry][HTTP Method Registry].
8///
9/// [Mozilla docs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
10/// [RFC7231, Section 4]: https://tools.ietf.org/html/rfc7231#section-4
11/// [HTTP Method Registry]: https://www.iana.org/assignments/http-methods/http-methods.xhtml
12#[non_exhaustive]
13#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
14pub enum Method {
15 /// The ACL method modifies the access control list (which can be read via the DAV:acl
16 /// property) of a resource.
17 ///
18 /// See [RFC3744, Section 8.1][].
19 ///
20 /// [RFC3744, Section 8.1]: https://tools.ietf.org/html/rfc3744#section-8.1
21 Acl,
22
23 /// A collection can be placed under baseline control with a BASELINE-CONTROL request.
24 ///
25 /// See [RFC3253, Section 12.6][].
26 ///
27 /// [RFC3253, Section 12.6]: https://tools.ietf.org/html/rfc3253#section-12.6
28 BaselineControl,
29
30 /// The BIND method modifies the collection identified by the Request- URI, by adding a new
31 /// binding from the segment specified in the BIND body to the resource identified in the BIND
32 /// body.
33 ///
34 /// See [RFC5842, Section 4][].
35 ///
36 /// [RFC5842, Section 4]: https://tools.ietf.org/html/rfc5842#section-4
37 Bind,
38
39 /// A CHECKIN request can be applied to a checked-out version-controlled resource to produce a
40 /// new version whose content and dead properties are copied from the checked-out resource.
41 ///
42 /// See [RFC3253, Section 4.4][] and [RFC3253, Section 9.4][].
43 ///
44 /// [RFC3253, Section 4.4]: https://tools.ietf.org/html/rfc3253#section-4.4
45 /// [RFC3253, Section 9.4]: https://tools.ietf.org/html/rfc3253#section-9.4
46 Checkin,
47
48 /// A CHECKOUT request can be applied to a checked-in version-controlled resource to allow
49 /// modifications to the content and dead properties of that version-controlled resource.
50 ///
51 /// See [RFC3253, Section 4.3][] and [RFC3253, Section 8.8][].
52 ///
53 /// [RFC3253, Section 4.3]: https://tools.ietf.org/html/rfc3253#section-4.3
54 /// [RFC3253, Section 8.8]: https://tools.ietf.org/html/rfc3253#section-8.8
55 Checkout,
56
57 /// The CONNECT method requests that the recipient establish a tunnel to the destination origin
58 /// server identified by the request-target and, if successful, thereafter restrict its
59 /// behavior to blind forwarding of packets, in both directions, until the tunnel is closed.
60 ///
61 /// See [RFC7231, Section 4.3.6][].
62 ///
63 /// [RFC7231, Section 4.3.6]: https://tools.ietf.org/html/rfc7231#section-4.3.6
64 Connect,
65
66 /// The COPY method creates a duplicate of the source resource identified by the Request-URI,
67 /// in the destination resource identified by the URI in the Destination header.
68 ///
69 /// See [RFC4918, Section 9.8][].
70 ///
71 /// [RFC4918, Section 9.8]: https://tools.ietf.org/html/rfc4918#section-9.8
72 Copy,
73
74 /// The DELETE method requests that the origin server remove the association between the target
75 /// resource and its current functionality.
76 ///
77 /// See [RFC7231, Section 4.3.5][].
78 ///
79 /// [RFC7231, Section 4.3.5]: https://tools.ietf.org/html/rfc7231#section-4.3.5
80 Delete,
81
82 /// The GET method requests transfer of a current selected representation for the target
83 /// resource.
84 ///
85 /// See [RFC7231, Section 4.3.1][].
86 ///
87 /// [RFC7231, Section 4.3.1]: https://tools.ietf.org/html/rfc7231#section-4.3.1
88 Get,
89
90 /// The HEAD method is identical to GET except that the server MUST NOT send a message body in
91 /// the response.
92 ///
93 /// See [RFC7231, Section 4.3.2][].
94 ///
95 /// [RFC7231, Section 4.3.2]: https://tools.ietf.org/html/rfc7231#section-4.3.2
96 Head,
97
98 /// A LABEL request can be applied to a version to modify the labels that select that version.
99 ///
100 /// See [RFC3253, Section 8.2][].
101 ///
102 /// [RFC3253, Section 8.2]: https://tools.ietf.org/html/rfc3253#section-8.2
103 Label,
104
105 /// The LINK method establishes one or more Link relationships between the existing resource
106 /// identified by the Request-URI and other existing resources.
107 ///
108 /// See [RFC2068, Section 19.6.1.2][].
109 ///
110 /// [RFC2068, Section 19.6.1.2]: https://tools.ietf.org/html/rfc2068#section-19.6.1.2
111 Link,
112
113 /// The LOCK method is used to take out a lock of any access type and to refresh an existing
114 /// lock.
115 ///
116 /// See [RFC4918, Section 9.10][].
117 ///
118 /// [RFC4918, Section 9.10]: https://tools.ietf.org/html/rfc4918#section-9.10
119 Lock,
120
121 /// The MERGE method performs the logical merge of a specified version (the "merge source")
122 /// into a specified version-controlled resource (the "merge target").
123 ///
124 /// See [RFC3253, Section 11.2][].
125 ///
126 /// [RFC3253, Section 11.2]: https://tools.ietf.org/html/rfc3253#section-11.2
127 Merge,
128
129 /// A MKACTIVITY request creates a new activity resource.
130 ///
131 /// See [RFC3253, Section 13.5].
132 ///
133 /// [RFC3253, Section 13.5]: https://tools.ietf.org/html/rfc3253#section-13.5
134 MkActivity,
135
136 /// An HTTP request using the MKCALENDAR method creates a new calendar collection resource.
137 ///
138 /// See [RFC4791, Section 5.3.1][] and [RFC8144, Section 2.3][].
139 ///
140 /// [RFC4791, Section 5.3.1]: https://tools.ietf.org/html/rfc4791#section-5.3.1
141 /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc8144#section-2.3
142 MkCalendar,
143
144 /// MKCOL creates a new collection resource at the location specified by the Request-URI.
145 ///
146 /// See [RFC4918, Section 9.3][], [RFC5689, Section 3][] and [RFC8144, Section 2.3][].
147 ///
148 /// [RFC4918, Section 9.3]: https://tools.ietf.org/html/rfc4918#section-9.3
149 /// [RFC5689, Section 3]: https://tools.ietf.org/html/rfc5689#section-3
150 /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc5689#section-3
151 MkCol,
152
153 /// The MKREDIRECTREF method requests the creation of a redirect reference resource.
154 ///
155 /// See [RFC4437, Section 6][].
156 ///
157 /// [RFC4437, Section 6]: https://tools.ietf.org/html/rfc4437#section-6
158 MkRedirectRef,
159
160 /// A MKWORKSPACE request creates a new workspace resource.
161 ///
162 /// See [RFC3253, Section 6.3][].
163 ///
164 /// [RFC3253, Section 6.3]: https://tools.ietf.org/html/rfc3253#section-6.3
165 MkWorkspace,
166
167 /// The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY),
168 /// followed by consistency maintenance processing, followed by a delete of the source, where
169 /// all three actions are performed in a single operation.
170 ///
171 /// See [RFC4918, Section 9.9][].
172 ///
173 /// [RFC4918, Section 9.9]: https://tools.ietf.org/html/rfc4918#section-9.9
174 Move,
175
176 /// The OPTIONS method requests information about the communication options available for the
177 /// target resource, at either the origin server or an intervening intermediary.
178 ///
179 /// See [RFC7231, Section 4.3.7][].
180 ///
181 /// [RFC7231, Section 4.3.7]: https://tools.ietf.org/html/rfc7231#section-4.3.7
182 Options,
183
184 /// The ORDERPATCH method is used to change the ordering semantics of a collection, to change
185 /// the order of the collection's members in the ordering, or both.
186 ///
187 /// See [RFC3648, Section 7][].
188 ///
189 /// [RFC3648, Section 7]: https://tools.ietf.org/html/rfc3648#section-7
190 OrderPatch,
191
192 /// The PATCH method requests that a set of changes described in the request entity be applied
193 /// to the resource identified by the Request- URI.
194 ///
195 /// See [RFC5789, Section 2][].
196 ///
197 /// [RFC5789, Section 2]: https://tools.ietf.org/html/rfc5789#section-2
198 Patch,
199
200 /// The POST method requests that the target resource process the representation enclosed in
201 /// the request according to the resource's own specific semantics.
202 ///
203 /// For example, POST is used for the following functions (among others):
204 ///
205 /// - Providing a block of data, such as the fields entered into an HTML form, to a
206 /// data-handling process;
207 /// - Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group
208 /// of articles;
209 /// - Creating a new resource that has yet to be identified by the origin server; and
210 /// - Appending data to a resource's existing representation(s).
211 ///
212 /// See [RFC7231, Section 4.3.3][].
213 ///
214 /// [RFC7231, Section 4.3.3]: https://tools.ietf.org/html/rfc7231#section-4.3.3
215 Post,
216
217 /// This method is never used by an actual client. This method will appear to be used when an
218 /// HTTP/1.1 server or intermediary attempts to parse an HTTP/2 connection preface.
219 ///
220 /// See [RFC7540, Section 3.5][] and [RFC7540, Section 11.6][]
221 ///
222 /// [RFC7540, Section 3.5]: https://tools.ietf.org/html/rfc7540#section-3.5
223 /// [RFC7540, Section 11.6]: https://tools.ietf.org/html/rfc7540#section-11.6
224 Pri,
225
226 /// The PROPFIND method retrieves properties defined on the resource identified by the
227 /// Request-URI.
228 ///
229 /// See [RFC4918, Section 9.1][] and [RFC8144, Section 2.1][].
230 ///
231 /// [RFC4918, Section 9.1]: https://tools.ietf.org/html/rfc4918#section-9.1
232 /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1
233 PropFind,
234
235 /// The PROPPATCH method processes instructions specified in the request body to set and/or
236 /// remove properties defined on the resource identified by the Request-URI.
237 ///
238 /// See [RFC4918, Section 9.2][] and [RFC8144, Section 2.2][].
239 ///
240 /// [RFC4918, Section 9.2]: https://tools.ietf.org/html/rfc4918#section-9.2
241 /// [RFC8144, Section 2.2]: https://tools.ietf.org/html/rfc8144#section-2.2
242 PropPatch,
243
244 /// The PUT method requests that the state of the target resource be created or replaced with
245 /// the state defined by the representation enclosed in the request message payload.
246 ///
247 /// See [RFC7231, Section 4.3.4][].
248 ///
249 /// [RFC7231, Section 4.3.4]: https://tools.ietf.org/html/rfc7231#section-4.3.4
250 Put,
251
252 /// The REBIND method removes a binding to a resource from a collection, and adds a binding to
253 /// that resource into the collection identified by the Request-URI.
254 ///
255 /// See [RFC5842, Section 6][].
256 ///
257 /// [RFC5842, Section 6]: https://tools.ietf.org/html/rfc5842#section-6
258 Rebind,
259
260 /// A REPORT request is an extensible mechanism for obtaining information about a resource.
261 ///
262 /// See [RFC3253, Section 3.6][] and [RFC8144, Section 2.1][].
263 ///
264 /// [RFC3253, Section 3.6]: https://tools.ietf.org/html/rfc3253#section-3.6
265 /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1
266 Report,
267
268 /// The client invokes the SEARCH method to initiate a server-side search. The body of the
269 /// request defines the query.
270 ///
271 /// See [RFC5323, Section 2][].
272 ///
273 /// [RFC5323, Section 2]: https://tools.ietf.org/html/rfc5323#section-2
274 Search,
275
276 /// The TRACE method requests a remote, application-level loop-back of the request message.
277 ///
278 /// See [RFC7231, Section 4.3.8][].
279 ///
280 /// [RFC7231, Section 4.3.8]: https://tools.ietf.org/html/rfc7231#section-4.3.8
281 Trace,
282
283 /// The UNBIND method modifies the collection identified by the Request- URI by removing the
284 /// binding identified by the segment specified in the UNBIND body.
285 ///
286 /// See [RFC5842, Section 5][].
287 ///
288 /// [RFC5842, Section 5]: https://tools.ietf.org/html/rfc5842#section-5
289 Unbind,
290
291 /// An UNCHECKOUT request can be applied to a checked-out version-controlled resource to cancel
292 /// the CHECKOUT and restore the pre-CHECKOUT state of the version-controlled resource.
293 ///
294 /// See [RFC3253, Section 4.5][].
295 ///
296 /// [RFC3253, Section 4.5]: https://tools.ietf.org/html/rfc3253#section-4.5
297 Uncheckout,
298
299 /// The UNLINK method removes one or more Link relationships from the existing resource
300 /// identified by the Request-URI.
301 ///
302 /// See [RFC2068, Section 19.6.1.3][].
303 ///
304 /// [RFC2068, Section 19.6.1.3]: https://tools.ietf.org/html/rfc2068#section-19.6.1.3
305 Unlink,
306
307 /// The UNLOCK method removes the lock identified by the lock token in the Lock-Token request
308 /// header.
309 ///
310 /// See [RFC4918, Section 9.11][].
311 ///
312 /// [RFC4918, Section 9.11]: https://tools.ietf.org/html/rfc4918#section-9.11
313 Unlock,
314
315 /// The UPDATE method modifies the content and dead properties of a checked-in
316 /// version-controlled resource (the "update target") to be those of a specified version (the
317 /// "update source") from the version history of that version-controlled resource.
318 ///
319 /// See [RFC3253, Section 7.1][].
320 ///
321 /// [RFC3253, Section 7.1]: https://tools.ietf.org/html/rfc3253#section-7.1
322 Update,
323
324 /// The UPDATEREDIRECTREF method requests the update of a redirect reference resource.
325 ///
326 /// See [RFC4437, Section 7][].
327 ///
328 /// [RFC4437, Section 7]: https://tools.ietf.org/html/rfc4437#section-7
329 UpdateRedirectRef,
330
331 /// A VERSION-CONTROL request can be used to create a version-controlled resource at the
332 /// request-URL.
333 ///
334 /// See [RFC3253, Section 3.5].
335 ///
336 /// [RFC3253, Section 3.5]: https://tools.ietf.org/html/rfc3253#section-3.5
337 VersionControl,
338}
339
340impl Method {
341 /// Whether a method is considered "safe", meaning the request is essentially read-only.
342 ///
343 /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1) for more details.
344 pub fn is_safe(&self) -> bool {
345 matches!(
346 self,
347 Method::Get | Method::Head | Method::Options | Method::Pri | Method::PropFind | Method::Report | Method::Search | Method::Trace
348 )
349 }
350}
351
352#[cfg(feature = "serde")]
353mod serde {
354 use super::Method;
355 use serde::de::{Error as DeError, Unexpected, Visitor};
356 use serde::{Deserialize, Deserializer, Serialize, Serializer};
357 use std::fmt;
358 use std::str::FromStr;
359
360 struct MethodVisitor;
361
362 impl<'de> Visitor<'de> for MethodVisitor {
363 type Value = Method;
364
365 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
366 write!(formatter, "a HTTP method &str")
367 }
368
369 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
370 where
371 E: DeError,
372 {
373 match Method::from_str(v) {
374 Ok(method) => Ok(method),
375 Err(_) => Err(DeError::invalid_value(Unexpected::Str(v), &self)),
376 }
377 }
378 }
379
380 impl<'de> Deserialize<'de> for Method {
381 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
382 where
383 D: Deserializer<'de>,
384 {
385 deserializer.deserialize_str(MethodVisitor)
386 }
387 }
388
389 impl Serialize for Method {
390 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
391 where
392 S: Serializer,
393 {
394 serializer.serialize_str(self.as_ref())
395 }
396 }
397}
398
399impl Display for Method {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.write_str(AsRef::<str>::as_ref(self))
402 }
403}
404
405impl FromStr for Method {
406 type Err = crate::Error;
407
408 fn from_str(s: &str) -> Result<Self, Self::Err> {
409 #[allow(clippy::match_str_case_mismatch)]
410 match &*s.to_ascii_uppercase() {
411 "ACL" => Ok(Self::Acl),
412 "BASELINE-CONTROL" => Ok(Self::BaselineControl),
413 "BIND" => Ok(Self::Bind),
414 "CHECKIN" => Ok(Self::Checkin),
415 "CHECKOUT" => Ok(Self::Checkout),
416 "CONNECT" => Ok(Self::Connect),
417 "COPY" => Ok(Self::Copy),
418 "DELETE" => Ok(Self::Delete),
419 "GET" => Ok(Self::Get),
420 "HEAD" => Ok(Self::Head),
421 "LABEL" => Ok(Self::Label),
422 "LINK" => Ok(Self::Link),
423 "LOCK" => Ok(Self::Lock),
424 "MERGE" => Ok(Self::Merge),
425 "MKACTIVITY" => Ok(Self::MkActivity),
426 "MKCALENDAR" => Ok(Self::MkCalendar),
427 "MKCOL" => Ok(Self::MkCol),
428 "MKREDIRECTREF" => Ok(Self::MkRedirectRef),
429 "MKWORKSPACE" => Ok(Self::MkWorkspace),
430 "MOVE" => Ok(Self::Move),
431 "OPTIONS" => Ok(Self::Options),
432 "ORDERPATCH" => Ok(Self::OrderPatch),
433 "PATCH" => Ok(Self::Patch),
434 "POST" => Ok(Self::Post),
435 "PRI" => Ok(Self::Pri),
436 "PROPFIND" => Ok(Self::PropFind),
437 "PROPPATCH" => Ok(Self::PropPatch),
438 "PUT" => Ok(Self::Put),
439 "REBIND" => Ok(Self::Rebind),
440 "REPORT" => Ok(Self::Report),
441 "SEARCH" => Ok(Self::Search),
442 "TRACE" => Ok(Self::Trace),
443 "UNBIND" => Ok(Self::Unbind),
444 "UNCHECKOUT" => Ok(Self::Uncheckout),
445 "UNLINK" => Ok(Self::Unlink),
446 "UNLOCK" => Ok(Self::Unlock),
447 "UPDATE" => Ok(Self::Update),
448 "UPDATEREDIRECTREF" => Ok(Self::UpdateRedirectRef),
449 "VERSION-CONTROL" => Ok(Self::VersionControl),
450 _ => crate::bail!("Invalid HTTP method"),
451 }
452 }
453}
454
455impl<'a> std::convert::TryFrom<&'a str> for Method {
456 type Error = crate::Error;
457
458 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
459 Self::from_str(value)
460 }
461}
462
463impl AsRef<str> for Method {
464 fn as_ref(&self) -> &str {
465 match self {
466 Self::Acl => "ACL",
467 Self::BaselineControl => "BASELINE-CONTROL",
468 Self::Bind => "BIND",
469 Self::Checkin => "CHECKIN",
470 Self::Checkout => "CHECKOUT",
471 Self::Connect => "CONNECT",
472 Self::Copy => "COPY",
473 Self::Delete => "DELETE",
474 Self::Get => "GET",
475 Self::Head => "HEAD",
476 Self::Label => "LABEL",
477 Self::Link => "LINK",
478 Self::Lock => "LOCK",
479 Self::Merge => "MERGE",
480 Self::MkActivity => "MKACTIVITY",
481 Self::MkCalendar => "MKCALENDAR",
482 Self::MkCol => "MKCOL",
483 Self::MkRedirectRef => "MKREDIRECTREF",
484 Self::MkWorkspace => "MKWORKSPACE",
485 Self::Move => "MOVE",
486 Self::Options => "OPTIONS",
487 Self::OrderPatch => "ORDERPATCH",
488 Self::Patch => "PATCH",
489 Self::Post => "POST",
490 Self::Pri => "PRI",
491 Self::PropFind => "PROPFIND",
492 Self::PropPatch => "PROPPATCH",
493 Self::Put => "PUT",
494 Self::Rebind => "REBIND",
495 Self::Report => "REPORT",
496 Self::Search => "SEARCH",
497 Self::Trace => "TRACE",
498 Self::Unbind => "UNBIND",
499 Self::Uncheckout => "UNCHECKOUT",
500 Self::Unlink => "UNLINK",
501 Self::Unlock => "UNLOCK",
502 Self::Update => "UPDATE",
503 Self::UpdateRedirectRef => "UPDATEREDIRECTREF",
504 Self::VersionControl => "VERSION-CONTROL",
505 }
506 }
507}
508
509#[cfg(test)]
510mod test {
511 use std::collections::HashSet;
512
513 use super::Method;
514
515 #[test]
516 fn serde() -> Result<(), serde_json::Error> {
517 assert_eq!(Method::Get, serde_json::from_str("\"GET\"")?);
518 assert_eq!(Some("PATCH"), serde_json::to_value(Method::Patch)?.as_str());
519 Ok(())
520 }
521
522 #[test]
523 fn serde_fail() {
524 serde_json::from_str::<Method>("\"ABC\"").expect_err("Did deserialize from invalid string");
525 }
526
527 #[test]
528 fn names() -> Result<(), crate::Error> {
529 let method_names = [
530 "ACL",
531 "BASELINE-CONTROL",
532 "BIND",
533 "CHECKIN",
534 "CHECKOUT",
535 "CONNECT",
536 "COPY",
537 "DELETE",
538 "GET",
539 "HEAD",
540 "LABEL",
541 "LINK",
542 "LOCK",
543 "MERGE",
544 "MKACTIVITY",
545 "MKCALENDAR",
546 "MKCOL",
547 "MKREDIRECTREF",
548 "MKWORKSPACE",
549 "MOVE",
550 "OPTIONS",
551 "ORDERPATCH",
552 "PATCH",
553 "POST",
554 "PRI",
555 "PROPFIND",
556 "PROPPATCH",
557 "PUT",
558 "REBIND",
559 "REPORT",
560 "SEARCH",
561 "TRACE",
562 "UNBIND",
563 "UNCHECKOUT",
564 "UNLINK",
565 "UNLOCK",
566 "UPDATE",
567 "UPDATEREDIRECTREF",
568 "VERSION-CONTROL",
569 ];
570
571 let methods = method_names.iter().map(|s| s.parse::<Method>()).collect::<Result<HashSet<_>, _>>()?;
572
573 // check that we didn't accidentally map two methods to the same variant
574 assert_eq!(methods.len(), method_names.len());
575
576 // check that a method's name and the name it is parsed from match
577 for method in methods {
578 assert_eq!(method.as_ref().parse::<Method>()?, method);
579 }
580
581 Ok(())
582 }
583}