1use std::collections::HashMap;
18
19use bytes::Bytes;
20use http::Extensions;
21
22use crate::Method;
23
24#[derive(Debug, Clone)]
26pub struct Request<B = Bytes> {
27 method: Method,
28 url: url::Url,
29 headers: HashMap<String, String>,
30 body: Option<B>,
31 extensions: Extensions,
32}
33
34impl<B> Request<B> {
35 #[must_use]
37 pub fn builder(method: Method, url: url::Url) -> RequestBuilder<B> {
38 RequestBuilder::new(method, url)
39 }
40
41 #[must_use]
43 pub const fn method(&self) -> Method {
44 self.method
45 }
46
47 #[must_use]
49 pub fn url(&self) -> &url::Url {
50 &self.url
51 }
52
53 #[must_use]
55 pub fn headers(&self) -> &HashMap<String, String> {
56 &self.headers
57 }
58
59 #[must_use]
61 pub fn headers_mut(&mut self) -> &mut HashMap<String, String> {
62 &mut self.headers
63 }
64
65 #[must_use]
67 pub fn header(&self, name: &str) -> Option<&str> {
68 self.headers.get(name).map(String::as_str)
69 }
70
71 #[must_use]
73 pub const fn body(&self) -> Option<&B> {
74 self.body.as_ref()
75 }
76
77 #[must_use]
81 pub fn extensions(&self) -> &Extensions {
82 &self.extensions
83 }
84
85 #[must_use]
87 pub fn extensions_mut(&mut self) -> &mut Extensions {
88 &mut self.extensions
89 }
90
91 #[must_use]
93 pub fn into_parts(
94 self,
95 ) -> (
96 Method,
97 url::Url,
98 HashMap<String, String>,
99 Option<B>,
100 Extensions,
101 ) {
102 (
103 self.method,
104 self.url,
105 self.headers,
106 self.body,
107 self.extensions,
108 )
109 }
110
111 #[must_use]
116 pub fn from_parts(
117 method: Method,
118 url: url::Url,
119 headers: HashMap<String, String>,
120 body: Option<B>,
121 extensions: Extensions,
122 ) -> Self {
123 Self {
124 method,
125 url,
126 headers,
127 body,
128 extensions,
129 }
130 }
131}
132
133#[derive(Debug, Clone)]
135pub struct RequestBuilder<B = Bytes> {
136 method: Method,
137 url: url::Url,
138 headers: HashMap<String, String>,
139 body: Option<B>,
140 extensions: Extensions,
141}
142
143impl<B> RequestBuilder<B> {
144 #[must_use]
146 pub fn new(method: Method, url: url::Url) -> Self {
147 Self {
148 method,
149 url,
150 headers: HashMap::new(),
151 body: None,
152 extensions: Extensions::new(),
153 }
154 }
155
156 #[must_use]
158 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
159 self.headers.insert(name.into(), value.into());
160 self
161 }
162
163 #[must_use]
165 pub fn headers(mut self, headers: impl IntoIterator<Item = (String, String)>) -> Self {
166 self.headers.extend(headers);
167 self
168 }
169
170 #[must_use]
172 pub fn query(mut self, name: &str, value: &str) -> Self {
173 self.url.query_pairs_mut().append_pair(name, value);
174 self
175 }
176
177 #[must_use]
179 pub fn query_pairs(mut self, pairs: impl IntoIterator<Item = (String, String)>) -> Self {
180 {
181 let mut query = self.url.query_pairs_mut();
182 for (name, value) in pairs {
183 query.append_pair(&name, &value);
184 }
185 }
186 self
187 }
188
189 #[must_use]
191 pub fn body(mut self, body: B) -> Self {
192 self.body = Some(body);
193 self
194 }
195
196 #[must_use]
200 pub fn extension<T: Clone + Send + Sync + 'static>(mut self, value: T) -> Self {
201 self.extensions.insert(value);
202 self
203 }
204
205 #[must_use]
209 pub fn extensions(mut self, extensions: Extensions) -> Self {
210 self.extensions = extensions;
211 self
212 }
213
214 #[must_use]
216 pub fn build(self) -> Request<B> {
217 Request {
218 method: self.method,
219 url: self.url,
220 headers: self.headers,
221 body: self.body,
222 extensions: self.extensions,
223 }
224 }
225}
226
227impl RequestBuilder<Bytes> {
228 pub fn json<T: serde::Serialize>(self, value: &T) -> crate::Result<Self> {
234 let body = crate::to_json(value)?;
235 Ok(self.header("Content-Type", "application/json").body(body))
236 }
237
238 pub fn form<T: serde::Serialize>(self, value: &T) -> crate::Result<Self> {
244 let body = crate::to_form(value)?;
245 Ok(self
246 .header("Content-Type", "application/x-www-form-urlencoded")
247 .body(body))
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn request_builder_basic() {
257 let url = url::Url::parse("https://api.example.com/users").expect("valid URL");
258 let request = Request::<Bytes>::builder(Method::Get, url.clone())
259 .header("Accept", "application/json")
260 .build();
261
262 assert_eq!(request.method(), Method::Get);
263 assert_eq!(request.url().as_str(), "https://api.example.com/users");
264 assert_eq!(request.header("Accept"), Some("application/json"));
265 assert!(request.body().is_none());
266 }
267
268 #[test]
269 fn request_builder_with_query() {
270 let url = url::Url::parse("https://api.example.com/users").expect("valid URL");
271 let request = Request::<Bytes>::builder(Method::Get, url)
272 .query("page", "1")
273 .query("limit", "10")
274 .build();
275
276 assert_eq!(
277 request.url().as_str(),
278 "https://api.example.com/users?page=1&limit=10"
279 );
280 }
281
282 #[test]
283 fn request_builder_with_body() {
284 let url = url::Url::parse("https://api.example.com/users").expect("valid URL");
285 let body = Bytes::from(r#"{"name":"test"}"#);
286 let request = Request::builder(Method::Post, url)
287 .header("Content-Type", "application/json")
288 .body(body.clone())
289 .build();
290
291 assert_eq!(request.method(), Method::Post);
292 assert_eq!(request.body(), Some(&body));
293 }
294
295 #[test]
296 fn request_builder_json() {
297 #[derive(serde::Serialize)]
298 struct User {
299 name: String,
300 }
301
302 let url = url::Url::parse("https://api.example.com/users").expect("valid URL");
303 let request = Request::builder(Method::Post, url)
304 .json(&User {
305 name: "test".to_string(),
306 })
307 .expect("json")
308 .build();
309
310 assert_eq!(request.header("Content-Type"), Some("application/json"));
311 assert!(request.body().is_some());
312 }
313
314 #[test]
315 fn request_extensions() {
316 #[derive(Debug, Clone, PartialEq)]
317 struct RequestId(u64);
318
319 let url = url::Url::parse("https://api.example.com").expect("valid URL");
320 let mut request = Request::<Bytes>::builder(Method::Get, url)
321 .extension(RequestId(42))
322 .build();
323
324 assert_eq!(
326 request.extensions().get::<RequestId>(),
327 Some(&RequestId(42))
328 );
329
330 request.extensions_mut().insert(RequestId(100));
332 assert_eq!(
333 request.extensions().get::<RequestId>(),
334 Some(&RequestId(100))
335 );
336 }
337
338 #[test]
339 fn request_from_parts_roundtrip() {
340 #[derive(Debug, Clone, PartialEq)]
341 struct TraceId(String);
342
343 let url = url::Url::parse("https://api.example.com").expect("valid URL");
344 let request = Request::<Bytes>::builder(Method::Post, url)
345 .header("Content-Type", "application/json")
346 .body(Bytes::from(r#"{"name":"test"}"#))
347 .extension(TraceId("abc123".into()))
348 .build();
349
350 let (method, url, headers, body, extensions) = request.into_parts();
351 let reconstructed = Request::from_parts(method, url, headers, body, extensions);
352
353 assert_eq!(reconstructed.method(), Method::Post);
354 assert_eq!(
355 reconstructed.header("Content-Type"),
356 Some("application/json")
357 );
358 assert!(reconstructed.body().is_some());
359 assert_eq!(
360 reconstructed.extensions().get::<TraceId>(),
361 Some(&TraceId("abc123".into()))
362 );
363 }
364
365 #[test]
366 fn request_builder_extensions_replace() {
367 #[derive(Debug, Clone, PartialEq)]
368 struct Marker(u32);
369
370 let url = url::Url::parse("https://api.example.com").expect("valid URL");
371
372 let mut ext = Extensions::new();
373 ext.insert(Marker(99));
374
375 let request = Request::<Bytes>::builder(Method::Get, url)
376 .extension(Marker(1)) .extensions(ext)
378 .build();
379
380 assert_eq!(request.extensions().get::<Marker>(), Some(&Marker(99)));
381 }
382}