use std::{ borrow::Cow, ops::{DerefMut, Index}, }; use serde::de; use crate::{de::PathDeserializer, Resource, ResourcePath}; #[derive(Debug, Clone)] pub(crate) enum PathItem { Static(Cow<'static, str>), Segment(u16, u16), } impl Default for PathItem { fn default() -> Self { Self::Static(Cow::Borrowed("")) } } /// Resource path match information. /// /// If resource path contains variable patterns, `Path` stores them. #[derive(Debug, Clone, Default)] pub struct Path { path: T, pub(crate) skip: u16, pub(crate) segments: Vec<(Cow<'static, str>, PathItem)>, } impl Path { pub fn new(path: T) -> Path { Path { path, skip: 0, segments: Vec::new(), } } /// Returns reference to inner path instance. #[inline] pub fn get_ref(&self) -> &T { &self.path } /// Returns mutable reference to inner path instance. #[inline] pub fn get_mut(&mut self) -> &mut T { &mut self.path } /// Returns full path as a string. #[inline] pub fn as_str(&self) -> &str { self.path.path() } /// Returns unprocessed part of the path. /// /// Returns empty string if no more is to be processed. #[inline] pub fn unprocessed(&self) -> &str { // clamp skip to path length let skip = (self.skip as usize).min(self.as_str().len()); &self.path.path()[skip..] } /// Returns unprocessed part of the path. #[doc(hidden)] #[deprecated(since = "0.6.0", note = "Use `.as_str()` or `.unprocessed()`.")] #[inline] pub fn path(&self) -> &str { let skip = self.skip as usize; let path = self.path.path(); if skip <= path.len() { &path[skip..] } else { "" } } /// Set new path. #[inline] pub fn set(&mut self, path: T) { self.skip = 0; self.path = path; self.segments.clear(); } /// Reset state. #[inline] pub fn reset(&mut self) { self.skip = 0; self.segments.clear(); } /// Skip first `n` chars in path. #[inline] pub fn skip(&mut self, n: u16) { self.skip += n; } pub(crate) fn add(&mut self, name: impl Into>, value: PathItem) { match value { PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))), PathItem::Segment(begin, end) => self.segments.push(( name.into(), PathItem::Segment(self.skip + begin, self.skip + end), )), } } #[doc(hidden)] pub fn add_static( &mut self, name: impl Into>, value: impl Into>, ) { self.segments .push((name.into(), PathItem::Static(value.into()))); } /// Check if there are any matched patterns. #[inline] pub fn is_empty(&self) -> bool { self.segments.is_empty() } /// Returns number of interpolated segments. #[inline] pub fn segment_count(&self) -> usize { self.segments.len() } /// Get matched parameter by name without type conversion pub fn get(&self, name: &str) -> Option<&str> { for (seg_name, val) in self.segments.iter() { if name == seg_name { return match val { PathItem::Static(ref s) => Some(s), PathItem::Segment(s, e) => { Some(&self.path.path()[(*s as usize)..(*e as usize)]) } }; } } None } /// Get matched parameter by name. /// /// If keyed parameter is not available empty string is used as default value. pub fn query(&self, key: &str) -> &str { if let Some(s) = self.get(key) { s } else { "" } } /// Return iterator to items in parameter container. pub fn iter(&self) -> PathIter<'_, T> { PathIter { idx: 0, params: self, } } /// Try to deserialize matching parameters to a specified type `U` pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result { de::Deserialize::deserialize(PathDeserializer::new(self)) } } #[derive(Debug)] pub struct PathIter<'a, T> { idx: usize, params: &'a Path, } impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> { type Item = (&'a str, &'a str); #[inline] fn next(&mut self) -> Option<(&'a str, &'a str)> { if self.idx < self.params.segment_count() { let idx = self.idx; let res = match self.params.segments[idx].1 { PathItem::Static(ref s) => s, PathItem::Segment(s, e) => &self.params.path.path()[(s as usize)..(e as usize)], }; self.idx += 1; return Some((&self.params.segments[idx].0, res)); } None } } impl<'a, T: ResourcePath> Index<&'a str> for Path { type Output = str; fn index(&self, name: &'a str) -> &str { self.get(name) .expect("Value for parameter is not available") } } impl Index for Path { type Output = str; fn index(&self, idx: usize) -> &str { match self.segments[idx].1 { PathItem::Static(ref s) => s, PathItem::Segment(s, e) => &self.path.path()[(s as usize)..(e as usize)], } } } impl Resource for Path { type Path = T; fn resource_path(&mut self) -> &mut Path { self } } impl Resource for T where T: DerefMut>, P: ResourcePath, { type Path = P; fn resource_path(&mut self) -> &mut Path { &mut *self } } #[cfg(test)] mod tests { use std::cell::RefCell; use super::*; #[allow(clippy::needless_borrow)] #[test] fn deref_impls() { let mut foo = Path::new("/foo"); let _ = (&mut foo).resource_path(); let foo = RefCell::new(foo); let _ = foo.borrow_mut().resource_path(); } }