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}