From 0ff0daa795ce725d56a9c8d5adc2553585bfb721 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 4 Mar 2019 11:47:03 -0800 Subject: [PATCH] allow custom checks for resource selection --- router/src/de.rs | 16 ++++----- router/src/lib.rs | 16 +++++---- router/src/path.rs | 10 +++--- router/src/resource.rs | 4 +-- router/src/router.rs | 81 ++++++++++++++++++++++++++++-------------- router/src/url.rs | 4 +-- 6 files changed, 82 insertions(+), 49 deletions(-) diff --git a/router/src/de.rs b/router/src/de.rs index 8a564740..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::Resource; +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: Resource + 'de> { +pub struct PathDeserializer<'de, T: ResourcePath + 'de> { path: &'de Path, } -impl<'de, T: Resource + 'de> PathDeserializer<'de, T> { +impl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> { pub fn new(path: &'de Path) -> Self { PathDeserializer { path } } } -impl<'de, T: Resource + '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: Resource + 'de> Deserializer<'de> for PathDeserializer<'de, T> { parse_single_value!(deserialize_char, visit_char, "char"); } -struct ParamsDeserializer<'de, T: Resource> { +struct ParamsDeserializer<'de, T: ResourcePath> { params: PathIter<'de, T>, current: Option<(&'de str, &'de str)>, } -impl<'de, T: Resource> 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: Resource> { +struct ParamsSeq<'de, T: ResourcePath> { params: PathIter<'de, T>, } -impl<'de, T: Resource> 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 2a805848..18d9894b 100644 --- a/router/src/lib.rs +++ b/router/src/lib.rs @@ -9,23 +9,27 @@ pub use self::path::Path; pub use self::resource::ResourceDef; pub use self::router::{ResourceInfo, Router, RouterBuilder}; -pub trait Resource { +pub trait Resource { + fn resource_path(&mut self) -> &mut Path; +} + +pub trait ResourcePath { fn path(&self) -> &str; } -impl Resource for String { +impl ResourcePath for String { fn path(&self) -> &str { self.as_str() } } -impl<'a> Resource for &'a str { +impl<'a> ResourcePath for &'a str { fn path(&self) -> &str { self } } -impl> Resource for string::String { +impl> ResourcePath for string::String { fn path(&self) -> &str { &*self } @@ -39,10 +43,10 @@ pub use self::url::Url; #[cfg(feature = "http")] mod http_support { - use super::Resource; + use super::ResourcePath; use http::Uri; - impl Resource 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 1d5162c3..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::Resource; +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: Resource> 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: Resource> Iterator for PathIter<'a, T> { } } -impl<'a, T: Resource> 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: Resource> 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/resource.rs b/router/src/resource.rs index aef7e1e2..18a10f0e 100644 --- a/router/src/resource.rs +++ b/router/src/resource.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use regex::{escape, Regex}; use crate::path::{Path, PathItem}; -use crate::Resource; +use crate::ResourcePath; const MAX_DYNAMIC_SEGMENTS: usize = 16; @@ -119,7 +119,7 @@ impl ResourceDef { } /// 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() { diff --git a/router/src/router.rs b/router/src/router.rs index 49f8020d..629c13eb 100644 --- a/router/src/router.rs +++ b/router/src/router.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use std::rc::Rc; -use crate::path::Path; use crate::resource::ResourceDef; -use crate::Resource; +use crate::{Resource, ResourcePath}; #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum ResourceId { @@ -26,14 +25,14 @@ pub(crate) struct ResourceMap { } /// Resource router. -pub struct Router { +pub struct Router { rmap: Rc, named: HashMap, - resources: Vec, + resources: Vec<(T, Option)>, } -impl Router { - pub fn build() -> RouterBuilder { +impl Router { + pub fn build() -> RouterBuilder { RouterBuilder { rmap: ResourceMap::default(), named: HashMap::new(), @@ -41,48 +40,71 @@ impl Router { } } - pub fn recognize(&self, path: &mut Path) -> Option<(&T, ResourceInfo)> { + pub fn recognize, P: ResourcePath>( + &self, + res: &mut R, + ) -> Option<(&T, ResourceInfo)> { for (idx, resource) in self.rmap.patterns.iter().enumerate() { - if resource.match_path(path) { + if resource.match_path(res.resource_path()) { let info = ResourceInfo { rmap: self.rmap.clone(), resource: ResourceId::Normal(idx as u16), }; - return Some((&self.resources[idx], info)); + return Some((&self.resources[idx].0, info)); } } None } - pub fn recognize_mut( + pub fn recognize_mut, P: ResourcePath>( &mut self, - path: &mut Path, + res: &mut R, ) -> Option<(&mut T, ResourceInfo)> { for (idx, resource) in self.rmap.patterns.iter().enumerate() { - if resource.match_path(path) { + if resource.match_path(res.resource_path()) { let info = ResourceInfo { rmap: self.rmap.clone(), resource: ResourceId::Normal(idx as u16), }; - return Some((&mut self.resources[idx], info)); + return Some((&mut self.resources[idx].0, info)); + } + } + None + } + + pub fn recognize_mut_checked, P: ResourcePath, F>( + &mut self, + res: &mut R, + check: F, + ) -> Option<(&mut T, ResourceInfo)> + where + F: Fn(&R, &Option) -> bool, + { + for (idx, resource) in self.rmap.patterns.iter().enumerate() { + if resource.match_path(res.resource_path()) && check(res, &self.resources[idx].1) { + let info = ResourceInfo { + rmap: self.rmap.clone(), + resource: ResourceId::Normal(idx as u16), + }; + return Some((&mut self.resources[idx].0, info)); } } None } } -impl<'a, T> IntoIterator for &'a Router { - type Item = &'a T; - type IntoIter = std::slice::Iter<'a, T>; +impl<'a, T, U> IntoIterator for &'a Router { + type Item = &'a (T, Option); + type IntoIter = std::slice::Iter<'a, (T, Option)>; fn into_iter(self) -> Self::IntoIter { self.resources.iter() } } -impl<'a, T> IntoIterator for &'a mut Router { - type Item = &'a mut T; - type IntoIter = std::slice::IterMut<'a, T>; +impl<'a, T, U> IntoIterator for &'a mut Router { + type Item = &'a mut (T, Option); + type IntoIter = std::slice::IterMut<'a, (T, Option)>; fn into_iter(self) -> Self::IntoIter { self.resources.iter_mut() @@ -104,33 +126,40 @@ impl ResourceMap { } } -pub struct RouterBuilder { +pub struct RouterBuilder { rmap: ResourceMap, named: HashMap, - resources: Vec, + resources: Vec<(T, Option)>, } -impl RouterBuilder { +impl RouterBuilder { /// Register resource for specified path. pub fn path(&mut self, path: &str, resource: T) { self.rmap.register(ResourceDef::new(path)); - self.resources.push(resource); + self.resources.push((resource, None)); } /// Register resource for specified path prefix. pub fn prefix(&mut self, prefix: &str, resource: T) { self.rmap.register(ResourceDef::prefix(prefix)); - self.resources.push(resource); + self.resources.push((resource, None)); } /// Register resource for ResourceDef pub fn rdef(&mut self, rdef: ResourceDef, resource: T) { self.rmap.register(rdef); - self.resources.push(resource); + self.resources.push((resource, None)); + } + + /// Method attachs user data to lastly added resource. + /// + /// This panics if no resources were added. + pub fn set_user_data(&mut self, userdata: Option) { + self.resources.last_mut().unwrap().1 = userdata; } /// Finish configuration and create router instance. - pub fn finish(self) -> Router { + pub fn finish(self) -> Router { Router { rmap: Rc::new(self.rmap), named: self.named, diff --git a/router/src/url.rs b/router/src/url.rs index 7435296e..f5ed57e9 100644 --- a/router/src/url.rs +++ b/router/src/url.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::Resource; +use crate::ResourcePath; #[allow(dead_code)] const GEN_DELIMS: &[u8] = b":/?#[]@"; @@ -67,7 +67,7 @@ impl Url { } } -impl Resource for Url { +impl ResourcePath for Url { fn path(&self) -> &str { self.path() }