matchit/
params.rs

1use std::iter;
2use std::mem;
3use std::slice;
4
5/// A single URL parameter, consisting of a key and a value.
6#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Default, Copy, Clone)]
7struct Param<'k, 'v> {
8    key: &'k [u8],
9    value: &'v [u8],
10}
11
12impl<'k, 'v> Param<'k, 'v> {
13    // this could be from_utf8_unchecked, but we'll keep this safe for now
14    fn key_str(&self) -> &'k str {
15        std::str::from_utf8(self.key).unwrap()
16    }
17
18    fn value_str(&self) -> &'v str {
19        std::str::from_utf8(self.value).unwrap()
20    }
21}
22
23/// A list of parameters returned by a route match.
24///
25/// ```rust
26/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
27/// # let mut router = matchit::Router::new();
28/// # router.insert("/users/:id", true).unwrap();
29/// let matched = router.at("/users/1")?;
30///
31/// // you can iterate through the keys and values
32/// for (key, value) in matched.params.iter() {
33///     println!("key: {}, value: {}", key, value);
34/// }
35///
36/// // or get a specific value by key
37/// let id = matched.params.get("id");
38/// assert_eq!(id, Some("1"));
39/// # Ok(())
40/// # }
41/// ```
42#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
43pub struct Params<'k, 'v> {
44    kind: ParamsKind<'k, 'v>,
45}
46
47// most routes have 1-3 dynamic parameters, so we can avoid a heap allocation in common cases.
48const SMALL: usize = 3;
49
50#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
51enum ParamsKind<'k, 'v> {
52    None,
53    Small([Param<'k, 'v>; SMALL], usize),
54    Large(Vec<Param<'k, 'v>>),
55}
56
57impl<'k, 'v> Params<'k, 'v> {
58    pub(crate) fn new() -> Self {
59        let kind = ParamsKind::None;
60        Self { kind }
61    }
62
63    /// Returns the number of parameters.
64    pub fn len(&self) -> usize {
65        match &self.kind {
66            ParamsKind::None => 0,
67            ParamsKind::Small(_, len) => *len,
68            ParamsKind::Large(vec) => vec.len(),
69        }
70    }
71
72    pub(crate) fn truncate(&mut self, n: usize) {
73        match &mut self.kind {
74            ParamsKind::None => {}
75            ParamsKind::Small(_, len) => {
76                *len = n;
77            }
78            ParamsKind::Large(vec) => {
79                vec.truncate(n);
80            }
81        }
82    }
83
84    /// Returns the value of the first parameter registered under the given key.
85    pub fn get(&self, key: impl AsRef<str>) -> Option<&'v str> {
86        let key = key.as_ref().as_bytes();
87
88        match &self.kind {
89            ParamsKind::None => None,
90            ParamsKind::Small(arr, len) => arr
91                .iter()
92                .take(*len)
93                .find(|param| param.key == key)
94                .map(Param::value_str),
95            ParamsKind::Large(vec) => vec
96                .iter()
97                .find(|param| param.key == key)
98                .map(Param::value_str),
99        }
100    }
101
102    /// Returns an iterator over the parameters in the list.
103    pub fn iter(&self) -> ParamsIter<'_, 'k, 'v> {
104        ParamsIter::new(self)
105    }
106
107    /// Returns `true` if there are no parameters in the list.
108    pub fn is_empty(&self) -> bool {
109        match &self.kind {
110            ParamsKind::None => true,
111            ParamsKind::Small(_, len) => *len == 0,
112            ParamsKind::Large(vec) => vec.is_empty(),
113        }
114    }
115
116    /// Inserts a key value parameter pair into the list.
117    pub(crate) fn push(&mut self, key: &'k [u8], value: &'v [u8]) {
118        #[cold]
119        fn drain_to_vec<T: Default>(len: usize, elem: T, arr: &mut [T; SMALL]) -> Vec<T> {
120            let mut vec = Vec::with_capacity(len + 1);
121            vec.extend(arr.iter_mut().map(mem::take));
122            vec.push(elem);
123            vec
124        }
125
126        let param = Param { key, value };
127        match &mut self.kind {
128            ParamsKind::None => {
129                self.kind = ParamsKind::Small([param, Param::default(), Param::default()], 1);
130            }
131            ParamsKind::Small(arr, len) => {
132                if *len == SMALL {
133                    self.kind = ParamsKind::Large(drain_to_vec(*len, param, arr));
134                    return;
135                }
136                arr[*len] = param;
137                *len += 1;
138            }
139            ParamsKind::Large(vec) => vec.push(param),
140        }
141    }
142
143    // Transform each key.
144    pub(crate) fn for_each_key_mut(&mut self, f: impl Fn((usize, &mut &'k [u8]))) {
145        match &mut self.kind {
146            ParamsKind::None => {}
147            ParamsKind::Small(arr, len) => arr
148                .iter_mut()
149                .take(*len)
150                .map(|param| &mut param.key)
151                .enumerate()
152                .for_each(f),
153            ParamsKind::Large(vec) => vec
154                .iter_mut()
155                .map(|param| &mut param.key)
156                .enumerate()
157                .for_each(f),
158        }
159    }
160}
161
162/// An iterator over the keys and values of a route's [parameters](crate::Params).
163pub struct ParamsIter<'ps, 'k, 'v> {
164    kind: ParamsIterKind<'ps, 'k, 'v>,
165}
166
167impl<'ps, 'k, 'v> ParamsIter<'ps, 'k, 'v> {
168    fn new(params: &'ps Params<'k, 'v>) -> Self {
169        let kind = match &params.kind {
170            ParamsKind::None => ParamsIterKind::None,
171            ParamsKind::Small(arr, len) => ParamsIterKind::Small(arr.iter().take(*len)),
172            ParamsKind::Large(vec) => ParamsIterKind::Large(vec.iter()),
173        };
174        Self { kind }
175    }
176}
177
178enum ParamsIterKind<'ps, 'k, 'v> {
179    None,
180    Small(iter::Take<slice::Iter<'ps, Param<'k, 'v>>>),
181    Large(slice::Iter<'ps, Param<'k, 'v>>),
182}
183
184impl<'ps, 'k, 'v> Iterator for ParamsIter<'ps, 'k, 'v> {
185    type Item = (&'k str, &'v str);
186
187    fn next(&mut self) -> Option<Self::Item> {
188        match self.kind {
189            ParamsIterKind::None => None,
190            ParamsIterKind::Small(ref mut iter) => {
191                iter.next().map(|p| (p.key_str(), p.value_str()))
192            }
193            ParamsIterKind::Large(ref mut iter) => {
194                iter.next().map(|p| (p.key_str(), p.value_str()))
195            }
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn no_alloc() {
206        assert_eq!(Params::new().kind, ParamsKind::None);
207    }
208
209    #[test]
210    fn heap_alloc() {
211        let vec = vec![
212            ("hello", "hello"),
213            ("world", "world"),
214            ("foo", "foo"),
215            ("bar", "bar"),
216            ("baz", "baz"),
217        ];
218
219        let mut params = Params::new();
220        for (key, value) in vec.clone() {
221            params.push(key.as_bytes(), value.as_bytes());
222            assert_eq!(params.get(key), Some(value));
223        }
224
225        match params.kind {
226            ParamsKind::Large(..) => {}
227            _ => panic!(),
228        }
229
230        assert!(params.iter().eq(vec.clone()));
231    }
232
233    #[test]
234    fn stack_alloc() {
235        let vec = vec![("hello", "hello"), ("world", "world"), ("baz", "baz")];
236
237        let mut params = Params::new();
238        for (key, value) in vec.clone() {
239            params.push(key.as_bytes(), value.as_bytes());
240            assert_eq!(params.get(key), Some(value));
241        }
242
243        match params.kind {
244            ParamsKind::Small(..) => {}
245            _ => panic!(),
246        }
247
248        assert!(params.iter().eq(vec.clone()));
249    }
250
251    #[test]
252    fn ignore_array_default() {
253        let params = Params::new();
254        assert!(params.get("").is_none());
255    }
256}