1use url::Url;
3
4use crate::{body::Body, headers::Headers};
5
6#[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#[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}