Skip to main content

xitca_router/
params.rs

1use core::slice;
2
3use crate::{SmallStr, Vec};
4
5/// A single URL parameter, consisting of a key and a value.
6#[derive(Debug, PartialEq, Eq, Clone, Hash)]
7pub struct Param {
8    key: SmallStr,
9    value: SmallStr,
10}
11
12impl Param {
13    fn key_str(&self) -> &str {
14        self.key.as_ref()
15    }
16
17    fn value_str(&self) -> &str {
18        self.value.as_ref()
19    }
20}
21
22/// A list of parameters returned by a route match.
23///
24/// ```rust
25/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
26/// # let mut router = xitca_router::Router::new();
27/// # router.insert("/users/{id}", true)?;
28/// let matched = router.at("/users/1")?;
29///
30/// // you can get a specific value by key
31/// let id = matched.params.get("id");
32/// assert_eq!(id, Some("1"));
33///
34/// // or iterate through the keys and values
35/// for (key, value) in matched.params.iter() {
36///     println!("key: {}, value: {}", key, value);
37/// }
38/// # Ok(())
39/// # }
40/// ```
41#[derive(Clone, Debug)]
42pub struct Params {
43    inner: Vec<Param>,
44}
45
46impl Default for Params {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl Params {
53    /// Returns the number of parameters.
54    #[inline]
55    pub fn len(&self) -> usize {
56        self.inner.len()
57    }
58
59    /// Returns `true` if there are no parameters in the list.
60    #[inline]
61    pub fn is_empty(&self) -> bool {
62        self.len() == 0
63    }
64
65    /// Returns the value of the first parameter registered under the given key.
66    #[inline]
67    pub fn get(&self, key: impl AsRef<str>) -> Option<&str> {
68        self.inner
69            .iter()
70            .find(|param| param.key_str() == key.as_ref())
71            .map(Param::value_str)
72    }
73
74    #[inline]
75    pub fn iter(&self) -> Iter<'_> {
76        Iter {
77            inner: self.inner.iter(),
78        }
79    }
80}
81
82impl Params {
83    pub(super) const fn new() -> Self {
84        Self { inner: Vec::new() }
85    }
86
87    pub(super) fn truncate(&mut self, n: usize) {
88        self.inner.truncate(n)
89    }
90
91    pub(super) fn push_val(&mut self, value: &str) {
92        self.inner.push(Param {
93            key: const { SmallStr::new() },
94            value: value.into(),
95        });
96    }
97
98    pub(super) fn push(&mut self, key: &str, value: &str) {
99        self.inner.push(Param {
100            key: key.into(),
101            value: value.into(),
102        });
103    }
104
105    // Set each param key from the provided slice, zipping with the stored params.
106    // Using zip avoids a bounds check on keys[i] that indexed access would require.
107    pub(crate) fn apply_keys(&mut self, keys: &[SmallStr]) {
108        self.inner
109            .iter_mut()
110            .zip(keys.iter())
111            .for_each(|(param, key)| param.key = key.clone());
112    }
113}
114
115impl IntoIterator for Params {
116    type Item = (SmallStr, SmallStr);
117    type IntoIter = IntoIter<<Vec<Param> as IntoIterator>::IntoIter>;
118
119    #[inline]
120    fn into_iter(self) -> Self::IntoIter {
121        IntoIter {
122            inner: self.inner.into_iter(),
123        }
124    }
125}
126
127pub struct Iter<'a> {
128    inner: slice::Iter<'a, Param>,
129}
130
131impl<'a> Iterator for Iter<'a> {
132    type Item = (&'a str, &'a str);
133
134    #[inline]
135    fn next(&mut self) -> Option<Self::Item> {
136        self.inner.next().map(|p| (p.key.as_ref(), p.value.as_ref()))
137    }
138
139    #[inline]
140    fn size_hint(&self) -> (usize, Option<usize>) {
141        self.inner.size_hint()
142    }
143}
144
145pub struct IntoIter<I> {
146    inner: I,
147}
148
149impl<I> Iterator for IntoIter<I>
150where
151    I: Iterator<Item = Param>,
152{
153    type Item = (SmallStr, SmallStr);
154
155    #[inline]
156    fn next(&mut self) -> Option<Self::Item> {
157        self.inner.next().map(|p| (p.key, p.value))
158    }
159
160    #[inline]
161    fn size_hint(&self) -> (usize, Option<usize>) {
162        self.inner.size_hint()
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn no_alloc() {
172        assert!(Params::new().is_empty());
173    }
174
175    #[test]
176    fn ignore_array_default() {
177        let params = Params::new();
178        assert!(params.get("").is_none());
179    }
180}