/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 ¶ms.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 | | } |