1use std::{
4 fmt,
5 future::{Ready, ready},
6};
7
8use actix_web::{FromRequest, HttpRequest, ResponseError, dev::Payload, http::StatusCode};
9use derive_more::Error;
10use serde::de::DeserializeOwned;
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
67pub struct Query<T>(pub T);
68
69impl_more::impl_deref_and_mut!(<T> in Query<T> => T);
70impl_more::forward_display!(<T> in Query<T>);
71
72impl<T> Query<T> {
73 pub fn into_inner(self) -> T {
75 self.0
76 }
77}
78
79impl<T: DeserializeOwned> Query<T> {
80 pub fn from_query(query_str: &str) -> Result<Self, QueryDeserializeError> {
92 let parser = form_urlencoded::parse(query_str.as_bytes());
93 let de = serde_html_form::Deserializer::new(parser);
94
95 serde_path_to_error::deserialize(de)
96 .map(Self)
97 .map_err(|err| QueryDeserializeError {
98 path: err.path().clone(),
99 source: err.into_inner(),
100 })
101 }
102}
103
104impl<T: DeserializeOwned> FromRequest for Query<T> {
106 type Error = QueryDeserializeError;
107 type Future = Ready<Result<Self, Self::Error>>;
108
109 #[inline]
110 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
111 ready(Self::from_query(req.query_string()).inspect_err(|err| {
112 tracing::debug!(
113 "Failed during Query extractor deserialization. \
114 Request path: \"{}\". \
115 Error path: \"{}\".",
116 req.match_name().unwrap_or(req.path()),
117 err.path(),
118 );
119 }))
120 }
121}
122
123#[derive(Debug, Error)]
125pub struct QueryDeserializeError {
126 path: serde_path_to_error::Path,
128
129 source: serde_html_form::de::Error,
131}
132
133impl QueryDeserializeError {
134 pub fn path(&self) -> impl fmt::Display + '_ {
136 &self.path
137 }
138
139 pub fn source(&self) -> &serde_html_form::de::Error {
141 &self.source
142 }
143}
144
145impl fmt::Display for QueryDeserializeError {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.write_str("Query deserialization failed")?;
148
149 if self.path.iter().len() > 0 {
150 write!(f, " at path: {}", &self.path)?;
151 }
152
153 Ok(())
154 }
155}
156
157impl ResponseError for QueryDeserializeError {
158 fn status_code(&self) -> StatusCode {
159 StatusCode::UNPROCESSABLE_ENTITY
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use actix_web::test::TestRequest;
166 use derive_more::Display;
167 use serde::Deserialize;
168
169 use super::*;
170
171 #[derive(Deserialize, Debug, Display)]
172 struct Id {
173 id: String,
174 }
175
176 #[actix_web::test]
177 async fn test_service_request_extract() {
178 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
179 assert!(Query::<Id>::from_query(req.query_string()).is_err());
180
181 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
182 let mut s = Query::<Id>::from_query(req.query_string()).unwrap();
183
184 assert_eq!(s.id, "test");
185 assert_eq!(format!("{s}, {s:?}"), "test, Query(Id { id: \"test\" })");
186
187 s.id = "test1".to_string();
188 let s = s.into_inner();
189 assert_eq!(s.id, "test1");
190 }
191
192 #[actix_web::test]
193 async fn extract_array() {
194 #[derive(Debug, Deserialize)]
195 struct Test {
196 #[serde(rename = "user")]
197 users: Vec<String>,
198 }
199
200 let req = TestRequest::with_uri("/?user=foo&user=bar").to_srv_request();
201 let s = Query::<Test>::from_query(req.query_string()).unwrap();
202
203 assert_eq!(s.users[0], "foo");
204 assert_eq!(s.users[1], "bar");
205 }
206
207 #[actix_web::test]
208 async fn test_request_extract() {
209 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
210 let (req, mut pl) = req.into_parts();
211 assert!(Query::<Id>::from_request(&req, &mut pl).await.is_err());
212
213 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
214 let (req, mut pl) = req.into_parts();
215
216 let mut s = Query::<Id>::from_request(&req, &mut pl).await.unwrap();
217 assert_eq!(s.id, "test");
218 assert_eq!(format!("{s}, {s:?}"), "test, Query(Id { id: \"test\" })");
219
220 s.id = "test1".to_string();
221 let s = s.into_inner();
222 assert_eq!(s.id, "test1");
223 }
224
225 #[actix_web::test]
226 #[should_panic]
227 async fn test_tuple_panic() {
228 let req = TestRequest::with_uri("/?one=1&two=2").to_srv_request();
229 let (req, mut pl) = req.into_parts();
230
231 Query::<(u32, u32)>::from_request(&req, &mut pl)
232 .await
233 .unwrap();
234 }
235}