summaryrefslogtreecommitdiff
path: root/vendor/matchit/src/params.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/matchit/src/params.rs')
-rw-r--r--vendor/matchit/src/params.rs262
1 files changed, 262 insertions, 0 deletions
diff --git a/vendor/matchit/src/params.rs b/vendor/matchit/src/params.rs
new file mode 100644
index 00000000..625b544b
--- /dev/null
+++ b/vendor/matchit/src/params.rs
@@ -0,0 +1,262 @@
+use std::{fmt, iter, mem, slice};
+
+/// A single URL parameter, consisting of a key and a value.
+#[derive(PartialEq, Eq, Ord, PartialOrd, Default, Copy, Clone)]
+struct Param<'k, 'v> {
+ // Keys and values are stored as byte slices internally by the router
+ // to avoid UTF8 checks when slicing, but UTF8 is still respected,
+ // so these slices are valid strings.
+ key: &'k [u8],
+ value: &'v [u8],
+}
+
+impl<'k, 'v> Param<'k, 'v> {
+ const EMPTY: Param<'static, 'static> = Param {
+ key: b"",
+ value: b"",
+ };
+
+ // Returns the parameter key as a string.
+ fn key_str(&self) -> &'k str {
+ std::str::from_utf8(self.key).unwrap()
+ }
+
+ // Returns the parameter value as a string.
+ fn value_str(&self) -> &'v str {
+ std::str::from_utf8(self.value).unwrap()
+ }
+}
+
+/// A list of parameters returned by a route match.
+///
+/// ```rust
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// # let mut router = matchit::Router::new();
+/// # router.insert("/users/{id}", true).unwrap();
+/// let matched = router.at("/users/1")?;
+///
+/// // Iterate through the keys and values.
+/// for (key, value) in matched.params.iter() {
+/// println!("key: {}, value: {}", key, value);
+/// }
+///
+/// // Get a specific value by name.
+/// let id = matched.params.get("id");
+/// assert_eq!(id, Some("1"));
+/// # Ok(())
+/// # }
+/// ```
+#[derive(PartialEq, Eq, Ord, PartialOrd, Clone)]
+pub struct Params<'k, 'v> {
+ kind: ParamsKind<'k, 'v>,
+}
+
+// Most routes have a small number of dynamic parameters, so we can avoid
+// heap allocations in the common case.
+const SMALL: usize = 3;
+
+// A list of parameters, optimized to avoid allocations when possible.
+#[derive(PartialEq, Eq, Ord, PartialOrd, Clone)]
+enum ParamsKind<'k, 'v> {
+ Small([Param<'k, 'v>; SMALL], usize),
+ Large(Vec<Param<'k, 'v>>),
+}
+
+impl<'k, 'v> Params<'k, 'v> {
+ pub(crate) fn new() -> Self {
+ Self {
+ kind: ParamsKind::Small([Param::EMPTY; 3], 0),
+ }
+ }
+
+ /// Returns the number of parameters.
+ pub fn len(&self) -> usize {
+ match self.kind {
+ ParamsKind::Small(_, len) => len,
+ ParamsKind::Large(ref vec) => vec.len(),
+ }
+ }
+
+ // Truncates the parameter list to the given length.
+ pub(crate) fn truncate(&mut self, n: usize) {
+ match &mut self.kind {
+ ParamsKind::Small(_, len) => *len = n,
+ ParamsKind::Large(vec) => vec.truncate(n),
+ }
+ }
+
+ /// Returns the value of the first parameter registered under the given key.
+ pub fn get(&self, key: impl AsRef<str>) -> Option<&'v str> {
+ let key = key.as_ref().as_bytes();
+
+ match &self.kind {
+ ParamsKind::Small(arr, len) => arr
+ .iter()
+ .take(*len)
+ .find(|param| param.key == key)
+ .map(Param::value_str),
+ ParamsKind::Large(vec) => vec
+ .iter()
+ .find(|param| param.key == key)
+ .map(Param::value_str),
+ }
+ }
+
+ /// Returns an iterator over the parameters in the list.
+ pub fn iter(&self) -> ParamsIter<'_, 'k, 'v> {
+ ParamsIter::new(self)
+ }
+
+ /// Returns `true` if there are no parameters in the list.
+ pub fn is_empty(&self) -> bool {
+ match self.kind {
+ ParamsKind::Small(_, len) => len == 0,
+ ParamsKind::Large(ref vec) => vec.is_empty(),
+ }
+ }
+
+ /// Inserts a key value parameter pair into the list.
+ pub(crate) fn push(&mut self, key: &'k [u8], value: &'v [u8]) {
+ #[cold]
+ fn drain_to_vec<T: Default>(len: usize, elem: T, arr: &mut [T; SMALL]) -> Vec<T> {
+ let mut vec = Vec::with_capacity(len + 1);
+ vec.extend(arr.iter_mut().map(mem::take));
+ vec.push(elem);
+ vec
+ }
+
+ let param = Param { key, value };
+ match &mut self.kind {
+ ParamsKind::Small(arr, len) => {
+ if *len == SMALL {
+ self.kind = ParamsKind::Large(drain_to_vec(*len, param, arr));
+ return;
+ }
+
+ arr[*len] = param;
+ *len += 1;
+ }
+ ParamsKind::Large(vec) => vec.push(param),
+ }
+ }
+
+ // Applies a transformation function to each key.
+ pub(crate) fn for_each_key_mut(&mut self, f: impl Fn((usize, &mut &'k [u8]))) {
+ match &mut self.kind {
+ ParamsKind::Small(arr, len) => arr
+ .iter_mut()
+ .take(*len)
+ .map(|param| &mut param.key)
+ .enumerate()
+ .for_each(f),
+ ParamsKind::Large(vec) => vec
+ .iter_mut()
+ .map(|param| &mut param.key)
+ .enumerate()
+ .for_each(f),
+ }
+ }
+}
+
+impl fmt::Debug for Params<'_, '_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter()).finish()
+ }
+}
+
+/// An iterator over the keys and values of a route's [parameters](crate::Params).
+pub struct ParamsIter<'ps, 'k, 'v> {
+ kind: ParamsIterKind<'ps, 'k, 'v>,
+}
+
+impl<'ps, 'k, 'v> ParamsIter<'ps, 'k, 'v> {
+ fn new(params: &'ps Params<'k, 'v>) -> Self {
+ let kind = match &params.kind {
+ ParamsKind::Small(arr, len) => ParamsIterKind::Small(arr.iter().take(*len)),
+ ParamsKind::Large(vec) => ParamsIterKind::Large(vec.iter()),
+ };
+ Self { kind }
+ }
+}
+
+enum ParamsIterKind<'ps, 'k, 'v> {
+ Small(iter::Take<slice::Iter<'ps, Param<'k, 'v>>>),
+ Large(slice::Iter<'ps, Param<'k, 'v>>),
+}
+
+impl<'ps, 'k, 'v> Iterator for ParamsIter<'ps, 'k, 'v> {
+ type Item = (&'k str, &'v str);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.kind {
+ ParamsIterKind::Small(ref mut iter) => {
+ iter.next().map(|p| (p.key_str(), p.value_str()))
+ }
+ ParamsIterKind::Large(ref mut iter) => {
+ iter.next().map(|p| (p.key_str(), p.value_str()))
+ }
+ }
+ }
+}
+
+impl ExactSizeIterator for ParamsIter<'_, '_, '_> {
+ fn len(&self) -> usize {
+ match self.kind {
+ ParamsIterKind::Small(ref iter) => iter.len(),
+ ParamsIterKind::Large(ref iter) => iter.len(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn heap_alloc() {
+ let vec = vec![
+ ("hello", "hello"),
+ ("world", "world"),
+ ("foo", "foo"),
+ ("bar", "bar"),
+ ("baz", "baz"),
+ ];
+
+ let mut params = Params::new();
+ for (key, value) in vec.clone() {
+ params.push(key.as_bytes(), value.as_bytes());
+ assert_eq!(params.get(key), Some(value));
+ }
+
+ match params.kind {
+ ParamsKind::Large(..) => {}
+ _ => panic!(),
+ }
+
+ assert!(params.iter().eq(vec.clone()));
+ }
+
+ #[test]
+ fn stack_alloc() {
+ let vec = vec![("hello", "hello"), ("world", "world"), ("baz", "baz")];
+
+ let mut params = Params::new();
+ for (key, value) in vec.clone() {
+ params.push(key.as_bytes(), value.as_bytes());
+ assert_eq!(params.get(key), Some(value));
+ }
+
+ match params.kind {
+ ParamsKind::Small(..) => {}
+ _ => panic!(),
+ }
+
+ assert!(params.iter().eq(vec.clone()));
+ }
+
+ #[test]
+ fn ignore_array_default() {
+ let params = Params::new();
+ assert!(params.get("").is_none());
+ }
+}