satex_core/extension/
url_params.rs1use http::Extensions;
2use matchit::Params;
3use std::ops::Deref;
4use std::sync::Arc;
5
6#[derive(Clone, Debug)]
7pub enum UrlParams {
8 Params(Vec<(Arc<str>, PercentDecodedStr)>),
9 InvalidUtf8InPathParam { key: Arc<str> },
10}
11
12pub fn insert_url_params(extensions: &mut Extensions, params: Params<'_, '_>) {
13 let current_params = extensions.get_mut();
14
15 if let Some(UrlParams::InvalidUtf8InPathParam { .. }) = current_params {
16 return;
18 }
19
20 let params = params
21 .iter()
22 .map(|(k, v)| {
23 if let Some(decoded) = PercentDecodedStr::new(v) {
24 Ok((Arc::from(k), decoded))
25 } else {
26 Err(Arc::from(k))
27 }
28 })
29 .collect::<Result<Vec<_>, _>>();
30
31 match (current_params, params) {
32 (Some(UrlParams::InvalidUtf8InPathParam { .. }), _) => {
33 unreachable!("we check for this state earlier in this method")
34 }
35 (_, Err(invalid_key)) => {
36 extensions.insert(UrlParams::InvalidUtf8InPathParam { key: invalid_key });
37 }
38 (Some(UrlParams::Params(current)), Ok(params)) => {
39 current.extend(params);
40 }
41 (None, Ok(params)) => {
42 extensions.insert(UrlParams::Params(params));
43 }
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, Hash)]
48pub struct PercentDecodedStr(Arc<str>);
49
50impl PercentDecodedStr {
51 pub(crate) fn new<S>(s: S) -> Option<Self>
52 where
53 S: AsRef<str>,
54 {
55 percent_encoding::percent_decode(s.as_ref().as_bytes())
56 .decode_utf8()
57 .ok()
58 .map(|decoded| Self(decoded.as_ref().into()))
59 }
60
61 pub(crate) fn as_str(&self) -> &str {
62 &self.0
63 }
64}
65
66impl Deref for PercentDecodedStr {
67 type Target = str;
68
69 #[inline]
70 fn deref(&self) -> &Self::Target {
71 self.as_str()
72 }
73}