pavex/request/path/
raw_path_params.rs

1use std::borrow::Cow;
2
3use matchit::{Params, ParamsIter};
4use percent_encoding::percent_decode_str;
5
6use crate::request::path::errors::DecodeError;
7
8/// Extract (raw) path parameters from the URL of an incoming request.
9///
10/// # Guide
11///
12/// Check out [the guide](https://pavex.dev/docs/guide/request_data/path/raw_path_parameters/)
13/// for more information on how to use this extractor.
14///
15/// # Example
16///
17/// ```rust
18/// use pavex::{get, request::path::RawPathParams};
19///
20/// // Define a route with a few path parameters.
21/// #[get(path = "/address/{address_id}/home/{home_id}")]
22/// pub fn get_home(params: &RawPathParams) -> String {
23///     let home_id = &params.get("home_id").unwrap();
24///     let street_id = &params.get("street_id").unwrap();
25///     format!("The home with id {} is in street {}", home_id, street_id)
26/// }
27/// ```
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub struct RawPathParams<'server, 'request>(Params<'server, 'request>);
30
31impl Default for RawPathParams<'_, '_> {
32    fn default() -> Self {
33        Self(Params::new())
34    }
35}
36
37impl<'server, 'request> RawPathParams<'server, 'request> {
38    /// Returns the number of extracted path parameters.
39    pub fn len(&self) -> usize {
40        self.0.len()
41    }
42
43    /// Returns the value of the first path parameter registered under the given key.
44    pub fn get(&self, key: impl AsRef<str>) -> Option<&'request str> {
45        self.0.get(key)
46    }
47
48    /// Returns an iterator over the parameters in the list.
49    pub fn iter(&self) -> RawPathParamsIter<'_, 'server, 'request> {
50        RawPathParamsIter(self.0.iter())
51    }
52
53    /// Returns `true` if no path parameters have been extracted from the request's path.
54    pub fn is_empty(&self) -> bool {
55        self.0.is_empty()
56    }
57}
58
59impl<'k, 'v> From<Params<'k, 'v>> for RawPathParams<'k, 'v> {
60    fn from(value: Params<'k, 'v>) -> Self {
61        Self(value)
62    }
63}
64
65/// An iterator over the path parameters extracted via [`RawPathParams`].
66pub struct RawPathParamsIter<'extractor, 'server, 'request>(
67    ParamsIter<'extractor, 'server, 'request>,
68);
69
70impl<'server, 'request> Iterator for RawPathParamsIter<'_, 'server, 'request> {
71    type Item = (&'server str, EncodedParamValue<'request>);
72
73    fn next(&mut self) -> Option<Self::Item> {
74        self.0
75            .next()
76            .map(|(key, value)| (key, EncodedParamValue::new(value)))
77    }
78}
79
80/// A wrapper around a percent-encoded path parameter, obtained via [`RawPathParams`].
81///
82/// Use [`decode`](Self::decode) to extract the percent-encoded value.
83#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
84pub struct EncodedParamValue<'request>(&'request str);
85
86impl<'request> EncodedParamValue<'request> {
87    fn new(s: &'request str) -> Self {
88        Self(s)
89    }
90
91    /// Percent-decode a raw path parameter.
92    ///
93    /// If decoding fails, a [`DecodeError`] is returned.
94    pub fn decode(&self) -> Result<Cow<'request, str>, DecodeError> {
95        percent_decode_str(self.0)
96            .decode_utf8()
97            .map_err(|e| DecodeError {
98                invalid_raw_segment: self.0.to_owned(),
99                source: e,
100            })
101    }
102
103    /// Get a reference to the underlying percent-encoded string.
104    pub fn as_str(&self) -> &'request str {
105        self.0
106    }
107}