diff --git a/router/src/de.rs b/router/src/de.rs index 89622fd3..1ce797bd 100644 --- a/router/src/de.rs +++ b/router/src/de.rs @@ -2,7 +2,7 @@ use serde::de::{self, Deserializer, Error as DeError, Visitor}; use serde::forward_to_deserialize_any; use crate::path::{Path, PathIter}; -use crate::RequestPath; +use crate::ResourcePath; macro_rules! unsupported_type { ($trait_fn:ident, $name:expr) => { @@ -33,17 +33,17 @@ macro_rules! parse_single_value { } } -pub struct PathDeserializer<'de, T: RequestPath + 'de> { +pub struct PathDeserializer<'de, T: ResourcePath + 'de> { path: &'de Path, } -impl<'de, T: RequestPath + 'de> PathDeserializer<'de, T> { +impl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> { pub fn new(path: &'de Path) -> Self { PathDeserializer { path } } } -impl<'de, T: RequestPath + 'de> Deserializer<'de> for PathDeserializer<'de, T> { +impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> { type Error = de::value::Error; fn deserialize_map(self, visitor: V) -> Result @@ -206,12 +206,12 @@ impl<'de, T: RequestPath + 'de> Deserializer<'de> for PathDeserializer<'de, T> { parse_single_value!(deserialize_char, visit_char, "char"); } -struct ParamsDeserializer<'de, T: RequestPath> { +struct ParamsDeserializer<'de, T: ResourcePath> { params: PathIter<'de, T>, current: Option<(&'de str, &'de str)>, } -impl<'de, T: RequestPath> de::MapAccess<'de> for ParamsDeserializer<'de, T> { +impl<'de, T: ResourcePath> de::MapAccess<'de> for ParamsDeserializer<'de, T> { type Error = de::value::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> @@ -406,11 +406,11 @@ impl<'de> Deserializer<'de> for Value<'de> { unsupported_type!(deserialize_identifier, "identifier"); } -struct ParamsSeq<'de, T: RequestPath> { +struct ParamsSeq<'de, T: ResourcePath> { params: PathIter<'de, T>, } -impl<'de, T: RequestPath> de::SeqAccess<'de> for ParamsSeq<'de, T> { +impl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> { type Error = de::value::Error; fn next_element_seed(&mut self, seed: U) -> Result, Self::Error> diff --git a/router/src/lib.rs b/router/src/lib.rs index 509cd413..07836641 100644 --- a/router/src/lib.rs +++ b/router/src/lib.rs @@ -1,31 +1,31 @@ //! Resource path matching library. mod de; mod path; -mod pattern; +mod resource; mod router; pub use self::de::PathDeserializer; pub use self::path::Path; -pub use self::pattern::Pattern; +pub use self::resource::ResourceDef; pub use self::router::{ResourceInfo, Router, RouterBuilder}; -pub trait RequestPath { +pub trait ResourcePath { fn path(&self) -> &str; } -impl RequestPath for String { +impl ResourcePath for String { fn path(&self) -> &str { self.as_str() } } -impl<'a> RequestPath for &'a str { +impl<'a> ResourcePath for &'a str { fn path(&self) -> &str { self } } -impl> RequestPath for string::String { +impl> ResourcePath for string::String { fn path(&self) -> &str { &*self } @@ -39,10 +39,10 @@ pub use self::url::Url; #[cfg(feature = "http")] mod http_support { - use super::RequestPath; + use super::ResourcePath; use http::Uri; - impl RequestPath for Uri { + impl ResourcePath for Uri { fn path(&self) -> &str { self.path() } diff --git a/router/src/path.rs b/router/src/path.rs index 9fe94f25..58666bdc 100644 --- a/router/src/path.rs +++ b/router/src/path.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use serde::de; use crate::de::PathDeserializer; -use crate::RequestPath; +use crate::ResourcePath; #[derive(Debug, Clone, Copy)] pub(crate) enum PathItem { @@ -42,7 +42,7 @@ impl Clone for Path { } } -impl Path { +impl Path { pub fn new(path: T) -> Path { Path { path, @@ -165,7 +165,7 @@ pub struct PathIter<'a, T> { params: &'a Path, } -impl<'a, T: RequestPath> Iterator for PathIter<'a, T> { +impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> { type Item = (&'a str, &'a str); #[inline] @@ -183,7 +183,7 @@ impl<'a, T: RequestPath> Iterator for PathIter<'a, T> { } } -impl<'a, T: RequestPath> Index<&'a str> for Path { +impl<'a, T: ResourcePath> Index<&'a str> for Path { type Output = str; fn index(&self, name: &'a str) -> &str { @@ -192,7 +192,7 @@ impl<'a, T: RequestPath> Index<&'a str> for Path { } } -impl Index for Path { +impl Index for Path { type Output = str; fn index(&self, idx: usize) -> &str { diff --git a/router/src/pattern.rs b/router/src/resource.rs similarity index 84% rename from router/src/pattern.rs rename to router/src/resource.rs index 24d96a9c..18a10f0e 100644 --- a/router/src/pattern.rs +++ b/router/src/resource.rs @@ -5,20 +5,35 @@ use std::rc::Rc; use regex::{escape, Regex}; use crate::path::{Path, PathItem}; -use crate::RequestPath; +use crate::ResourcePath; const MAX_DYNAMIC_SEGMENTS: usize = 16; -/// Resource type describes an entry in resources table +/// ResourceDef describes an entry in resources table /// -/// Resource pattern can contain only 16 dynamic segments +/// Resource definition can contain only 16 dynamic segments #[derive(Clone, Debug)] -pub struct Pattern { +pub struct ResourceDef { tp: PatternType, + rtp: ResourceType, + name: String, pattern: String, elements: Vec, } +#[derive(Debug, Copy, Clone, PartialEq)] +/// Resource type +pub enum ResourceType { + /// Normal resource + Normal, + /// Resource for application default handler + Default, + /// External resource + External, + /// Unknown resource type + Unset, +} + #[derive(Debug, Clone, PartialEq)] enum PatternElement { Str(String), @@ -32,12 +47,12 @@ enum PatternType { Dynamic(Regex, Vec>, usize), } -impl Pattern { +impl ResourceDef { /// Parse path pattern and create new `Pattern` instance. /// /// Panics if path pattern is wrong. pub fn new(path: &str) -> Self { - Pattern::with_prefix(path, false) + ResourceDef::with_prefix(path, false) } /// Parse path pattern and create new `Pattern` instance. @@ -46,13 +61,22 @@ impl Pattern { /// /// Panics if path regex pattern is wrong. pub fn prefix(path: &str) -> Self { - Pattern::with_prefix(path, true) + ResourceDef::with_prefix(path, true) + } + + /// Construct external resource def + /// + /// Panics if path pattern is malformed. + pub fn external(path: &str) -> Self { + let mut resource = ResourceDef::with_prefix(path, false); + resource.rtp = ResourceType::External; + resource } /// 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 (pattern, elements, is_dynamic, len) = Pattern::parse(&path, for_prefix); + let (pattern, elements, is_dynamic, len) = ResourceDef::parse(&path, for_prefix); let tp = if is_dynamic { let re = match Regex::new(&pattern) { @@ -71,9 +95,11 @@ impl Pattern { PatternType::Static(pattern.clone()) }; - Pattern { + ResourceDef { tp, elements, + name: String::new(), + rtp: ResourceType::Normal, pattern: path.to_owned(), } } @@ -93,7 +119,7 @@ impl Pattern { } /// Is the given path and parameters a match against this pattern? - pub fn match_path(&self, path: &mut Path) -> bool { + pub fn match_path(&self, path: &mut Path) -> bool { match self.tp { PatternType::Static(ref s) => { if s == path.path() { @@ -261,20 +287,32 @@ impl Pattern { } } -impl PartialEq for Pattern { - fn eq(&self, other: &Pattern) -> bool { +impl Eq for ResourceDef {} + +impl PartialEq for ResourceDef { + fn eq(&self, other: &ResourceDef) -> bool { self.pattern == other.pattern } } -impl Eq for Pattern {} - -impl Hash for Pattern { +impl Hash for ResourceDef { fn hash(&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 for ResourceDef { + fn from(path: String) -> ResourceDef { + ResourceDef::new(&path) + } +} + #[cfg(test)] mod tests { use super::*; @@ -282,29 +320,29 @@ mod tests { #[test] fn test_parse_static() { - let re = Pattern::new("/"); + let re = ResourceDef::new("/"); assert!(re.is_match("/")); assert!(!re.is_match("/a")); - let re = Pattern::new("/name"); + let re = ResourceDef::new("/name"); assert!(re.is_match("/name")); assert!(!re.is_match("/name1")); assert!(!re.is_match("/name/")); assert!(!re.is_match("/name~")); - let re = Pattern::new("/name/"); + let re = ResourceDef::new("/name/"); assert!(re.is_match("/name/")); assert!(!re.is_match("/name")); assert!(!re.is_match("/name/gs")); - let re = Pattern::new("/user/profile"); + let re = ResourceDef::new("/user/profile"); assert!(re.is_match("/user/profile")); assert!(!re.is_match("/user/profile/profile")); } #[test] fn test_parse_param() { - let re = Pattern::new("/user/{id}"); + let re = ResourceDef::new("/user/{id}"); assert!(re.is_match("/user/profile")); assert!(re.is_match("/user/2345")); assert!(!re.is_match("/user/2345/")); @@ -318,7 +356,7 @@ mod tests { assert!(re.match_path(&mut path)); assert_eq!(path.get("id").unwrap(), "1245125"); - let re = Pattern::new("/v{version}/resource/{id}"); + let re = ResourceDef::new("/v{version}/resource/{id}"); assert!(re.is_match("/v1/resource/320120")); assert!(!re.is_match("/v/resource/1")); assert!(!re.is_match("/resource")); @@ -328,7 +366,7 @@ mod tests { assert_eq!(path.get("version").unwrap(), "151"); assert_eq!(path.get("id").unwrap(), "adahg32"); - let re = Pattern::new("/{id:[[:digit:]]{6}}"); + let re = ResourceDef::new("/{id:[[:digit:]]{6}}"); assert!(re.is_match("/012345")); assert!(!re.is_match("/012")); assert!(!re.is_match("/01234567")); @@ -341,7 +379,7 @@ mod tests { #[test] fn test_parse_urlencoded_param() { - let re = Pattern::new("/user/{id}/test"); + let re = ResourceDef::new("/user/{id}/test"); let mut path = Path::new("/user/2345/test"); assert!(re.match_path(&mut path)); @@ -359,14 +397,14 @@ mod tests { #[test] fn test_resource_prefix() { - let re = Pattern::prefix("/name"); + let re = ResourceDef::prefix("/name"); assert!(re.is_match("/name")); assert!(re.is_match("/name/")); assert!(re.is_match("/name/test/test")); assert!(re.is_match("/name1")); assert!(re.is_match("/name~")); - let re = Pattern::prefix("/name/"); + let re = ResourceDef::prefix("/name/"); assert!(re.is_match("/name/")); assert!(re.is_match("/name/gs")); assert!(!re.is_match("/name")); @@ -374,7 +412,7 @@ mod tests { #[test] fn test_reousrce_prefix_dynamic() { - let re = Pattern::prefix("/{name}/"); + let re = ResourceDef::prefix("/{name}/"); assert!(re.is_match("/name/")); assert!(re.is_match("/name/gs")); assert!(!re.is_match("/name")); diff --git a/router/src/router.rs b/router/src/router.rs index 4568e778..e7844c0b 100644 --- a/router/src/router.rs +++ b/router/src/router.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use std::rc::Rc; use crate::path::Path; -use crate::pattern::Pattern; -use crate::RequestPath; +use crate::resource::ResourceDef; +use crate::ResourcePath; #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum ResourceId { @@ -20,15 +20,15 @@ pub struct ResourceInfo { #[derive(Default, Debug)] pub(crate) struct ResourceMap { - root: Option, - named: HashMap, - patterns: Vec, + root: Option, + named: HashMap, + patterns: Vec, } /// Resource router. pub struct Router { rmap: Rc, - named: HashMap, + named: HashMap, resources: Vec, } @@ -41,7 +41,7 @@ impl Router { } } - pub fn recognize(&self, path: &mut Path) -> Option<(&T, ResourceInfo)> { + pub fn recognize(&self, path: &mut Path) -> Option<(&T, ResourceInfo)> { if !path.path().is_empty() { for (idx, resource) in self.rmap.patterns.iter().enumerate() { if resource.match_path(path) { @@ -56,7 +56,7 @@ impl Router { None } - pub fn recognize_mut( + pub fn recognize_mut( &mut self, path: &mut Path, ) -> Option<(&mut T, ResourceInfo)> { @@ -94,11 +94,11 @@ impl<'a, T> IntoIterator for &'a mut Router { } impl ResourceMap { - fn register(&mut self, pattern: Pattern) { + fn register(&mut self, pattern: ResourceDef) { self.patterns.push(pattern); } - fn register_named(&mut self, name: String, pattern: Pattern) { + fn register_named(&mut self, name: String, pattern: ResourceDef) { self.patterns.push(pattern.clone()); self.named.insert(name, pattern); } @@ -110,18 +110,18 @@ impl ResourceMap { pub struct RouterBuilder { rmap: ResourceMap, - named: HashMap, + named: HashMap, resources: Vec, } impl RouterBuilder { pub fn path(&mut self, path: &str, resource: T) { - self.rmap.register(Pattern::new(path)); + self.rmap.register(ResourceDef::new(path)); self.resources.push(resource); } pub fn prefix(&mut self, prefix: &str, resource: T) { - self.rmap.register(Pattern::prefix(prefix)); + self.rmap.register(ResourceDef::prefix(prefix)); self.resources.push(resource); } diff --git a/router/src/url.rs b/router/src/url.rs index 2af24b7d..f5ed57e9 100644 --- a/router/src/url.rs +++ b/router/src/url.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::RequestPath; +use crate::ResourcePath; #[allow(dead_code)] const GEN_DELIMS: &[u8] = b":/?#[]@"; @@ -67,7 +67,7 @@ impl Url { } } -impl RequestPath for Url { +impl ResourcePath for Url { fn path(&self) -> &str { self.path() } @@ -190,11 +190,11 @@ mod tests { use http::{HttpTryFrom, Uri}; use super::*; - use crate::{Path, Pattern}; + use crate::{Path, ResourceDef}; #[test] fn test_parse_url() { - let re = Pattern::new("/user/{id}/test"); + let re = ResourceDef::new("/user/{id}/test"); let url = Uri::try_from("/user/2345/test").unwrap(); let mut path = Path::new(Url::new(url));