chuchi_core/request/
mod.rs

1mod builder;
2pub use builder::RequestBuilder;
3
4use crate::body::Body;
5#[cfg(feature = "json")]
6use crate::header::CONTENT_TYPE;
7use crate::header::{RequestHeader, Uri};
8
9use std::time::Duration;
10
11/// The request that is received from a client.
12#[derive(Debug)]
13pub struct Request {
14	pub header: RequestHeader,
15	pub body: Body,
16}
17
18impl Request {
19	/// Creates a new `Request`.
20	pub fn new(header: RequestHeader, body: Body) -> Self {
21		Self { header, body }
22	}
23
24	/// Creates a new `Request` with a builder.
25	pub fn builder(uri: Uri) -> RequestBuilder {
26		RequestBuilder::new(uri)
27	}
28
29	/// Takes the body replacing it with an empty one.
30	pub fn take_body(&mut self) -> Body {
31		self.body.take()
32	}
33
34	/// Get the request header by reference.
35	pub fn header(&self) -> &RequestHeader {
36		&self.header
37	}
38
39	/// Sets a read size limit.
40	pub fn set_size_limit(&mut self, size: Option<usize>) {
41		self.body.set_size_limit(size)
42	}
43
44	/// Sets a read timeout, the timer starts counting after you call into_*
45	pub fn set_timeout(&mut self, timeout: Option<Duration>) {
46		self.body.set_timeout(timeout)
47	}
48
49	/// Tries to deserialize the request body.
50	///
51	/// ## Errors
52	/// - If the header `content-type` does not contain `application/json`.
53	/// - If the body does not contain a valid json or some data is missing.
54	#[cfg(feature = "json")]
55	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
56	pub async fn deserialize<D>(&mut self) -> Result<D, DeserializeError>
57	where
58		D: serde::de::DeserializeOwned + Send + 'static,
59	{
60		use crate::header::Mime;
61
62		// try to read mime
63		// this will not work if content-type has charset
64		// TODO allow charset (probably implement Parse for ContentType)
65		let raw_content_type = self
66			.header()
67			.value(CONTENT_TYPE)
68			.ok_or(DeserializeError::NoContentType)?;
69		let mime: Mime = raw_content_type.trim().parse().map_err(|_| {
70			DeserializeError::UnknownContentType(raw_content_type.to_string())
71		})?;
72
73		if mime != Mime::JSON {
74			return Err(DeserializeError::WrongMimeType(mime));
75		}
76
77		// now parse body
78		self.body
79			.take()
80			.deserialize()
81			.await
82			.map_err(DeserializeError::Json)
83	}
84
85	#[cfg(feature = "query")]
86	#[cfg_attr(docsrs, doc(cfg(feature = "query")))]
87	pub fn deserialize_query<D>(&self) -> Result<D, DeserializeError>
88	where
89		D: serde::de::DeserializeOwned + Send + 'static,
90	{
91		let query = self.header().uri().query().unwrap_or("");
92
93		serde_urlencoded::from_str(query)
94			.map_err(DeserializeError::UrlEncoded)
95	}
96}
97
98#[cfg(any(feature = "json", feature = "query"))]
99mod serde_error {
100	use crate::header::Mime;
101
102	use std::fmt;
103
104	#[derive(Debug)]
105	#[non_exhaustive]
106	pub enum DeserializeError {
107		NoContentType,
108		UnknownContentType(String),
109		WrongMimeType(Mime),
110		#[cfg(feature = "json")]
111		Json(serde_json::Error),
112		#[cfg(feature = "query")]
113		UrlEncoded(serde::de::value::Error),
114	}
115
116	impl fmt::Display for DeserializeError {
117		fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118			write!(f, "Failed to deserialize request {:?}", self)
119		}
120	}
121
122	impl std::error::Error for DeserializeError {}
123
124	#[derive(Debug)]
125	#[non_exhaustive]
126	pub enum SerializeError {
127		#[cfg(feature = "json")]
128		Json(serde_json::Error),
129		#[cfg(feature = "query")]
130		UrlEncoded(serde_urlencoded::ser::Error),
131	}
132
133	impl fmt::Display for SerializeError {
134		fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135			write!(f, "Failed to serialize request {:?}", self)
136		}
137	}
138
139	impl std::error::Error for SerializeError {}
140}
141
142#[cfg(any(feature = "json", feature = "query"))]
143pub use serde_error::*;
144
145#[cfg(test)]
146mod tests {
147	#[allow(unused_imports)]
148	use super::*;
149
150	#[cfg(feature = "query")]
151	#[tokio::test]
152	async fn deserialize_query() {
153		let uri = "http://localhost:8080/?a=1&b=2";
154		let req = Request::builder(uri.parse().unwrap()).build();
155
156		#[derive(serde::Deserialize)]
157		struct Query {
158			a: String,
159			b: String,
160			c: Option<String>,
161		}
162
163		let query: Query = req.deserialize_query().unwrap();
164		assert_eq!(query.a, "1");
165		assert_eq!(query.b, "2");
166		assert_eq!(query.c, None);
167	}
168}