Coverage Report

Created: 2026-03-11 07:34

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