pincer_core/
method.rs

1//! HTTP method types.
2
3use derive_more::Display;
4
5/// HTTP request method.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
7pub enum Method {
8    /// GET method - retrieve a resource.
9    #[display("GET")]
10    Get,
11    /// POST method - create a resource.
12    #[display("POST")]
13    Post,
14    /// PUT method - replace a resource.
15    #[display("PUT")]
16    Put,
17    /// DELETE method - remove a resource.
18    #[display("DELETE")]
19    Delete,
20    /// PATCH method - partially update a resource.
21    #[display("PATCH")]
22    Patch,
23    /// HEAD method - retrieve headers only.
24    #[display("HEAD")]
25    Head,
26    /// OPTIONS method - retrieve allowed methods.
27    #[display("OPTIONS")]
28    Options,
29}
30
31impl Method {
32    /// Returns `true` if the method is safe (does not modify resources).
33    #[must_use]
34    pub const fn is_safe(&self) -> bool {
35        matches!(self, Self::Get | Self::Head | Self::Options)
36    }
37
38    /// Returns `true` if the method is idempotent.
39    #[must_use]
40    pub const fn is_idempotent(&self) -> bool {
41        matches!(
42            self,
43            Self::Get | Self::Head | Self::Options | Self::Put | Self::Delete
44        )
45    }
46}
47
48impl From<Method> for http::Method {
49    fn from(method: Method) -> Self {
50        match method {
51            Method::Get => Self::GET,
52            Method::Post => Self::POST,
53            Method::Put => Self::PUT,
54            Method::Delete => Self::DELETE,
55            Method::Patch => Self::PATCH,
56            Method::Head => Self::HEAD,
57            Method::Options => Self::OPTIONS,
58        }
59    }
60}
61
62impl TryFrom<http::Method> for Method {
63    type Error = crate::Error;
64
65    fn try_from(method: http::Method) -> Result<Self, Self::Error> {
66        match method {
67            http::Method::GET => Ok(Self::Get),
68            http::Method::POST => Ok(Self::Post),
69            http::Method::PUT => Ok(Self::Put),
70            http::Method::DELETE => Ok(Self::Delete),
71            http::Method::PATCH => Ok(Self::Patch),
72            http::Method::HEAD => Ok(Self::Head),
73            http::Method::OPTIONS => Ok(Self::Options),
74            other => Err(crate::Error::InvalidRequest(format!(
75                "unsupported HTTP method: {other}"
76            ))),
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn method_display() {
87        assert_eq!(Method::Get.to_string(), "GET");
88        assert_eq!(Method::Post.to_string(), "POST");
89        assert_eq!(Method::Put.to_string(), "PUT");
90        assert_eq!(Method::Delete.to_string(), "DELETE");
91        assert_eq!(Method::Patch.to_string(), "PATCH");
92        assert_eq!(Method::Head.to_string(), "HEAD");
93        assert_eq!(Method::Options.to_string(), "OPTIONS");
94    }
95
96    #[test]
97    fn method_is_safe() {
98        assert!(Method::Get.is_safe());
99        assert!(Method::Head.is_safe());
100        assert!(Method::Options.is_safe());
101        assert!(!Method::Post.is_safe());
102        assert!(!Method::Put.is_safe());
103        assert!(!Method::Delete.is_safe());
104        assert!(!Method::Patch.is_safe());
105    }
106
107    #[test]
108    fn method_is_idempotent() {
109        assert!(Method::Get.is_idempotent());
110        assert!(Method::Head.is_idempotent());
111        assert!(Method::Options.is_idempotent());
112        assert!(Method::Put.is_idempotent());
113        assert!(Method::Delete.is_idempotent());
114        assert!(!Method::Post.is_idempotent());
115        assert!(!Method::Patch.is_idempotent());
116    }
117
118    #[test]
119    fn method_into_http() {
120        assert_eq!(http::Method::from(Method::Get), http::Method::GET);
121        assert_eq!(http::Method::from(Method::Post), http::Method::POST);
122    }
123
124    #[test]
125    fn method_from_http() {
126        assert_eq!(
127            Method::try_from(http::Method::GET).expect("GET"),
128            Method::Get
129        );
130        assert_eq!(
131            Method::try_from(http::Method::POST).expect("POST"),
132            Method::Post
133        );
134    }
135}