fire_http_representation/request/
builder.rs

1use super::Request;
2use crate::body::Body;
3use crate::header::{
4	values::IntoHeaderName, ContentType, HeaderValue, HeaderValues, Method,
5	RequestHeader, Uri, CONTENT_LENGTH, CONTENT_TYPE,
6};
7
8use std::fmt;
9use std::net::SocketAddr;
10
11/// A builder to create a `Request`.
12///
13/// This is only useful for direct request calling on a FirePit.
14#[derive(Debug)]
15pub struct RequestBuilder {
16	header: RequestHeader,
17	body: Body,
18}
19
20impl RequestBuilder {
21	/// Creates a new `RequestBuilder`.
22	pub fn new(uri: Uri) -> Self {
23		Self {
24			header: RequestHeader {
25				address: ([127, 0, 0, 1], 0).into(),
26				method: Method::GET,
27				uri,
28				values: HeaderValues::new(),
29			},
30			body: Body::new(),
31		}
32	}
33
34	/// Sets the address.
35	pub fn address(mut self, addr: impl Into<SocketAddr>) -> Self {
36		self.header.address = addr.into();
37		self
38	}
39
40	/// Set the method.
41	pub fn method(mut self, method: Method) -> Self {
42		self.header.method = method;
43		self
44	}
45
46	/// Sets the content type.
47	pub fn content_type(self, content_type: impl Into<ContentType>) -> Self {
48		self.header(CONTENT_TYPE, content_type.into())
49	}
50
51	/// Sets a header value.
52	///
53	/// ## Note
54	/// Only ASCII characters are allowed, use
55	/// `self.values_mut().insert_encoded()` to allow any character.
56	///
57	/// ## Panics
58	/// If the value is not a valid `HeaderValue`.
59	pub fn header<K, V>(mut self, key: K, val: V) -> Self
60	where
61		K: IntoHeaderName,
62		V: TryInto<HeaderValue>,
63		V::Error: fmt::Debug,
64	{
65		self.values_mut().insert(key, val);
66		self
67	}
68
69	/// Returns `HeaderValues` mutably.
70	pub fn values_mut(&mut self) -> &mut HeaderValues {
71		&mut self.header.values
72	}
73
74	/// Sets the body dropping the previous one.
75	pub fn body(mut self, body: impl Into<Body>) -> Self {
76		self.body = body.into();
77		self
78	}
79
80	/// Sets the body from a serialized value
81	#[cfg(feature = "json")]
82	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
83	pub fn serialize<S: ?Sized>(
84		self,
85		value: &S,
86	) -> Result<Self, super::SerializeError>
87	where
88		S: serde::Serialize,
89	{
90		Ok(self.body(
91			Body::serialize(value)
92				.map_err(|e| super::SerializeError::Json(e))?,
93		))
94	}
95
96	/// Serializes the value to a query string and appends it to the path.
97	///
98	/// ## Note
99	/// This replaces the previous query.
100	#[cfg(feature = "query")]
101	#[cfg_attr(docsrs, doc(cfg(feature = "query")))]
102	pub fn serialize_query<S: ?Sized>(
103		mut self,
104		value: &S,
105	) -> Result<Self, super::SerializeError>
106	where
107		S: serde::Serialize,
108	{
109		let mut parts = self.header.uri.into_parts();
110
111		let query = serde_urlencoded::to_string(&value)
112			.map_err(|e| super::SerializeError::UrlEncoded(e))?;
113
114		parts.path_and_query = Some(
115			format!(
116				"{}?{}",
117				parts
118					.path_and_query
119					.as_ref()
120					.map(|p| p.path())
121					.unwrap_or("/"),
122				query
123			)
124			.parse()
125			.expect("serde_urlencoded should always return a valid query"),
126		);
127
128		self.header.uri = Uri::from_parts(parts).unwrap();
129
130		Ok(self)
131	}
132
133	/// Builds a `Request`. Adding the `content-length` header
134	/// if the len of the body is known.
135	pub fn build(mut self) -> Request {
136		// lets calculate content-length
137		// if the body size is already known
138		if let Some(len) = self.body.len() {
139			self.values_mut().insert(CONTENT_LENGTH, len);
140		}
141
142		Request::new(self.header, self.body)
143	}
144}