add ntex-rouoter crate

This commit is contained in:
Nikolay Kim 2020-03-22 12:36:52 +06:00
parent 68daf75e91
commit e862efc4e6
10 changed files with 2754 additions and 0 deletions

View file

@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ members = [
"ntex", "ntex",
"ntex-router",
"ntex-service", "ntex-service",
"ntex-web-macros", "ntex-web-macros",
@ -11,6 +12,7 @@ members = [
[patch.crates-io] [patch.crates-io]
ntex = { path = "ntex" } ntex = { path = "ntex" }
ntex-router = { path = "ntex-router" }
ntex-service = { path = "ntex-service" } ntex-service = { path = "ntex-service" }
actix-server = { path = "actix-net/actix-server" } actix-server = { path = "actix-net/actix-server" }

57
ntex-router/CHANGES.txt Normal file
View file

@ -0,0 +1,57 @@
# Changes
## [0.3.0] - 2020-03-22
* Use prefix tree for underling data representation
## [0.2.4] - 2019-12-31
* Add `ResourceDef::resource_path_named()` path generation method
## [0.2.3] - 2019-12-25
* Add impl `IntoPattern` for `&String`
## [0.2.2] - 2019-12-25
* Use `IntoPattern` for `RouterBuilder::path()`
## [0.2.1] - 2019-12-25
* Add `IntoPattern` trait
* Add multi-pattern resources
## [0.2.0] - 2019-12-07
* Update http to 0.2
* Update regex to 1.3
* Use bytestring instead of string
## [0.1.5] - 2019-05-15
* Remove debug prints
## [0.1.4] - 2019-05-15
* Fix checked resource match
## [0.1.3] - 2019-04-22
* Added support for `remainder match` (i.e "/path/{tail}*")
## [0.1.2] - 2019-04-07
* Export `Quoter` type
* Allow to reset `Path` instance
## [0.1.1] - 2019-04-03
* Get dynamic segment by name instead of iterator.
## [0.1.0] - 2019-03-09
* Initial release

28
ntex-router/Cargo.toml Normal file
View file

@ -0,0 +1,28 @@
[package]
name = "ntex-router"
version = "0.3.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Path router"
keywords = ["ntex"]
repository = "https://github.com/fafhrd91/ntex.git"
documentation = "https://docs.rs/ntex-router/"
license = "MIT"
edition = "2018"
[lib]
name = "ntex_router"
path = "src/lib.rs"
[features]
default = ["http"]
[dependencies]
regex = "1.3.5"
serde = "1.0.105"
bytestring = "0.1.2"
log = "0.4.8"
http = { version = "0.2.0", optional = true }
[dev-dependencies]
http = "0.2.0"
serde_derive = "1.0"

714
ntex-router/src/de.rs Normal file
View file

@ -0,0 +1,714 @@
use serde::de::{self, Deserializer, Error as DeError, Visitor};
use serde::forward_to_deserialize_any;
use crate::path::{Path, PathIter};
use crate::ResourcePath;
macro_rules! unsupported_type {
($trait_fn:ident, $name:expr) => {
fn $trait_fn<V>(self, _: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>
{
Err(de::value::Error::custom(concat!("unsupported type: ", $name)))
}
};
}
macro_rules! parse_single_value {
($trait_fn:ident, $visit_fn:ident, $tp:tt) => {
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>
{
if self.path.len() != 1 {
Err(de::value::Error::custom(
format!("wrong number of parameters: {} expected 1",
self.path.len()).as_str()))
} else {
let v = self.path[0].parse().map_err(
|_| de::value::Error::custom(
format!("can not parse {:?} to a {}", &self.path[0], $tp)))?;
visitor.$visit_fn(v)
}
}
}
}
pub struct PathDeserializer<'de, T: ResourcePath + 'de> {
path: &'de Path<T>,
}
impl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> {
pub fn new(path: &'de Path<T>) -> Self {
PathDeserializer { path }
}
}
impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> {
type Error = de::value::Error;
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(ParamsDeserializer {
params: self.path.iter(),
current: None,
})
}
fn deserialize_struct<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.len() < len {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected {}",
self.path.len(),
len
)
.as_str(),
))
} else {
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
}
fn deserialize_tuple_struct<V>(
self,
_: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.len() < len {
Err(de::value::Error::custom(
format!(
"wrong number of parameters: {} expected {}",
self.path.len(),
len
)
.as_str(),
))
} else {
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
}
fn deserialize_enum<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.is_empty() {
Err(de::value::Error::custom(
"expeceted at least one parameters",
))
} else {
visitor.visit_enum(ValueEnum {
value: &self.path[0],
})
}
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.path.len() != 1 {
Err(de::value::Error::custom(
format!("wrong number of parameters: {} expected 1", self.path.len())
.as_str(),
))
} else {
visitor.visit_str(&self.path[0])
}
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(ParamsSeq {
params: self.path.iter(),
})
}
unsupported_type!(deserialize_any, "'any'");
unsupported_type!(deserialize_bytes, "bytes");
unsupported_type!(deserialize_option, "Option<T>");
unsupported_type!(deserialize_identifier, "identifier");
unsupported_type!(deserialize_ignored_any, "ignored_any");
parse_single_value!(deserialize_bool, visit_bool, "bool");
parse_single_value!(deserialize_i8, visit_i8, "i8");
parse_single_value!(deserialize_i16, visit_i16, "i16");
parse_single_value!(deserialize_i32, visit_i32, "i32");
parse_single_value!(deserialize_i64, visit_i64, "i64");
parse_single_value!(deserialize_u8, visit_u8, "u8");
parse_single_value!(deserialize_u16, visit_u16, "u16");
parse_single_value!(deserialize_u32, visit_u32, "u32");
parse_single_value!(deserialize_u64, visit_u64, "u64");
parse_single_value!(deserialize_f32, visit_f32, "f32");
parse_single_value!(deserialize_f64, visit_f64, "f64");
parse_single_value!(deserialize_string, visit_string, "String");
parse_single_value!(deserialize_byte_buf, visit_string, "String");
parse_single_value!(deserialize_char, visit_char, "char");
}
struct ParamsDeserializer<'de, T: ResourcePath> {
params: PathIter<'de, T>,
current: Option<(&'de str, &'de str)>,
}
impl<'de, T: ResourcePath> de::MapAccess<'de> for ParamsDeserializer<'de, T> {
type Error = de::value::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: de::DeserializeSeed<'de>,
{
self.current = self.params.next().map(|ref item| (item.0, item.1));
match self.current {
Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)),
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: de::DeserializeSeed<'de>,
{
if let Some((_, value)) = self.current.take() {
seed.deserialize(Value { value })
} else {
Err(de::value::Error::custom("unexpected item"))
}
}
}
struct Key<'de> {
key: &'de str,
}
impl<'de> Deserializer<'de> for Key<'de> {
type Error = de::value::Error;
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_str(self.key)
}
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("Unexpected"))
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum ignored_any
}
}
macro_rules! parse_value {
($trait_fn:ident, $visit_fn:ident, $tp:tt) => {
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>
{
let v = self.value.parse().map_err(
|_| de::value::Error::custom(
format!("can not parse {:?} to a {}", self.value, $tp)))?;
visitor.$visit_fn(v)
}
}
}
struct Value<'de> {
value: &'de str,
}
impl<'de> Deserializer<'de> for Value<'de> {
type Error = de::value::Error;
parse_value!(deserialize_bool, visit_bool, "bool");
parse_value!(deserialize_i8, visit_i8, "i8");
parse_value!(deserialize_i16, visit_i16, "i16");
parse_value!(deserialize_i32, visit_i32, "i16");
parse_value!(deserialize_i64, visit_i64, "i64");
parse_value!(deserialize_u8, visit_u8, "u8");
parse_value!(deserialize_u16, visit_u16, "u16");
parse_value!(deserialize_u32, visit_u32, "u32");
parse_value!(deserialize_u64, visit_u64, "u64");
parse_value!(deserialize_f32, visit_f32, "f32");
parse_value!(deserialize_f64, visit_f64, "f64");
parse_value!(deserialize_string, visit_string, "String");
parse_value!(deserialize_byte_buf, visit_string, "String");
parse_value!(deserialize_char, visit_char, "char");
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_bytes(self.value.as_bytes())
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_str(self.value)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_some(self)
}
fn deserialize_enum<V>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_enum(ValueEnum { value: self.value })
}
fn deserialize_newtype_struct<V>(
self,
_: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: tuple"))
}
fn deserialize_struct<V>(
self,
_: &'static str,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: struct"))
}
fn deserialize_tuple_struct<V>(
self,
_: &'static str,
_: usize,
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("unsupported type: tuple struct"))
}
unsupported_type!(deserialize_any, "any");
unsupported_type!(deserialize_seq, "seq");
unsupported_type!(deserialize_map, "map");
unsupported_type!(deserialize_identifier, "identifier");
}
struct ParamsSeq<'de, T: ResourcePath> {
params: PathIter<'de, T>,
}
impl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> {
type Error = de::value::Error;
fn next_element_seed<U>(&mut self, seed: U) -> Result<Option<U::Value>, Self::Error>
where
U: de::DeserializeSeed<'de>,
{
match self.params.next() {
Some(item) => Ok(Some(seed.deserialize(Value { value: item.1 })?)),
None => Ok(None),
}
}
}
struct ValueEnum<'de> {
value: &'de str,
}
impl<'de> de::EnumAccess<'de> for ValueEnum<'de> {
type Error = de::value::Error;
type Variant = UnitVariant;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: de::DeserializeSeed<'de>,
{
Ok((seed.deserialize(Key { key: self.value })?, UnitVariant))
}
}
struct UnitVariant;
impl<'de> de::VariantAccess<'de> for UnitVariant {
type Error = de::value::Error;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
Err(de::value::Error::custom("not supported"))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("not supported"))
}
fn struct_variant<V>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(de::value::Error::custom("not supported"))
}
}
// #[cfg(test)]
// mod tests {
// use serde::de;
// use serde_derive::Deserialize;
// use super::*;
// use crate::path::Path;
// use crate::router::Router;
// #[derive(Deserialize)]
// struct MyStruct {
// key: String,
// value: String,
// }
// #[derive(Deserialize)]
// struct Id {
// _id: String,
// }
// #[derive(Debug, Deserialize)]
// struct Test1(String, u32);
// #[derive(Debug, Deserialize)]
// struct Test2 {
// key: String,
// value: u32,
// }
// #[derive(Debug, Deserialize, PartialEq)]
// #[serde(rename_all = "lowercase")]
// enum TestEnum {
// Val1,
// Val2,
// }
// #[derive(Debug, Deserialize)]
// struct Test3 {
// val: TestEnum,
// }
// #[test]
// fn test_request_extract() {
// let mut router = Router::<()>::build();
// router.path("/{key}/{value}/", ());
// let router = router.finish();
// let mut path = Path::new("/name/user1/");
// assert!(router.recognize(&mut path).is_some());
// let s: MyStruct = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(s.key, "name");
// assert_eq!(s.value, "user1");
// let s: (String, String) =
// de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(s.0, "name");
// assert_eq!(s.1, "user1");
// let mut router = Router::<()>::build();
// router.path("/{key}/{value}/", ());
// let router = router.finish();
// let mut path = Path::new("/name/32/");
// assert!(router.recognize(&mut path).is_some());
// let s: Test1 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(s.0, "name");
// assert_eq!(s.1, 32);
// let s: Test2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(s.key, "name");
// assert_eq!(s.value, 32);
// let s: (String, u8) =
// de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(s.0, "name");
// assert_eq!(s.1, 32);
// let res: Vec<String> =
// de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(res[0], "name".to_owned());
// assert_eq!(res[1], "32".to_owned());
// }
// #[test]
// fn test_extract_path_single() {
// let mut router = Router::<()>::build();
// router.path("/{value}/", ());
// let router = router.finish();
// let mut path = Path::new("/32/");
// assert!(router.recognize(&mut path).is_some());
// let i: i8 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(i, 32);
// }
// #[test]
// fn test_extract_enum() {
// let mut router = Router::<()>::build();
// router.path("/{val}/", ());
// let router = router.finish();
// let mut path = Path::new("/val1/");
// assert!(router.recognize(&mut path).is_some());
// let i: TestEnum = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(i, TestEnum::Val1);
// let mut router = Router::<()>::build();
// router.path("/{val1}/{val2}/", ());
// let router = router.finish();
// let mut path = Path::new("/val1/val2/");
// assert!(router.recognize(&mut path).is_some());
// let i: (TestEnum, TestEnum) =
// de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(i, (TestEnum::Val1, TestEnum::Val2));
// }
// #[test]
// fn test_extract_enum_value() {
// let mut router = Router::<()>::build();
// router.path("/{val}/", ());
// let router = router.finish();
// let mut path = Path::new("/val1/");
// assert!(router.recognize(&mut path).is_some());
// let i: Test3 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
// assert_eq!(i.val, TestEnum::Val1);
// let mut path = Path::new("/val3/");
// assert!(router.recognize(&mut path).is_some());
// let i: Result<Test3, de::value::Error> =
// de::Deserialize::deserialize(PathDeserializer::new(&path));
// assert!(i.is_err());
// assert!(format!("{:?}", i).contains("unknown variant"));
// }
// #[test]
// fn test_extract_errors() {
// let mut router = Router::<()>::build();
// router.path("/{value}/", ());
// let router = router.finish();
// let mut path = Path::new("/name/");
// assert!(router.recognize(&mut path).is_some());
// let s: Result<Test1, de::value::Error> =
// de::Deserialize::deserialize(PathDeserializer::new(&path));
// assert!(s.is_err());
// assert!(format!("{:?}", s).contains("wrong number of parameters"));
// let s: Result<Test2, de::value::Error> =
// de::Deserialize::deserialize(PathDeserializer::new(&path));
// assert!(s.is_err());
// assert!(format!("{:?}", s).contains("can not parse"));
// let s: Result<(String, String), de::value::Error> =
// de::Deserialize::deserialize(PathDeserializer::new(&path));
// assert!(s.is_err());
// assert!(format!("{:?}", s).contains("wrong number of parameters"));
// let s: Result<u32, de::value::Error> =
// de::Deserialize::deserialize(PathDeserializer::new(&path));
// assert!(s.is_err());
// assert!(format!("{:?}", s).contains("can not parse"));
// }
// #[test]
// fn test_extract_path_decode() {
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{value}/")));
// macro_rules! test_single_value {
// ($value:expr, $expected:expr) => {{
// let req = TestRequest::with_uri($value).finish();
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// assert_eq!(
// *Path::<String>::from_request(&req, &PathConfig::default()).unwrap(),
// $expected
// );
// }};
// }
// test_single_value!("/%25/", "%");
// test_single_value!("/%40%C2%A3%24%25%5E%26%2B%3D/", "@£$%^&+=");
// test_single_value!("/%2B/", "+");
// test_single_value!("/%252B/", "%2B");
// test_single_value!("/%2F/", "/");
// test_single_value!("/%252F/", "%2F");
// test_single_value!(
// "/http%3A%2F%2Flocalhost%3A80%2Ffoo/",
// "http://localhost:80/foo"
// );
// test_single_value!("/%2Fvar%2Flog%2Fsyslog/", "/var/log/syslog");
// test_single_value!(
// "/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%252Fvar%252Flog%252Fsyslog/",
// "http://localhost:80/file/%2Fvar%2Flog%2Fsyslog"
// );
// let req = TestRequest::with_uri("/%25/7/?id=test").finish();
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/")));
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// let s = Path::<Test2>::from_request(&req, &PathConfig::default()).unwrap();
// assert_eq!(s.key, "%");
// assert_eq!(s.value, 7);
// let s = Path::<(String, String)>::from_request(&req, &PathConfig::default()).unwrap();
// assert_eq!(s.0, "%");
// assert_eq!(s.1, "7");
// }
// #[test]
// fn test_extract_path_no_decode() {
// let mut router = Router::<()>::default();
// router.register_resource(Resource::new(ResourceDef::new("/{value}/")));
// let req = TestRequest::with_uri("/%25/").finish();
// let info = router.recognize(&req, &(), 0);
// let req = req.with_route_info(info);
// assert_eq!(
// *Path::<String>::from_request(&req, &&PathConfig::default().disable_decoding())
// .unwrap(),
// "%25"
// );
// }
// }

161
ntex-router/src/lib.rs Normal file
View file

@ -0,0 +1,161 @@
#![allow(clippy::cognitive_complexity)]
//! Resource path matching library.
mod de;
mod path;
mod resource;
mod router;
mod tree;
pub use self::de::PathDeserializer;
pub use self::path::Path;
pub use self::resource::ResourceDef;
pub use self::router::{ResourceInfo, Router, RouterBuilder};
pub trait Resource<T: ResourcePath> {
fn path(&self) -> &str;
fn resource_path(&mut self) -> &mut Path<T>;
}
pub trait ResourcePath {
fn path(&self) -> &str;
fn unquote<'a>(s: &'a str) -> std::borrow::Cow<'a, str> {
s.into()
}
}
impl ResourcePath for String {
fn path(&self) -> &str {
self.as_str()
}
}
impl<'a> ResourcePath for &'a str {
fn path(&self) -> &str {
self
}
}
impl ResourcePath for bytestring::ByteString {
fn path(&self) -> &str {
&*self
}
}
/// Helper trait for type that could be converted to path pattern
pub trait IntoPattern {
/// Signle patter
fn is_single(&self) -> bool;
fn patterns(&self) -> Vec<String>;
}
impl IntoPattern for String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.clone()]
}
}
impl<'a> IntoPattern for &'a String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.as_str().to_string()]
}
}
impl<'a> IntoPattern for &'a str {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![(*self).to_string()]
}
}
impl<T: AsRef<str>> IntoPattern for Vec<T> {
fn is_single(&self) -> bool {
self.len() == 1
}
fn patterns(&self) -> Vec<String> {
self.into_iter().map(|v| v.as_ref().to_string()).collect()
}
}
macro_rules! array_patterns (($tp:ty, $num:tt) => {
impl IntoPattern for [$tp; $num] {
fn is_single(&self) -> bool {
$num == 1
}
fn patterns(&self) -> Vec<String> {
self.iter().map(|v| v.to_string()).collect()
}
}
});
array_patterns!(&str, 1);
array_patterns!(&str, 2);
array_patterns!(&str, 3);
array_patterns!(&str, 4);
array_patterns!(&str, 5);
array_patterns!(&str, 6);
array_patterns!(&str, 7);
array_patterns!(&str, 8);
array_patterns!(&str, 9);
array_patterns!(&str, 10);
array_patterns!(&str, 11);
array_patterns!(&str, 12);
array_patterns!(&str, 13);
array_patterns!(&str, 14);
array_patterns!(&str, 15);
array_patterns!(&str, 16);
array_patterns!(String, 1);
array_patterns!(String, 2);
array_patterns!(String, 3);
array_patterns!(String, 4);
array_patterns!(String, 5);
array_patterns!(String, 6);
array_patterns!(String, 7);
array_patterns!(String, 8);
array_patterns!(String, 9);
array_patterns!(String, 10);
array_patterns!(String, 11);
array_patterns!(String, 12);
array_patterns!(String, 13);
array_patterns!(String, 14);
array_patterns!(String, 15);
array_patterns!(String, 16);
mod quoter;
#[cfg(feature = "http")]
mod http_support {
use super::ResourcePath;
use http::Uri;
impl ResourcePath for Uri {
fn path(&self) -> &str {
self.path()
}
fn unquote<'a>(s: &'a str) -> std::borrow::Cow<'a, str> {
if let Some(q) = super::quoter::requote(s.as_bytes()) {
std::borrow::Cow::Owned(q)
} else {
std::borrow::Cow::Borrowed(s)
}
}
}
}

221
ntex-router/src/path.rs Normal file
View file

@ -0,0 +1,221 @@
use std::ops::Index;
use serde::de;
use crate::de::PathDeserializer;
use crate::{Resource, ResourcePath};
#[derive(Debug, Clone)]
pub(super) enum PathItem {
Static(&'static str),
Segment(String),
}
/// Resource path match information
///
/// If resource path contains variable patterns, `Path` stores them.
#[derive(Debug)]
pub struct Path<T> {
path: T,
pub(super) skip: u16,
pub(super) segments: Vec<(&'static str, PathItem)>,
}
impl<T: Default> Default for Path<T> {
fn default() -> Self {
Path {
path: T::default(),
skip: 0,
segments: Vec::new(),
}
}
}
impl<T: Clone> Clone for Path<T> {
fn clone(&self) -> Self {
Path {
path: self.path.clone(),
skip: self.skip,
segments: self.segments.clone(),
}
}
}
impl<T: ResourcePath> Path<T> {
pub fn new(path: T) -> Path<T> {
Path {
path,
skip: 0,
segments: Vec::new(),
}
}
#[inline]
/// Get reference to inner path instance
pub fn get_ref(&self) -> &T {
&self.path
}
#[inline]
/// Get mutable reference to inner path instance
pub fn get_mut(&mut self) -> &mut T {
&mut self.path
}
#[inline]
/// Path
pub fn path(&self) -> &str {
let skip = self.skip as usize;
let path = self.path.path();
if skip <= path.len() {
&path[skip..]
} else {
""
}
}
#[inline]
/// Set new path
pub fn set(&mut self, path: T) {
self.skip = 0;
self.path = path;
self.segments.clear();
}
#[inline]
/// Reset state
pub fn reset(&mut self) {
self.skip = 0;
self.segments.clear();
}
#[inline]
/// Skip first `n` chars in path
pub fn skip(&mut self, n: u16) {
self.skip += n;
}
pub(crate) fn add(&mut self, name: &'static str, value: String) {
self.segments.push((name, PathItem::Segment(value)))
}
#[doc(hidden)]
pub fn add_static(&mut self, name: &'static str, value: &'static str) {
self.segments.push((name, PathItem::Static(value)));
}
#[inline]
/// Check if there are any matched patterns
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
#[inline]
/// 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 {
return match item.1 {
PathItem::Static(ref s) => Some(&s),
PathItem::Segment(ref s) => Some(s),
};
}
}
if key == "tail" {
Some(&self.path.path()[(self.skip as usize)..])
} else {
None
}
}
/// Get unprocessed part of the path
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,
}
}
/// 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))
}
}
#[derive(Debug)]
pub struct PathIter<'a, T> {
idx: usize,
params: &'a Path<T>,
}
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.len() {
let idx = self.idx;
let res = match self.params.segments[idx].1 {
PathItem::Static(s) => s,
PathItem::Segment(ref s) => s.as_str(),
};
self.idx += 1;
return Some((&self.params.segments[idx].0, res));
}
None
}
}
impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
type Output = str;
fn index(&self, name: &'a str) -> &str {
self.get(name)
.expect("Value for parameter is not available")
}
}
impl<T: ResourcePath> Index<usize> for Path<T> {
type Output = str;
fn index(&self, idx: usize) -> &str {
match self.segments[idx].1 {
PathItem::Static(ref s) => &s,
PathItem::Segment(ref s) => &s,
}
}
}
impl<T: ResourcePath> Resource<T> for Path<T> {
fn path(&self) -> &str {
self.path()
}
fn resource_path(&mut self) -> &mut Path<T> {
self
}
}

62
ntex-router/src/quoter.rs Normal file
View file

@ -0,0 +1,62 @@
pub fn requote(val: &[u8]) -> Option<String> {
let mut has_pct = 0;
let mut pct = [b'%', 0, 0];
let mut idx = 0;
let mut cloned: Option<Vec<u8>> = None;
let len = val.len();
while idx < len {
let ch = val[idx];
if has_pct != 0 {
pct[has_pct] = val[idx];
has_pct += 1;
if has_pct == 3 {
has_pct = 0;
let buf = cloned.as_mut().unwrap();
if let Some(ch) = restore_ch(pct[1], pct[2]) {
buf.push(ch);
} else {
buf.extend_from_slice(&pct[..]);
}
}
} else if ch == b'%' {
has_pct = 1;
if cloned.is_none() {
let mut c = Vec::with_capacity(len);
c.extend_from_slice(&val[..idx]);
cloned = Some(c);
}
} else if let Some(ref mut cloned) = cloned {
cloned.push(ch)
}
idx += 1;
}
if let Some(data) = cloned {
// Unsafe: we get data from http::Uri, which does utf-8 checks already
// this code only decodes valid pct encoded values
Some(unsafe { String::from_utf8_unchecked(data) })
} else {
None
}
}
#[inline]
fn from_hex(v: u8) -> Option<u8> {
if v >= b'0' && v <= b'9' {
Some(v - 0x30) // ord('0') == 0x30
} else if v >= b'A' && v <= b'F' {
Some(v - 0x41 + 10) // ord('A') == 0x41
} else if v > b'a' && v <= b'f' {
Some(v - 0x61 + 10) // ord('a') == 0x61
} else {
None
}
}
#[inline]
fn restore_ch(d1: u8, d2: u8) -> Option<u8> {
from_hex(d1).and_then(|d1| from_hex(d2).map(move |d2| d1 << 4 | d2))
}

824
ntex-router/src/resource.rs Normal file
View file

@ -0,0 +1,824 @@
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use regex::{escape, Regex};
use super::IntoPattern;
#[derive(Clone, Debug)]
pub(super) struct Segments {
pub(super) tp: Vec<Segment>,
pub(super) slesh: bool,
}
/// ResourceDef describes an entry in resources table
///
/// Resource definition can contain only 16 dynamic segments
#[derive(Clone, Debug)]
pub struct ResourceDef {
id: u16,
pub(super) tp: Vec<Segments>, // set of matching paths
name: String,
pattern: String,
elements: Vec<PathElement>,
pub(super) prefix: bool,
}
#[derive(Debug, Clone, PartialEq)]
enum PathElement {
Str(String),
Var(String),
}
impl PathElement {
fn is_str(&self) -> bool {
match self {
PathElement::Str(_) => true,
_ => false,
}
}
fn into_str(self) -> String {
match self {
PathElement::Str(s) => s,
_ => panic!(),
}
}
fn as_str(&self) -> &str {
match self {
PathElement::Str(s) => s.as_str(),
PathElement::Var(s) => s.as_str(),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum Segment {
Static(String),
Dynamic {
pattern: Regex,
names: Vec<&'static str>,
len: usize,
tail: bool,
},
}
impl Eq for Segment {}
impl PartialEq for Segment {
fn eq(&self, other: &Self) -> bool {
match self {
Segment::Static(ref p1) => match other {
Segment::Static(p2) => p1 == p2,
_ => false,
},
Segment::Dynamic {
pattern: ref p1,
tail: t1,
..
} => match other {
Segment::Static { .. } => false,
Segment::Dynamic {
pattern: ref p2,
tail: t2,
..
} => p1.as_str() == p2.as_str() && t1 == t2,
},
}
}
}
impl ResourceDef {
/// Parse path pattern and create new `ResourceDef` instance.
///
/// Path segments are separatted by `/`. Pattern must start
/// with segment separator. Static segments could be
/// case insensitive.
///
/// Panics if path pattern is malformed.
pub fn new<T: IntoPattern>(path: T) -> Self {
if path.is_single() {
let patterns = path.patterns();
ResourceDef::with_prefix(&patterns[0], false)
} else {
let set = path.patterns();
let mut tp = Vec::new();
let mut elements = Vec::new();
for path in set {
let (pelems, elems) = ResourceDef::parse(&path);
tp.push(pelems);
elements = elems;
}
ResourceDef {
tp,
elements,
id: 0,
name: String::new(),
pattern: "".to_owned(),
prefix: false,
}
}
}
/// Parse path pattern and create new `ResourceDef` instance.
///
/// Use `prefix` type instead of `static`.
///
/// Panics if path regex pattern is malformed.
pub fn prefix(path: &str) -> Self {
ResourceDef::with_prefix(path, true)
}
/// Parse path pattern and create new `ResourceDef` instance.
/// Inserts `/` to the start of the pattern.
///
/// Panics if path regex pattern is malformed.
pub fn root_prefix(path: &str) -> Self {
ResourceDef::with_prefix(&insert_slash(path), true)
}
/// Resource id
pub fn id(&self) -> u16 {
self.id
}
/// Set resource id
pub fn set_id(&mut self, id: u16) {
self.id = id;
}
/// Parse path pattern and create new `Pattern` instance with custom prefix
fn with_prefix(path: &str, for_prefix: bool) -> Self {
let path = path.to_owned();
let (tp, elements) = ResourceDef::parse(&path);
ResourceDef {
elements,
id: 0,
tp: vec![tp],
name: String::new(),
pattern: path,
prefix: for_prefix,
}
}
/// Resource pattern name
pub fn name(&self) -> &str {
&self.name
}
/// Mutable reference to a name of a resource definition.
pub fn name_mut(&mut self) -> &mut String {
&mut self.name
}
/// Path pattern of the resource
pub fn pattern(&self) -> &str {
&self.pattern
}
/// Build resource path from elements. Returns `true` on success.
pub fn resource_path<U, I>(&self, path: &mut String, elements: &mut U) -> bool
where
U: Iterator<Item = I>,
I: AsRef<str>,
{
for el in &self.elements {
match *el {
PathElement::Str(ref s) => path.push_str(s),
PathElement::Var(_) => {
if let Some(val) = elements.next() {
path.push_str(val.as_ref())
} else {
return false;
}
}
}
}
true
}
/// Build resource path from elements. Returns `true` on success.
pub fn resource_path_named<K, V, S>(
&self,
path: &mut String,
elements: &HashMap<K, V, S>,
) -> bool
where
K: std::borrow::Borrow<str> + Eq + Hash,
V: AsRef<str>,
S: std::hash::BuildHasher,
{
for el in &self.elements {
match *el {
PathElement::Str(ref s) => path.push_str(s),
PathElement::Var(ref name) => {
if let Some(val) = elements.get(name) {
path.push_str(val.as_ref())
} else {
return false;
}
}
}
}
true
}
fn parse_segment<'a>(
pattern: &'a str,
elems: &mut Vec<PathElement>,
) -> (String, &'a str, bool) {
const DEFAULT_PATTERN: &str = ".+";
const DEFAULT_PATTERN_TAIL: &str = ".*";
let mut re = "^".to_string();
let mut end = None;
let mut tail = false;
let mut rem = pattern;
let mut pattern = &pattern[1..];
elems.push(PathElement::Str('/'.to_string()));
while let Some(start_idx) = pattern.find('{') {
if let Some(end) = end {
if start_idx > end {
break;
}
}
let p = pattern.split_at(start_idx);
pattern = p.1;
re.push_str(&escape(&p.0));
elems.push(PathElement::Str(p.0.to_string()));
// find closing }
let mut params_nesting = 0usize;
let close_idx = pattern
.find(|c| match c {
'{' => {
params_nesting += 1;
false
}
'}' => {
params_nesting -= 1;
params_nesting == 0
}
_ => false,
})
.expect("malformed dynamic segment");
let p = pattern.split_at(close_idx + 1);
rem = p.1;
let param = &p.0[1..p.0.len() - 1]; // Remove outer brackets
tail = rem == "*"; // tail match (should match regardless of segments)
let (name, pat) = match param.find(':') {
Some(idx) => {
if tail {
panic!("Custom regex is not supported for remainder match");
}
let (name, pattern) = param.split_at(idx);
(name, &pattern[1..])
}
None => (
param,
if tail {
rem = &rem[1..];
DEFAULT_PATTERN_TAIL
} else {
DEFAULT_PATTERN
},
),
};
re.push_str(&format!(r"(?P<{}>{})", &escape(name), pat));
elems.push(PathElement::Var(name.to_string()));
if let Some(idx) = rem.find(|c| c == '{' || c == '/') {
end = Some(idx);
pattern = rem;
continue;
} else {
re += rem;
rem = "";
break;
}
}
// find end of segment
if let Some(idx) = rem.find('/') {
re.push_str(&escape(&rem[..idx]));
rem = &rem[idx..];
} else {
re.push_str(&escape(rem));
rem = "";
};
re.push('$');
(re, rem, tail)
}
fn parse(mut pattern: &str) -> (Segments, Vec<PathElement>) {
let mut elems = Vec::new();
let mut pelems = Vec::new();
while let Some(idx) = pattern[1..].find(|c| c == '{' || c == '/') {
let idx = idx + 1;
// static segment
if let Some(i) = pattern[1..idx + 1].find('/') {
elems.push(PathElement::Str(pattern[..i + 1].to_string()));
pelems.push(Segment::Static(pattern[1..i + 1].to_string()));
pattern = &pattern[i + 1..];
continue;
}
// dynamic segment
let (re_part, rem, tail) = Self::parse_segment(pattern, &mut elems);
let re = Regex::new(&re_part).unwrap();
let names: Vec<_> = re
.capture_names()
.filter_map(|name| {
name.map(|name| Box::leak(Box::new(name.to_owned())).as_str())
})
.collect();
pelems.push(Segment::Dynamic {
names,
tail,
pattern: re,
len: 0,
});
pattern = rem;
if pattern.is_empty() {
break;
}
}
// tail
let slesh = pattern.ends_with('/');
if slesh {
pattern = &pattern[..pattern.len() - 1];
}
elems.push(PathElement::Str(pattern.to_string()));
if pattern.starts_with('/') {
pattern = &pattern[1..];
}
if !pattern.is_empty() {
// handle tail expression for static segment
if pattern.ends_with('*') {
let pattern =
Regex::new(&format!("^{}(.+)", &pattern[..pattern.len() - 1]))
.unwrap();
pelems.push(Segment::Dynamic {
pattern,
names: Vec::new(),
tail: true,
len: 0,
});
} else {
pelems.push(Segment::Static(pattern.to_string()));
}
}
// insert last slesh
if slesh {
elems.push(PathElement::Str("/".to_string()))
}
// merge path elements
let mut idx = 0;
while idx + 1 < elems.len() {
if elems[idx + 1].is_str() && elems[idx + 1].as_str().is_empty() {
elems.remove(idx + 1);
continue;
}
if elems[idx].is_str() && elems[idx + 1].is_str() {
let s2 = elems.remove(idx + 1).into_str();
if let PathElement::Str(ref mut s1) = elems[idx] {
s1.push_str(&s2);
continue;
}
}
idx += 1;
}
//println!("P: {:#?}", pelems);
//println!("T: {:#?}", elems);
(Segments { tp: pelems, slesh }, elems)
}
}
impl Eq for ResourceDef {}
impl PartialEq for ResourceDef {
fn eq(&self, other: &ResourceDef) -> bool {
self.pattern == other.pattern
}
}
impl Hash for ResourceDef {
fn hash<H: Hasher>(&self, state: &mut H) {
self.pattern.hash(state);
}
}
impl<'a> From<&'a str> for ResourceDef {
fn from(path: &'a str) -> ResourceDef {
ResourceDef::new(path)
}
}
impl From<String> for ResourceDef {
fn from(path: String) -> ResourceDef {
ResourceDef::new(path)
}
}
pub(crate) fn insert_slash(path: &str) -> String {
let mut path = path.to_owned();
if !path.is_empty() && !path.starts_with('/') {
path.insert(0, '/');
};
path
}
#[cfg(test)]
mod tests {
use super::*;
use crate::path::Path;
use crate::tree::Tree;
#[test]
fn test_parse_static() {
let re = ResourceDef::new("/");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/a")), None);
let re = ResourceDef::new("/name");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
assert_eq!(tree.find(&mut Path::new("/")), None);
assert_eq!(tree.find(&mut Path::new("/name1")), None);
assert_eq!(tree.find(&mut Path::new("/name/")), None);
assert_eq!(tree.find(&mut Path::new("/name~")), None);
let re = ResourceDef::new("/name/");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name")), None);
assert_eq!(tree.find(&mut Path::new("/name/gs")), None);
let re = ResourceDef::new("/user/profile");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/profile/profile")), None);
let mut tree = Tree::new(&ResourceDef::new("/name"), 1);
tree.insert(&ResourceDef::new("/name/"), 2);
assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/")), Some(2));
}
#[test]
fn test_parse_param() {
let re = ResourceDef::new("/{id}");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/2345/")), None);
assert_eq!(tree.find(&mut Path::new("/2345/sdg")), None);
let re = ResourceDef::new("/user/{id}");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
let mut resource = Path::new("/user/profile");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "profile");
let mut resource = Path::new("/user/1245125");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "1245125");
let re = ResourceDef::new("/v{version}/resource/{id}");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/v1/resource/320120")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v1/resource/320120/")), None,);
assert_eq!(tree.find(&mut Path::new("/v/resource/1")), None);
assert_eq!(tree.find(&mut Path::new("/resource")), None);
let mut resource = Path::new("/v151/resource/adahg32");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("version").unwrap(), "151");
assert_eq!(resource.get("id").unwrap(), "adahg32");
let re = ResourceDef::new("/{id:[[:digit:]]{6}}");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/012345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/012345/")), None);
assert_eq!(tree.find(&mut Path::new("/012345/index")), None);
assert_eq!(tree.find(&mut Path::new("/012")), None);
assert_eq!(tree.find(&mut Path::new("/01234567")), None);
assert_eq!(tree.find(&mut Path::new("/XXXXXX")), None);
let mut resource = Path::new("/012345");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "012345");
let re =
ResourceDef::new("/u/test/v{version}-no-{minor}xx/resource/{id}/{name}");
let tree = Tree::new(&re, 1);
let mut resource = Path::new("/u/test/v1-no-3xx/resource/320120/name");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("version").unwrap(), "1");
assert_eq!(resource.get("minor").unwrap(), "3");
assert_eq!(resource.get("id").unwrap(), "320120");
assert_eq!(resource.get("name").unwrap(), "name");
}
#[test]
fn test_dynamic_set() {
let re = ResourceDef::new(vec![
"/user/{id}",
"/v{version}/resource/{id}",
"/{id:[[:digit:]]{6}}",
]);
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
let mut resource = Path::new("/user/profile");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "profile");
let mut resource = Path::new("/user/1245125");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "1245125");
assert_eq!(tree.find(&mut Path::new("/v1/resource/320120")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/resource/1")), None);
assert_eq!(tree.find(&mut Path::new("/resource")), None);
let mut resource = Path::new("/v151/resource/adahg32");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("version").unwrap(), "151");
assert_eq!(resource.get("id").unwrap(), "adahg32");
assert_eq!(tree.find(&mut Path::new("/012345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/012")), None);
assert_eq!(tree.find(&mut Path::new("/01234567")), None);
assert_eq!(tree.find(&mut Path::new("/XXXXXX")), None);
let mut resource = Path::new("/012345");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "012345");
let re = ResourceDef::new([
"/user/{id}",
"/v{version}/resource/{id}",
"/{id:[[:digit:]]{6}}",
]);
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
let re = ResourceDef::new([
"/user/{id}".to_string(),
"/v{version}/resource/{id}".to_string(),
"/{id:[[:digit:]]{6}}".to_string(),
]);
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), None);
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), None);
}
#[cfg(feature = "http")]
#[test]
fn test_parse_urlencoded() {
use http::Uri;
use std::convert::TryFrom;
let tree = Tree::new(&ResourceDef::new("/user/{id}/test"), 1);
let uri = Uri::try_from("/user/2345/test").unwrap();
let mut resource = Path::new(uri);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "2345");
let uri = Uri::try_from("/user/qwe%25/test").unwrap();
let mut resource = Path::new(uri);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "qwe%");
let uri = Uri::try_from("/user/qwe%25rty/test").unwrap();
let mut resource = Path::new(uri);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "qwe%rty");
let uri = Uri::try_from("/user/foo-%2f-%252f-bar/test").unwrap();
let mut resource = Path::new(uri);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "foo-/-%2f-bar");
let uri = Uri::try_from(
"/user/http%3A%2F%2Flocalhost%3A80%2Ffile%2F%2Fvar%2Flog%2Fsyslog/test",
)
.unwrap();
let mut resource = Path::new(uri);
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(
resource.get("id").unwrap(),
"http://localhost:80/file//var/log/syslog"
);
}
#[test]
fn test_parse_tail() {
let re = ResourceDef::new("/user/-{id}*");
let tree = Tree::new(&re, 1);
let mut resource = Path::new("/user/-profile");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "profile");
let mut resource = Path::new("/user/-2345");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "2345");
let mut resource = Path::new("/user/-2345/");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "2345/");
let mut resource = Path::new("/user/-2345/sdg");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.get("id").unwrap(), "2345/sdg");
}
#[test]
fn test_static_tail() {
let re = ResourceDef::new("/*");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/")), None);
assert_eq!(tree.find(&mut Path::new("/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/2345/sdg")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
let re = ResourceDef::new("/user*");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
let re = ResourceDef::new("/v/user*");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/v/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345/sdg")), Some(1));
let re = ResourceDef::new("/user/*");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/2345/sdg")), Some(1));
assert_eq!(tree.find(&mut Path::new("/user/")), None);
assert_eq!(tree.find(&mut Path::new("/user")), None);
let re = ResourceDef::new("/v/user/*");
let tree = Tree::new(&re, 1);
assert_eq!(tree.find(&mut Path::new("/v/user/profile")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/2345/sdg")), Some(1));
assert_eq!(tree.find(&mut Path::new("/v/user/")), None);
assert_eq!(tree.find(&mut Path::new("/v/user")), None);
}
#[test]
fn test_resource_prefix() {
let tree = Tree::new(&ResourceDef::prefix("/name"), 1);
assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name1")), None);
assert_eq!(tree.find(&mut Path::new("/name~")), None);
let mut resource = Path::new("/name/subpath1/subpath2/index.html");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
let tree = Tree::new(&ResourceDef::prefix("/name/"), 1);
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name")), None);
assert_eq!(tree.find(&mut Path::new("/name1")), None);
let tree = Tree::new(&ResourceDef::root_prefix("name/"), 1);
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name")), None);
assert_eq!(tree.find(&mut Path::new("/name1")), None);
let mut resource = Path::new("/name/subpath1/subpath2/index.html");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
}
#[test]
fn test_reousrce_prefix_dynamic() {
let tree = Tree::new(&ResourceDef::prefix("/{name}/"), 1);
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name")), None);
assert_eq!(tree.find(&mut Path::new("/name1")), None);
assert_eq!(tree.find(&mut Path::new("/name~")), None);
let mut resource = Path::new("/test2/");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(&resource["name"], "test2");
assert_eq!(&resource[0], "test2");
let mut resource = Path::new("/test2/subpath1/subpath2/index.html");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(&resource["name"], "test2");
assert_eq!(&resource[0], "test2");
assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
let tree = Tree::new(&ResourceDef::prefix("/{name}"), 1);
assert_eq!(tree.find(&mut Path::new("/name")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name/test/test")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name1")), Some(1));
assert_eq!(tree.find(&mut Path::new("/name~")), Some(1));
let mut resource = Path::new("/test2/subpath1/subpath2/index.html");
assert_eq!(tree.find(&mut resource), Some(1));
assert_eq!(&resource["name"], "test2");
assert_eq!(&resource[0], "test2");
assert_eq!(resource.path(), "/subpath1/subpath2/index.html");
}
#[test]
fn test_resource_path() {
let mut s = String::new();
let resource = ResourceDef::new("/user/{item1}/test");
assert!(resource.resource_path(&mut s, &mut (&["user1"]).iter()));
assert_eq!(s, "/user/user1/test");
let mut s = String::new();
let resource = ResourceDef::new("/user/{item1}/{item2}/test");
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).iter()));
assert_eq!(s, "/user/item/item2/test");
let mut s = String::new();
let resource = ResourceDef::new("/user/{item1}/{item2}");
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).iter()));
assert_eq!(s, "/user/item/item2");
let mut s = String::new();
let resource = ResourceDef::new("/user/{item1}/{item2}/");
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).iter()));
assert_eq!(s, "/user/item/item2/");
let mut s = String::new();
assert!(!resource.resource_path(&mut s, &mut (&["item"]).iter()));
let mut s = String::new();
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).iter()));
assert_eq!(s, "/user/item/item2/");
assert!(!resource.resource_path(&mut s, &mut (&["item"]).iter()));
let mut s = String::new();
assert!(resource.resource_path(&mut s, &mut vec!["item", "item2"].into_iter()));
assert_eq!(s, "/user/item/item2/");
let mut map = HashMap::new();
map.insert("item1", "item");
let mut s = String::new();
assert!(!resource.resource_path_named(&mut s, &map));
let mut s = String::new();
map.insert("item2", "item2");
assert!(resource.resource_path_named(&mut s, &map));
assert_eq!(s, "/user/item/item2/");
}
}

288
ntex-router/src/router.rs Normal file
View file

@ -0,0 +1,288 @@
use super::tree::Tree;
use super::{IntoPattern, Resource, ResourceDef, ResourcePath};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ResourceId(pub u16);
/// Information about current resource
#[derive(Clone, Debug)]
pub struct ResourceInfo {
resource: ResourceId,
}
/// Resource router.
pub struct Router<T, U = ()> {
tree: Tree,
resources: Vec<(ResourceDef, T, Option<U>)>,
}
impl<T, U> Router<T, U> {
pub fn build() -> RouterBuilder<T, U> {
RouterBuilder {
resources: Vec::new(),
}
}
pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
where
R: Resource<P>,
P: ResourcePath,
{
if let Some(idx) = self.tree.find(resource) {
let item = &self.resources[idx];
Some((&item.1, ResourceId(item.0.id())))
} else {
None
}
}
pub fn recognize_mut<R, P>(
&mut self,
resource: &mut R,
) -> Option<(&mut T, ResourceId)>
where
R: Resource<P>,
P: ResourcePath,
{
if let Some(idx) = self.tree.find(resource) {
let item = &mut self.resources[idx];
Some((&mut item.1, ResourceId(item.0.id())))
} else {
None
}
}
pub fn recognize_mut_checked<R, P, F>(
&mut self,
resource: &mut R,
check: F,
) -> Option<(&mut T, ResourceId)>
where
F: Fn(&R, Option<&U>) -> bool,
R: Resource<P>,
P: ResourcePath,
{
if let Some(idx) = self.tree.find_checked(resource, &|idx, res| {
let item = &self.resources[idx];
check(res, item.2.as_ref())
}) {
let item = &mut self.resources[idx];
Some((&mut item.1, ResourceId(item.0.id())))
} else {
None
}
}
}
pub struct RouterBuilder<T, U = ()> {
resources: Vec<(ResourceDef, T, Option<U>)>,
}
impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path.
pub fn path<P: IntoPattern>(
&mut self,
path: P,
resource: T,
) -> &mut (ResourceDef, T, Option<U>) {
self.resources
.push((ResourceDef::new(path), resource, None));
self.resources.last_mut().unwrap()
}
/// Register resource for specified path prefix.
pub fn prefix(
&mut self,
prefix: &str,
resource: T,
) -> &mut (ResourceDef, T, Option<U>) {
self.resources
.push((ResourceDef::prefix(prefix), resource, None));
self.resources.last_mut().unwrap()
}
/// Register resource for ResourceDef
pub fn rdef(
&mut self,
rdef: ResourceDef,
resource: T,
) -> &mut (ResourceDef, T, Option<U>) {
self.resources.push((rdef, resource, None));
self.resources.last_mut().unwrap()
}
/// Finish configuration and create router instance.
pub fn finish(self) -> Router<T, U> {
let tree = if self.resources.is_empty() {
Tree::default()
} else {
let mut tree = Tree::new(&self.resources[0].0, 0);
for (idx, r) in self.resources[1..].iter().enumerate() {
tree.insert(&r.0, idx + 1)
}
tree
};
Router {
tree,
resources: self.resources,
}
}
}
#[cfg(test)]
mod tests {
use crate::path::Path;
use crate::router::{ResourceId, Router};
#[test]
fn test_recognizer_1() {
let mut router = Router::<usize>::build();
router.path("/name", 10).0.set_id(0);
router.path("/name/{val}", 11).0.set_id(1);
router.path("/name/{val}/index.html", 12).0.set_id(2);
router.path("/file/{file}.{ext}", 13).0.set_id(3);
router.path("/v{val}/{val2}/index.html", 14).0.set_id(4);
router.path("/v/{tail}*", 15).0.set_id(5);
router.path("/test2/{test}.html", 16).0.set_id(6);
router.path("/{test}/index.html", 17).0.set_id(7);
router.path("/v2/{custom:.*}/test.html", 18).0.set_id(8);
let mut router = router.finish();
let mut path = Path::new("/unknown");
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/unknown");
assert!(router.recognize(&mut path).is_none());
let mut path = Path::new("/name");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
assert_eq!(info, ResourceId(0));
assert!(path.is_empty());
let mut path = Path::new("/name");
let (h, info) = router.recognize(&mut path).unwrap();
assert_eq!(*h, 10);
assert_eq!(info, ResourceId(0));
assert!(path.is_empty());
let mut path = Path::new("/name/value");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(info, ResourceId(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
let mut path = Path::new("/name/value2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 12);
assert_eq!(info, ResourceId(2));
assert_eq!(path.get("val").unwrap(), "value2");
let mut path = Path::new("/file/file.gz");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 13);
assert_eq!(info, ResourceId(3));
assert_eq!(path.get("file").unwrap(), "file");
assert_eq!(path.get("ext").unwrap(), "gz");
let mut path = Path::new("/vtest/ttt/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 14);
assert_eq!(info, ResourceId(4));
assert_eq!(path.get("val").unwrap(), "test");
assert_eq!(path.get("val2").unwrap(), "ttt");
let mut path = Path::new("/v/blah-blah/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 15);
assert_eq!(info, ResourceId(5));
assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
let mut path = Path::new("/test2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 16);
assert_eq!(info, ResourceId(6));
assert_eq!(path.get("test").unwrap(), "index");
let mut path = Path::new("/bbb/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 17);
assert_eq!(info, ResourceId(7));
assert_eq!(path.get("test").unwrap(), "bbb");
let mut path = Path::new("/v2/blah-blah/test.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 18);
assert_eq!(info, ResourceId(8));
assert_eq!(path.get("custom").unwrap(), "blah-blah");
}
#[test]
fn test_recognizer_2() {
let mut router = Router::<usize>::build();
router.path("/index.json", 10);
router.path("/{source}.json", 11);
let mut router = router.finish();
let mut path = Path::new("/index.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
}
#[test]
fn test_recognizer_with_path_skip() {
let mut router = Router::<usize>::build();
router.path("/name", 10).0.set_id(0);
router.path("/name/{val}", 11).0.set_id(1);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(5);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test/name");
path.skip(5);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
println!("\n=======================");
let mut path = Path::new("/test/name/value");
path.skip(5);
let (h, id) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(id, ResourceId(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
// same patterns
let mut router = Router::<usize>::build();
router.path("/name", 10);
router.path("/name/{val}", 11);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(5);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test2/name-test");
path.skip(6);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name/ttt");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(&path["val"], "ttt");
}
}

397
ntex-router/src/tree.rs Normal file
View file

@ -0,0 +1,397 @@
use std::mem;
use super::path::PathItem;
use super::resource::{ResourceDef, Segment};
use super::{Resource, ResourcePath};
#[derive(Debug)]
pub(super) struct Tree {
key: Vec<Segment>,
value: Vec<Value>,
children: Vec<Tree>,
}
#[derive(Copy, Clone, Debug)]
enum Value {
Val(usize),
Slesh(usize),
Prefix(usize),
PrefixSlesh(usize),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum PathState {
Empty,
Slesh,
Tail,
}
impl Value {
fn value(&self) -> usize {
match self {
Value::Val(v) => *v,
Value::Slesh(v) => *v,
Value::Prefix(v) => *v,
Value::PrefixSlesh(v) => *v,
}
}
}
impl Default for Tree {
fn default() -> Tree {
Tree {
key: Vec::new(),
value: Vec::new(),
children: Vec::new(),
}
}
}
impl Tree {
pub(crate) fn new(resource: &ResourceDef, value: usize) -> Tree {
if resource.tp.is_empty() {
panic!("Non empty key is expected");
}
let val = if resource.tp[0].slesh {
if resource.prefix {
Value::PrefixSlesh(value)
} else {
Value::Slesh(value)
}
} else if resource.prefix {
Value::Prefix(value)
} else {
Value::Val(value)
};
let mut tree = Tree::child(resource.tp[0].tp.clone(), Some(val));
for seg in &resource.tp[1..] {
let val = if seg.slesh {
if resource.prefix {
Value::PrefixSlesh(value)
} else {
Value::Slesh(value)
}
} else if resource.prefix {
Value::Prefix(value)
} else {
Value::Val(value)
};
tree.insert_path(seg.tp.clone(), val)
}
tree
}
fn child(key: Vec<Segment>, value: Option<Value>) -> Tree {
let value = if let Some(val) = value {
vec![val]
} else {
Vec::new()
};
Tree {
key,
value,
children: Vec::new(),
}
}
pub(super) fn insert(&mut self, resource: &ResourceDef, value: usize) {
for seg in &resource.tp {
let value = if seg.slesh {
if resource.prefix {
Value::PrefixSlesh(value)
} else {
Value::Slesh(value)
}
} else if resource.prefix {
Value::Prefix(value)
} else {
Value::Val(value)
};
let key: Vec<_> = seg.tp.to_vec();
self.insert_path(key, value);
}
}
fn insert_path(&mut self, key: Vec<Segment>, value: Value) {
let p = common_prefix(&self.key, &key);
// split current key, and move all children to sub tree
if p < self.key.len() {
let child = Tree {
key: self.key.split_off(p),
value: mem::replace(&mut self.value, Vec::new()),
children: mem::take(&mut self.children),
};
self.children.push(child);
}
// update value if key is the same
if p == key.len() {
self.value.push(value);
} else {
// insert into sub tree
let mut child = self
.children
.iter_mut()
.find(|x| common_prefix(&x.key, &key[p..]) > 0);
if let Some(ref mut child) = child {
child.insert_path(key[p..].to_vec(), value)
} else {
self.children
.push(Tree::child(key[p..].to_vec(), Some(value)));
}
}
}
fn get_value<T, R, F>(&self, resource: &R, check: &F) -> Option<usize>
where
T: ResourcePath,
R: Resource<T>,
F: Fn(usize, &R) -> bool,
{
for val in &self.value {
let v = val.value();
if check(v, resource) {
return Some(v);
}
}
None
}
pub(crate) fn find<T, R>(&self, resource: &mut R) -> Option<usize>
where
T: ResourcePath,
R: Resource<T>,
{
self.find_checked(resource, &|_, _| true)
}
pub(crate) fn find_checked<T, R, F>(
&self,
resource: &mut R,
check: &F,
) -> Option<usize>
where
T: ResourcePath,
R: Resource<T>,
F: Fn(usize, &R) -> bool,
{
let path = resource.resource_path();
let mut segments = mem::take(&mut path.segments);
let path = resource.path();
println!("\n\n\n=========== {:?}", path);
if self.key.is_empty() {
if path == "/" {
if let Some(val) = self.get_value(resource, check) {
return Some(val);
}
}
let res = self
.children
.iter()
.map(|x| x.find_inner2(path, resource, check, 1, &mut segments))
.filter_map(|x| x)
.next();
return if let Some((val, skip)) = res {
let path = resource.resource_path();
path.segments = segments;
path.skip = path.skip + skip as u16;
Some(val)
} else {
None
};
}
if path.is_empty() {
return None;
}
if let Some((val, skip)) =
self.find_inner2(path, resource, check, 1, &mut segments)
{
let path = resource.resource_path();
path.segments = segments;
path.skip = path.skip + skip as u16;
Some(val)
} else {
None
}
}
fn find_inner2<T, R, F>(
&self,
path: &str,
resource: &R,
check: &F,
mut skip: usize,
segments: &mut Vec<(&'static str, PathItem)>,
) -> Option<(usize, usize)>
where
T: ResourcePath,
R: Resource<T>,
F: Fn(usize, &R) -> bool,
{
// println!("K: {:?} {:?}", self.value, self.key);
// println!("P: {:?} {:?}", path, skip);
let mut key: &[_] = &self.key;
let mut path = &path[1..];
loop {
let idx = if let Some(idx) = path.find('/') {
idx
} else {
path.len()
};
let segment = T::unquote(&path[..idx]);
//println!("ITEM: {:?} {:?}", segment, key[0]);
// check segment match
let is_match = match key[0] {
Segment::Static(ref pattern) => pattern == segment.as_ref(),
Segment::Dynamic {
ref pattern,
ref names,
tail,
..
} => {
// println!(
// "MATCH: {:?} {:?} {:?} {:?}",
// pattern.as_str(),
// segment,
// pattern.is_match(&segment),
// tail,
// );
// special treatment for tail, it matches regardless of sleshes
let seg = if tail { path } else { segment.as_ref() };
if let Some(captures) = pattern.captures(seg) {
let mut is_match = true;
for name in names.iter() {
if let Some(m) = captures.name(&name) {
segments.push((
name,
PathItem::Segment(m.as_str().to_string()),
));
} else {
log::error!(
"Dynamic path match but not all segments found: {}",
name
);
is_match = false;
break;
}
}
// we have to process checker for tail matches separately
if tail && is_match {
// checker
if let Some(val) = self.get_value(resource, check) {
return Some((val, skip + idx));
}
}
is_match
} else {
false
}
}
};
if is_match {
key = &key[1..];
skip += idx + 1;
// check path length (path is consumed)
if idx == path.len() {
return {
if key.is_empty() {
// checker
for val in &self.value {
let v = match val {
Value::Val(v) | Value::Prefix(v) => *v,
Value::Slesh(_) | Value::PrefixSlesh(_) => continue,
};
if check(v, resource) {
return Some((v, skip));
}
}
}
None
};
} else if key.is_empty() {
path = &path[idx..];
let p = if path.is_empty() {
PathState::Empty
} else if path == "/" {
PathState::Slesh
} else {
PathState::Tail
};
for val in &self.value {
let v = match val {
Value::Val(v) => {
if p == PathState::Empty {
*v
} else {
continue;
}
}
Value::Slesh(v) => {
if p == PathState::Slesh {
*v
} else {
continue;
}
}
Value::Prefix(v) => {
if p == PathState::Slesh || p == PathState::Tail {
*v
} else {
continue;
}
}
Value::PrefixSlesh(v) => {
if p == PathState::Slesh || p == PathState::Tail {
*v
} else {
continue;
}
}
};
if check(v, resource) {
return Some((v, skip - 1));
}
}
// println!("TREE: {:#?}", self);
return self
.children
.iter()
.map(|x| x.find_inner2(path, resource, check, skip, segments))
.filter_map(|x| x)
.next();
} else {
path = &path[idx + 1..];
}
} else {
return None;
}
}
}
}
fn common_prefix(k1: &[Segment], k2: &[Segment]) -> usize {
k1.iter()
.zip(k2.iter())
.take_while(|&(a, b)| a == b)
.count()
}