pavex/request/path/
path_params.rs

1use pavex_macros::methods;
2use serde::Deserialize;
3
4use crate::request::path::deserializer::PathDeserializer;
5use crate::request::path::errors::{DecodeError, ExtractPathParamsError, InvalidUtf8InPathParam};
6
7use super::RawPathParams;
8
9/// Extract (typed) path parameters from the path of an incoming request.
10///
11/// # Guide
12///
13/// Check out [the guide](https://pavex.dev/docs/guide/request_data/path/path_parameters/)
14/// for more information on how to use this extractor.
15///
16/// # Example
17///
18/// ```rust
19/// use pavex::{get, request::path::PathParams};
20///
21/// // Define a route with a path parameter, `{home_id}`.
22/// // The `PathParams` extractor deserializes the extracted path parameters into
23/// // the type you specified—`Home` in this case.
24/// #[get(path = "/home/{home_id}")]
25/// pub fn get_home(params: &PathParams<Home>) -> String {
26///    format!("The identifier for this home is: {}", params.0.home_id)
27/// }
28///
29/// // The PathParams attribute macro derives the necessary (de)serialization traits.
30/// #[PathParams]
31/// pub struct Home {
32///     // The name of the field must match the name of the path parameter
33///     // used in the route definition.
34///     home_id: u32
35/// }
36/// ```
37///
38/// `home_id` will be set to `1` for an incoming `/home/1` request.
39/// Extraction will fail, instead, if we receive an `/home/abc` request.
40#[doc(alias = "Path")]
41#[doc(alias = "RouteParams")]
42#[doc(alias = "UrlParams")]
43pub struct PathParams<T>(
44    /// The extracted path parameters, deserialized into `T`, the type you specified.
45    pub T,
46);
47
48#[methods]
49impl<T> PathParams<T> {
50    /// The default constructor for [`PathParams`].
51    ///
52    /// If the extraction fails, an [`ExtractPathParamsError`] is returned.
53    #[request_scoped(pavex = crate, id = "PATH_PARAMS_EXTRACT")]
54    pub fn extract<'server, 'request>(
55        params: RawPathParams<'server, 'request>,
56    ) -> Result<Self, ExtractPathParamsError>
57    where
58        T: Deserialize<'request>,
59        // The parameter ids live as long as the server, while the values are tied to the lifecycle
60        // of an incoming request. So it's always true that 'key outlives 'value.
61        'server: 'request,
62    {
63        let mut decoded_params = Vec::with_capacity(params.len());
64        for (id, value) in params.iter() {
65            let decoded_value = value.decode().map_err(|e| {
66                let DecodeError {
67                    invalid_raw_segment,
68                    source,
69                } = e;
70                ExtractPathParamsError::InvalidUtf8InPathParameter(InvalidUtf8InPathParam {
71                    invalid_key: id.into(),
72                    invalid_raw_segment,
73                    source,
74                })
75            })?;
76            decoded_params.push((id, decoded_value));
77        }
78        let deserializer = PathDeserializer::new(&decoded_params);
79        T::deserialize(deserializer)
80            .map_err(ExtractPathParamsError::PathDeserializationError)
81            .map(PathParams)
82    }
83}