mocktail/
request.rs

1//! Mock request
2use url::Url;
3
4use crate::{body::Body, headers::Headers};
5
6/// Represents a HTTP request.
7#[derive(Debug, Clone, PartialEq)]
8pub struct Request {
9    pub method: Method,
10    pub url: Url,
11    pub headers: Headers,
12    pub body: Body,
13}
14
15impl Request {
16    pub fn new(method: Method, url: Url) -> Self {
17        Self {
18            method,
19            url,
20            headers: Headers::default(),
21            body: Body::default(),
22        }
23    }
24
25    pub fn from_parts(parts: http::request::Parts) -> Self {
26        let url: Url = if parts.uri.authority().is_some() {
27            parts.uri.to_string()
28        } else {
29            format!("http://localhost{}", parts.uri)
30        }
31        .parse()
32        .unwrap();
33        Self {
34            method: parts.method.into(),
35            url,
36            headers: parts.headers.into(),
37            body: Body::default(),
38        }
39    }
40
41    pub fn with_headers(mut self, headers: Headers) -> Self {
42        self.headers = headers;
43        self
44    }
45
46    pub fn with_body(mut self, body: impl Into<Body>) -> Self {
47        self.body = body.into();
48        self
49    }
50
51    pub fn method(&self) -> &Method {
52        &self.method
53    }
54
55    pub fn url(&self) -> &Url {
56        &self.url
57    }
58
59    pub fn path(&self) -> &str {
60        self.url.path()
61    }
62
63    pub fn query(&self) -> Option<&str> {
64        self.url.query()
65    }
66
67    pub fn query_pairs(&self) -> url::form_urlencoded::Parse<'_> {
68        self.url.query_pairs()
69    }
70
71    pub fn headers(&self) -> &Headers {
72        &self.headers
73    }
74
75    pub fn body(&self) -> &Body {
76        &self.body
77    }
78}
79
80/// Represents a HTTP method.
81#[allow(clippy::upper_case_acronyms)]
82#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
83pub enum Method {
84    #[default]
85    GET,
86    HEAD,
87    POST,
88    PUT,
89    DELETE,
90    CONNECT,
91    OPTIONS,
92    TRACE,
93    PATCH,
94}
95
96impl std::fmt::Display for Method {
97    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
98        std::fmt::Debug::fmt(self, f)
99    }
100}
101
102impl std::str::FromStr for Method {
103    type Err = String;
104
105    fn from_str(value: &str) -> Result<Self, Self::Err> {
106        match value.to_uppercase().as_str() {
107            "GET" => Ok(Method::GET),
108            "HEAD" => Ok(Method::HEAD),
109            "POST" => Ok(Method::POST),
110            "PUT" => Ok(Method::PUT),
111            "DELETE" => Ok(Method::DELETE),
112            "CONNECT" => Ok(Method::CONNECT),
113            "OPTIONS" => Ok(Method::OPTIONS),
114            "TRACE" => Ok(Method::TRACE),
115            "PATCH" => Ok(Method::PATCH),
116            _ => Err(format!("Invalid HTTP method {value}")),
117        }
118    }
119}
120
121impl TryFrom<&str> for Method {
122    type Error = String;
123
124    fn try_from(value: &str) -> Result<Self, Self::Error> {
125        match value {
126            "POST" => Ok(Self::POST),
127            "GET" => Ok(Self::GET),
128            "HEAD" => Ok(Self::HEAD),
129            "PUT" => Ok(Self::PUT),
130            "DELETE" => Ok(Self::DELETE),
131            "CONNECT" => Ok(Self::CONNECT),
132            "OPTIONS" => Ok(Self::OPTIONS),
133            "TRACE" => Ok(Self::TRACE),
134            "PATCH" => Ok(Self::PATCH),
135            _ => Err(format!("Invalid HTTP method {value}")),
136        }
137    }
138}
139
140impl From<http::Method> for Method {
141    fn from(value: http::Method) -> Self {
142        match value.as_str() {
143            "GET" => Self::GET,
144            "HEAD" => Self::HEAD,
145            "POST" => Self::POST,
146            "PUT" => Self::PUT,
147            "DELETE" => Self::DELETE,
148            "CONNECT" => Self::CONNECT,
149            "OPTIONS" => Self::OPTIONS,
150            "TRACE" => Self::TRACE,
151            "PATCH" => Self::PATCH,
152            _ => unimplemented!(),
153        }
154    }
155}