volga/http/endpoints/args/
path.rs1use crate::{HttpRequest, error::Error};
4use futures_util::future::{ready, ok, Ready};
5use hyper::http::{request::Parts, Extensions};
6use serde::de::DeserializeOwned;
7
8use std::{
9 net::{IpAddr, SocketAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
10 ffi::{CString, OsString},
11 path::PathBuf,
12 num::NonZero,
13 borrow::Cow,
14 fmt::{self, Display, Formatter},
15 ops::{Deref, DerefMut}
16};
17
18use crate::http::endpoints::{
19 route::{PathArg, PathArgs},
20 args::{
21 FromPayload,
22 FromRequestParts,
23 FromRequestRef,
24 Payload, Source
25 }
26};
27
28#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
45pub struct Path<T>(pub T);
46
47impl<T> Path<T> {
48 #[inline]
50 pub fn into_inner(self) -> T {
51 self.0
52 }
53}
54
55impl<T> Deref for Path<T> {
56 type Target = T;
57
58 #[inline]
59 fn deref(&self) -> &T {
60 &self.0
61 }
62}
63
64impl<T> DerefMut for Path<T> {
65 #[inline]
66 fn deref_mut(&mut self) -> &mut T {
67 &mut self.0
68 }
69}
70
71impl<T: Display> Display for Path<T> {
72 #[inline]
73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 self.0.fmt(f)
75 }
76}
77
78impl<T: DeserializeOwned> Path<T> {
79 #[inline]
81 pub(crate) fn from_slice(route_params: &PathArgs) -> Result<Self, Error> {
82 let route_str = PathArg::make_query_str(route_params)?;
83 serde_urlencoded::from_str::<T>(&route_str)
84 .map(Path)
85 .map_err(PathError::from_serde_error)
86 }
87}
88
89impl<T: DeserializeOwned + Send> TryFrom<&Extensions> for Path<T> {
90 type Error = Error;
91
92 #[inline]
93 fn try_from(extensions: &Extensions) -> Result<Self, Error> {
94 extensions
95 .get::<PathArgs>()
96 .ok_or_else(PathError::args_missing)
97 .and_then(|params| Self::from_slice(params))
98 }
99}
100
101impl<T: DeserializeOwned + Send> TryFrom<&Parts> for Path<T> {
102 type Error = Error;
103
104 #[inline]
105 fn try_from(parts: &Parts) -> Result<Self, Error> {
106 let ext = &parts.extensions;
107 ext.try_into()
108 }
109}
110
111impl<T: DeserializeOwned + Send> FromRequestParts for Path<T> {
114 #[inline]
115 fn from_parts(parts: &Parts) -> Result<Self, Error> {
116 parts.try_into()
117 }
118}
119
120impl<T: DeserializeOwned + Send> FromRequestRef for Path<T> {
123 #[inline]
124 fn from_request(req: &HttpRequest) -> Result<Self, Error> {
125 req.extensions().try_into()
126 }
127}
128
129impl<T: DeserializeOwned + Send> FromPayload for Path<T> {
132 type Future = Ready<Result<Self, Error>>;
133
134 #[inline]
135 fn from_payload(payload: Payload<'_>) -> Self::Future {
136 let Payload::PathArgs(params) = payload else { unreachable!() };
137 ready(Self::from_slice(¶ms))
138 }
139
140 #[inline]
141 fn source() -> Source {
142 Source::PathArgs
143 }
144}
145
146impl FromPayload for String {
147 type Future = Ready<Result<Self, Error>>;
148
149 #[inline]
150 fn from_payload(payload: Payload<'_>) -> Self::Future {
151 let Payload::Path(param) = payload else { unreachable!() };
152 ok(param.value.into_string())
153 }
154
155 #[inline]
156 fn source() -> Source {
157 Source::Path
158 }
159}
160
161impl FromPayload for Cow<'static, str> {
162 type Future = Ready<Result<Self, Error>>;
163
164 #[inline]
165 fn from_payload(payload: Payload<'_>) -> Self::Future {
166 let Payload::Path(param) = payload else { unreachable!() };
167 ok(Cow::Owned(param.value.into_string()))
168 }
169
170 #[inline]
171 fn source() -> Source {
172 Source::Path
173 }
174}
175
176impl FromPayload for Box<str> {
177 type Future = Ready<Result<Self, Error>>;
178
179 #[inline]
180 fn from_payload(payload: Payload<'_>) -> Self::Future {
181 let Payload::Path(param) = payload else { unreachable!() };
182 ok(param.value)
183 }
184
185 #[inline]
186 fn source() -> Source {
187 Source::Path
188 }
189}
190
191impl FromPayload for Box<[u8]> {
192 type Future = Ready<Result<Self, Error>>;
193
194 #[inline]
195 fn from_payload(payload: Payload<'_>) -> Self::Future {
196 let Payload::Path(param) = payload else { unreachable!() };
197 ok(param.value.into_boxed_bytes())
198 }
199
200 #[inline]
201 fn source() -> Source {
202 Source::Path
203 }
204}
205
206macro_rules! impl_from_payload {
207 { $($type:ty),* $(,)? } => {
208 $(impl FromPayload for $type {
209 type Future = Ready<Result<Self, Error>>;
210 #[inline]
211 fn from_payload(payload: Payload<'_>) -> Self::Future {
212 let Payload::Path(param) = payload else { unreachable!() };
213 ready(param.value
214 .parse::<$type>()
215 .map_err(|_| PathError::type_mismatch(param.name.as_ref())))
216 }
217 #[inline]
218 fn source() -> Source {
219 Source::Path
220 }
221 })*
222 };
223}
224
225impl_from_payload! {
226 bool,
227 char,
228 i8, i16, i32, i64, i128, isize,
229 u8, u16, u32, u64, u128, usize,
230 f32, f64,
231 NonZero<i8>, NonZero<i16>, NonZero<i32>, NonZero<i64>, NonZero<i128>, NonZero<isize>,
232 NonZero<u8>, NonZero<u16>, NonZero<u32>, NonZero<u64>, NonZero<u128>, NonZero<usize>,
233 IpAddr, SocketAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6,
234 CString, OsString,
235 PathBuf
236}
237
238struct PathError;
240
241impl PathError {
242 #[inline]
243 fn from_serde_error(err: serde::de::value::Error) -> Error {
244 Error::client_error(format!("Path parsing error: {err}"))
245 }
246
247 #[inline]
248 fn type_mismatch(arg: &str) -> Error {
249 Error::client_error(format!("Path parsing error: argument `{arg}` type mismatch"))
250 }
251
252 #[inline]
253 fn args_missing() -> Error {
254 Error::client_error("Path parsing error: missing arguments")
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use hyper::{Request, http::Extensions};
261 use serde::Deserialize;
262 use crate::{HttpBody, HttpRequest, Path};
263 use crate::http::endpoints::route::{PathArg, PathArgs};
264 use crate::http::endpoints::args::{FromPayload, FromRequestParts, FromRequestRef, Payload};
265
266 #[derive(Deserialize)]
267 struct Params {
268 id: u32,
269 name: String
270 }
271
272 #[tokio::test]
273 async fn it_reads_isize_from_payload() {
274 let param = PathArg { name: "id".into(), value: "123".into() };
275 let id = isize::from_payload(Payload::Path(param)).await.unwrap();
276
277 assert_eq!(id, 123);
278 }
279
280 #[tokio::test]
281 async fn it_reads_i8_from_payload() {
282 let param = PathArg { name: "id".into(), value: "123".into() };
283 let id = i8::from_payload(Payload::Path(param)).await.unwrap();
284
285 assert_eq!(id, 123);
286 }
287
288 #[tokio::test]
289 async fn it_reads_i16_from_payload() {
290 let param = PathArg { name: "id".into(), value: "123".into() };
291 let id = i16::from_payload(Payload::Path(param)).await.unwrap();
292
293 assert_eq!(id, 123);
294 }
295
296 #[tokio::test]
297 async fn it_reads_i32_from_payload() {
298 let param = PathArg { name: "id".into(), value: "123".into() };
299 let id = i32::from_payload(Payload::Path(param)).await.unwrap();
300
301 assert_eq!(id, 123);
302 }
303
304 #[tokio::test]
305 async fn it_reads_i64_from_payload() {
306 let param = PathArg { name: "id".into(), value: "123".into() };
307 let id = i64::from_payload(Payload::Path(param)).await.unwrap();
308
309 assert_eq!(id, 123);
310 }
311
312 #[tokio::test]
313 async fn it_reads_i128_from_payload() {
314 let param = PathArg { name: "id".into(), value: "123".into() };
315 let id = i128::from_payload(Payload::Path(param)).await.unwrap();
316
317 assert_eq!(id, 123);
318 }
319
320 #[tokio::test]
321 async fn it_reads_usize_from_payload() {
322 let param = PathArg { name: "id".into(), value: "123".into() };
323 let id = usize::from_payload(Payload::Path(param)).await.unwrap();
324
325 assert_eq!(id, 123);
326 }
327
328 #[tokio::test]
329 async fn it_reads_u8_from_payload() {
330 let param = PathArg { name: "id".into(), value: "123".into() };
331 let id = u8::from_payload(Payload::Path(param)).await.unwrap();
332
333 assert_eq!(id, 123);
334 }
335
336 #[tokio::test]
337 async fn it_reads_u16_from_payload() {
338 let param = PathArg { name: "id".into(), value: "123".into() };
339 let id = u16::from_payload(Payload::Path(param)).await.unwrap();
340
341 assert_eq!(id, 123);
342 }
343
344 #[tokio::test]
345 async fn it_reads_u32_from_payload() {
346 let param = PathArg { name: "id".into(), value: "123".into() };
347 let id = u32::from_payload(Payload::Path(param)).await.unwrap();
348
349 assert_eq!(id, 123);
350 }
351
352 #[tokio::test]
353 async fn it_reads_u128_from_payload() {
354 let param = PathArg { name: "id".into(), value: "123".into() };
355 let id = u128::from_payload(Payload::Path(param)).await.unwrap();
356
357 assert_eq!(id, 123);
358 }
359
360 #[tokio::test]
361 async fn it_reads_string_from_payload() {
362 let param = PathArg { name: "id".into(), value: "123".into() };
363 let id = String::from_payload(Payload::Path(param)).await.unwrap();
364
365 assert_eq!(id, "123");
366 }
367
368 #[tokio::test]
369 async fn it_reads_box_str_from_payload() {
370 let param = PathArg { name: "id".into(), value: "123".into() };
371 let id = Box::<str>::from_payload(Payload::Path(param)).await.unwrap();
372
373 assert_eq!(&*id, "123");
374 }
375
376 #[tokio::test]
377 async fn it_reads_box_bytes_from_payload() {
378 let param = PathArg { name: "id".into(), value: "123".into() };
379 let id = Box::<[u8]>::from_payload(Payload::Path(param)).await.unwrap();
380
381 assert_eq!(&*id, [b'1', b'2', b'3']);
382 }
383
384 #[tokio::test]
385 async fn it_reads_path_from_payload() {
386 let args: PathArgs = smallvec::smallvec![
387 PathArg { name: "id".into(), value: "123".into() },
388 PathArg { name: "name".into(), value: "John".into() }
389 ];
390
391 let path = Path::<Params>::from_payload(Payload::PathArgs(args)).await.unwrap();
392
393 assert_eq!(path.id, 123u32);
394 assert_eq!(path.name, "John")
395 }
396
397 #[test]
398 fn it_parses_slice() {
399 let args: PathArgs = smallvec::smallvec![
400 PathArg { name: "id".into(), value: "123".into() },
401 PathArg { name: "name".into(), value: "John".into() }
402 ];
403
404 let path = Path::<Params>::from_slice(&args).unwrap();
405
406 assert_eq!(path.id, 123u32);
407 assert_eq!(path.name, "John")
408 }
409
410 #[test]
411 fn it_parses_request_extensions() {
412 let args: PathArgs = smallvec::smallvec![
413 PathArg { name: "id".into(), value: "123".into() },
414 PathArg { name: "name".into(), value: "John".into() }
415 ];
416
417 let mut ext = Extensions::new();
418 ext.insert(args);
419
420 let path = Path::<Params>::try_from(&ext).unwrap();
421
422 assert_eq!(path.id, 123u32);
423 assert_eq!(path.name, "John")
424 }
425
426 #[tokio::test]
427 async fn it_reads_path_from_parts() {
428 let args: PathArgs = smallvec::smallvec![
429 PathArg { name: "id".into(), value: "123".into() },
430 PathArg { name: "name".into(), value: "John".into() }
431 ];
432
433 let req = Request::get("/")
434 .extension(args)
435 .body(())
436 .unwrap();
437
438 let (parts, _) = req.into_parts();
439 let path = Path::<Params>::from_parts(&parts).unwrap();
440
441 assert_eq!(path.id, 123u32);
442 assert_eq!(path.name, "John")
443 }
444
445 #[tokio::test]
446 async fn it_reads_path_from_request_ref() {
447 let args: PathArgs = smallvec::smallvec![
448 PathArg { name: "id".into(), value: "123".into() },
449 PathArg { name: "name".into(), value: "John".into() }
450 ];
451
452 let req = Request::get("/")
453 .extension(args)
454 .body(HttpBody::empty())
455 .unwrap();
456
457 let (parts, body) = req.into_parts();
458 let req = HttpRequest::from_parts(parts, body);
459 let path = Path::<Params>::from_request(&req).unwrap();
460
461 assert_eq!(path.id, 123u32);
462 assert_eq!(path.name, "John")
463 }
464}