2019-01-05 22:19:25 +01:00
|
|
|
use std::ops::Index;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
2019-01-16 04:25:49 +01:00
|
|
|
use serde::de;
|
|
|
|
|
|
|
|
use crate::de::PathDeserializer;
|
2019-03-04 21:41:39 +01:00
|
|
|
use crate::{Resource, ResourcePath};
|
2019-01-05 22:19:25 +01:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub(crate) enum PathItem {
|
|
|
|
Static(&'static str),
|
|
|
|
Segment(u16, u16),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resource path match information
|
|
|
|
///
|
|
|
|
/// If resource path contains variable patterns, `Path` stores them.
|
2019-01-06 07:00:38 +01:00
|
|
|
#[derive(Debug)]
|
2019-01-05 22:19:25 +01:00
|
|
|
pub struct Path<T> {
|
|
|
|
path: T,
|
|
|
|
pub(crate) skip: u16,
|
|
|
|
pub(crate) segments: Vec<(Rc<String>, PathItem)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Default> Default for Path<T> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Path {
|
|
|
|
path: T::default(),
|
|
|
|
skip: 0,
|
|
|
|
segments: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 07:00:38 +01:00
|
|
|
impl<T: Clone> Clone for Path<T> {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Path {
|
|
|
|
path: self.path.clone(),
|
|
|
|
skip: self.skip,
|
|
|
|
segments: self.segments.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-04 20:47:03 +01:00
|
|
|
impl<T: ResourcePath> Path<T> {
|
2019-01-05 22:19:25 +01:00
|
|
|
pub fn new(path: T) -> Path<T> {
|
|
|
|
Path {
|
|
|
|
path,
|
|
|
|
skip: 0,
|
|
|
|
segments: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 07:00:38 +01:00
|
|
|
/// Get reference to inner path instance
|
|
|
|
pub fn get_ref(&self) -> &T {
|
|
|
|
&self.path
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get mutable reference to inner path instance
|
|
|
|
pub fn get_mut(&mut self) -> &mut T {
|
|
|
|
&mut self.path
|
|
|
|
}
|
|
|
|
|
2019-01-06 17:12:51 +01:00
|
|
|
/// Path
|
2019-01-05 22:19:25 +01:00
|
|
|
pub fn path(&self) -> &str {
|
|
|
|
let skip = self.skip as usize;
|
|
|
|
let path = self.path.path();
|
|
|
|
if skip <= path.len() {
|
|
|
|
&path[skip..]
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 17:12:51 +01:00
|
|
|
/// Reset inner path
|
|
|
|
pub fn set(&mut self, path: T) {
|
2019-01-05 22:19:25 +01:00
|
|
|
self.skip = 0;
|
|
|
|
self.path = path;
|
|
|
|
self.segments.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Skip first `n` chars in path
|
|
|
|
pub fn skip(&mut self, n: u16) {
|
|
|
|
self.skip = self.skip + n;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn add(&mut self, name: Rc<String>, value: PathItem) {
|
|
|
|
match value {
|
|
|
|
PathItem::Static(s) => self.segments.push((name, PathItem::Static(s))),
|
|
|
|
PathItem::Segment(begin, end) => self
|
|
|
|
.segments
|
|
|
|
.push((name, PathItem::Segment(self.skip + begin, self.skip + end))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 07:00:38 +01:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn add_static(&mut self, name: &str, value: &'static str) {
|
2019-01-05 22:19:25 +01:00
|
|
|
self.segments
|
|
|
|
.push((Rc::new(name.to_string()), PathItem::Static(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if there are any matched patterns
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.segments.is_empty()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check number of extracted parameters
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.segments.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get matched parameter by name without type conversion
|
|
|
|
pub fn get(&self, key: &str) -> Option<&str> {
|
|
|
|
for item in self.segments.iter() {
|
|
|
|
if key == item.0.as_str() {
|
|
|
|
return match item.1 {
|
|
|
|
PathItem::Static(ref s) => Some(&s),
|
|
|
|
PathItem::Segment(s, e) => {
|
|
|
|
Some(&self.path.path()[(s as usize)..(e as usize)])
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if key == "tail" {
|
|
|
|
Some(&self.path.path()[(self.skip as usize)..])
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 17:12:51 +01:00
|
|
|
/// Get unprocessed part of the path
|
2019-01-05 22:19:25 +01:00
|
|
|
pub fn unprocessed(&self) -> &str {
|
|
|
|
&self.path.path()[(self.skip as usize)..]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 04:25:49 +01:00
|
|
|
|
|
|
|
/// Try to deserialize matching parameters to a specified type `U`
|
|
|
|
pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
|
|
|
|
de::Deserialize::deserialize(PathDeserializer::new(self))
|
|
|
|
}
|
2019-01-05 22:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PathIter<'a, T> {
|
|
|
|
idx: usize,
|
|
|
|
params: &'a Path<T>,
|
|
|
|
}
|
|
|
|
|
2019-03-04 20:47:03 +01:00
|
|
|
impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
|
2019-01-05 22:19:25 +01:00
|
|
|
type Item = (&'a str, &'a str);
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<(&'a str, &'a str)> {
|
|
|
|
if self.idx < self.params.len() {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-04 20:47:03 +01:00
|
|
|
impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
|
2019-01-05 22:19:25 +01:00
|
|
|
type Output = str;
|
|
|
|
|
|
|
|
fn index(&self, name: &'a str) -> &str {
|
|
|
|
self.get(name)
|
|
|
|
.expect("Value for parameter is not available")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-04 20:47:03 +01:00
|
|
|
impl<T: ResourcePath> Index<usize> for Path<T> {
|
2019-01-05 22:19:25 +01:00
|
|
|
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)],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-04 21:41:39 +01:00
|
|
|
|
|
|
|
impl<T: ResourcePath> Resource<T> for Path<T> {
|
|
|
|
fn resource_path(&mut self) -> &mut Self {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
2019-03-08 13:43:51 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[cfg(feature = "http")]
|
|
|
|
#[test]
|
|
|
|
fn test_get_param_by_name() {
|
|
|
|
use crate::Url;
|
|
|
|
use http::{HttpTryFrom, Uri};
|
|
|
|
|
|
|
|
let mut params = Path::new(Url::new(Uri::try_from("/").unwrap()));
|
|
|
|
params.add_static("item1", "path");
|
|
|
|
params.add_static("item2", "http%3A%2F%2Flocalhost%3A80%2Ffoo");
|
|
|
|
|
|
|
|
assert_eq!(params.get("item0"), None);
|
|
|
|
assert_eq!(params.get_decoded("item0"), None);
|
|
|
|
assert_eq!(params.get("item1"), Some("path"));
|
|
|
|
assert_eq!(params.get_decoded("item1").unwrap().to_owned(), "path");
|
|
|
|
assert_eq!(
|
|
|
|
params.get("item2"),
|
|
|
|
Some("http%3A%2F%2Flocalhost%3A80%2Ffoo")
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
params.get_decoded("item2").unwrap().to_owned(),
|
|
|
|
"http://localhost:80/foo"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|