Skip to main content

nanofish_client/
method.rs

1/// HTTP Methods supported by the client
2///
3/// This enum represents the standard HTTP methods that can be used
4/// when making requests with the `HttpClient`.
5#[derive(Clone, Copy, Debug, PartialEq)]
6pub enum HttpMethod {
7    /// The GET method requests a representation of the specified resource.
8    /// Requests using GET should only retrieve data.
9    GET,
10    /// The POST method is used to submit an entity to the specified resource,
11    /// often causing a change in state or side effects on the server.
12    POST,
13    /// The PUT method replaces all current representations of the target
14    /// resource with the request payload.
15    PUT,
16    /// The DELETE method deletes the specified resource.
17    DELETE,
18    /// The PATCH method is used to apply partial modifications to a resource.
19    PATCH,
20    /// The CONNECT method establishes a tunnel to the server identified by the target resource.
21    CONNECT,
22    /// The OPTIONS method is used to describe the communication options for the target resource.
23    OPTIONS,
24    /// The TRACE method performs a message loop-back test along the path to the target resource.
25    TRACE,
26    /// The HEAD method asks for a response identical to that of a GET request,
27    /// but without the response body.
28    HEAD,
29}
30
31/// Error type for invalid HTTP methods
32#[derive(Debug, Clone, Copy, PartialEq)]
33pub struct InvalidHttpMethod;
34
35impl core::fmt::Display for InvalidHttpMethod {
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        write!(f, "Invalid HTTP method")
38    }
39}
40
41impl HttpMethod {
42    #[must_use]
43    /// Returns the string representation of the HTTP method.
44    pub fn as_str(self) -> &'static str {
45        match self {
46            HttpMethod::GET => "GET",
47            HttpMethod::POST => "POST",
48            HttpMethod::PUT => "PUT",
49            HttpMethod::DELETE => "DELETE",
50            HttpMethod::PATCH => "PATCH",
51            HttpMethod::CONNECT => "CONNECT",
52            HttpMethod::OPTIONS => "OPTIONS",
53            HttpMethod::TRACE => "TRACE",
54            HttpMethod::HEAD => "HEAD",
55        }
56    }
57}
58
59impl TryFrom<&str> for HttpMethod {
60    type Error = InvalidHttpMethod;
61
62    fn try_from(value: &str) -> Result<Self, Self::Error> {
63        match value {
64            "GET" => Ok(HttpMethod::GET),
65            "POST" => Ok(HttpMethod::POST),
66            "PUT" => Ok(HttpMethod::PUT),
67            "DELETE" => Ok(HttpMethod::DELETE),
68            "PATCH" => Ok(HttpMethod::PATCH),
69            "HEAD" => Ok(HttpMethod::HEAD),
70            "OPTIONS" => Ok(HttpMethod::OPTIONS),
71            "TRACE" => Ok(HttpMethod::TRACE),
72            "CONNECT" => Ok(HttpMethod::CONNECT),
73            _ => Err(InvalidHttpMethod),
74        }
75    }
76}
77
78impl TryFrom<&[u8]> for HttpMethod {
79    type Error = InvalidHttpMethod;
80
81    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
82        match value {
83            b"GET" => Ok(HttpMethod::GET),
84            b"POST" => Ok(HttpMethod::POST),
85            b"PUT" => Ok(HttpMethod::PUT),
86            b"DELETE" => Ok(HttpMethod::DELETE),
87            b"PATCH" => Ok(HttpMethod::PATCH),
88            b"HEAD" => Ok(HttpMethod::HEAD),
89            b"OPTIONS" => Ok(HttpMethod::OPTIONS),
90            b"TRACE" => Ok(HttpMethod::TRACE),
91            b"CONNECT" => Ok(HttpMethod::CONNECT),
92            _ => Err(InvalidHttpMethod),
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_http_method_as_str() {
103        assert_eq!(HttpMethod::GET.as_str(), "GET");
104        assert_eq!(HttpMethod::POST.as_str(), "POST");
105        assert_eq!(HttpMethod::PUT.as_str(), "PUT");
106        assert_eq!(HttpMethod::DELETE.as_str(), "DELETE");
107        assert_eq!(HttpMethod::PATCH.as_str(), "PATCH");
108        assert_eq!(HttpMethod::CONNECT.as_str(), "CONNECT");
109        assert_eq!(HttpMethod::OPTIONS.as_str(), "OPTIONS");
110        assert_eq!(HttpMethod::TRACE.as_str(), "TRACE");
111        assert_eq!(HttpMethod::HEAD.as_str(), "HEAD");
112    }
113
114    #[test]
115    fn test_try_from_str() {
116        // Test valid HTTP methods
117        assert_eq!(HttpMethod::try_from("GET"), Ok(HttpMethod::GET));
118        assert_eq!(HttpMethod::try_from("POST"), Ok(HttpMethod::POST));
119        assert_eq!(HttpMethod::try_from("PUT"), Ok(HttpMethod::PUT));
120        assert_eq!(HttpMethod::try_from("DELETE"), Ok(HttpMethod::DELETE));
121        assert_eq!(HttpMethod::try_from("PATCH"), Ok(HttpMethod::PATCH));
122        assert_eq!(HttpMethod::try_from("HEAD"), Ok(HttpMethod::HEAD));
123        assert_eq!(HttpMethod::try_from("OPTIONS"), Ok(HttpMethod::OPTIONS));
124        assert_eq!(HttpMethod::try_from("TRACE"), Ok(HttpMethod::TRACE));
125        assert_eq!(HttpMethod::try_from("CONNECT"), Ok(HttpMethod::CONNECT));
126
127        // Test invalid HTTP methods
128        assert_eq!(HttpMethod::try_from("get"), Err(InvalidHttpMethod));
129        assert_eq!(HttpMethod::try_from("INVALID"), Err(InvalidHttpMethod));
130        assert_eq!(HttpMethod::try_from(""), Err(InvalidHttpMethod));
131        assert_eq!(HttpMethod::try_from("123"), Err(InvalidHttpMethod));
132    }
133
134    #[test]
135    fn test_try_from_bytes() {
136        // Test valid HTTP methods
137        assert_eq!(HttpMethod::try_from(b"GET".as_slice()), Ok(HttpMethod::GET));
138        assert_eq!(
139            HttpMethod::try_from(b"POST".as_slice()),
140            Ok(HttpMethod::POST)
141        );
142        assert_eq!(HttpMethod::try_from(b"PUT".as_slice()), Ok(HttpMethod::PUT));
143        assert_eq!(
144            HttpMethod::try_from(b"DELETE".as_slice()),
145            Ok(HttpMethod::DELETE)
146        );
147        assert_eq!(
148            HttpMethod::try_from(b"PATCH".as_slice()),
149            Ok(HttpMethod::PATCH)
150        );
151        assert_eq!(
152            HttpMethod::try_from(b"HEAD".as_slice()),
153            Ok(HttpMethod::HEAD)
154        );
155        assert_eq!(
156            HttpMethod::try_from(b"OPTIONS".as_slice()),
157            Ok(HttpMethod::OPTIONS)
158        );
159        assert_eq!(
160            HttpMethod::try_from(b"TRACE".as_slice()),
161            Ok(HttpMethod::TRACE)
162        );
163        assert_eq!(
164            HttpMethod::try_from(b"CONNECT".as_slice()),
165            Ok(HttpMethod::CONNECT)
166        );
167
168        // Test invalid HTTP methods
169        assert_eq!(
170            HttpMethod::try_from(b"get".as_slice()),
171            Err(InvalidHttpMethod)
172        );
173        assert_eq!(
174            HttpMethod::try_from(b"INVALID".as_slice()),
175            Err(InvalidHttpMethod)
176        );
177        assert_eq!(HttpMethod::try_from(b"".as_slice()), Err(InvalidHttpMethod));
178        assert_eq!(
179            HttpMethod::try_from(b"123".as_slice()),
180            Err(InvalidHttpMethod)
181        );
182    }
183
184    #[test]
185    fn test_invalid_http_method_display() {
186        let error = InvalidHttpMethod;
187        assert_eq!(format!("{error}"), "Invalid HTTP method");
188    }
189
190    #[test]
191    fn test_roundtrip_str_conversion() {
192        let methods = [
193            HttpMethod::GET,
194            HttpMethod::POST,
195            HttpMethod::PUT,
196            HttpMethod::DELETE,
197            HttpMethod::PATCH,
198            HttpMethod::HEAD,
199            HttpMethod::OPTIONS,
200            HttpMethod::TRACE,
201            HttpMethod::CONNECT,
202        ];
203
204        for method in &methods {
205            let str_repr = method.as_str();
206            let parsed = HttpMethod::try_from(str_repr).unwrap();
207            assert_eq!(*method, parsed);
208        }
209    }
210}