diff --git a/router/src/resource.rs b/router/src/resource.rs index 32e3d99b..27c604a4 100644 --- a/router/src/resource.rs +++ b/router/src/resource.rs @@ -14,26 +14,13 @@ const MAX_DYNAMIC_SEGMENTS: usize = 16; /// Resource definition can contain only 16 dynamic segments #[derive(Clone, Debug)] pub struct ResourceDef { + id: u16, 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), @@ -75,13 +62,14 @@ impl ResourceDef { ResourceDef::with_prefix(&insert_slash(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 + /// 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 @@ -109,12 +97,22 @@ impl ResourceDef { ResourceDef { tp, elements, + id: 0, name: String::new(), - rtp: ResourceType::Normal, pattern: path.to_owned(), } } + /// 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 diff --git a/router/src/router.rs b/router/src/router.rs index 725a5534..21bcc674 100644 --- a/router/src/router.rs +++ b/router/src/router.rs @@ -1,170 +1,97 @@ -use std::collections::HashMap; -use std::rc::Rc; - -use crate::resource::ResourceDef; -use crate::{Resource, ResourcePath}; +use crate::{Resource, ResourceDef, ResourcePath}; #[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) enum ResourceId { - Default, - Normal(u16), -} +pub struct ResourceId(pub u16); /// Information about current resource #[derive(Clone, Debug)] pub struct ResourceInfo { - rmap: Rc, resource: ResourceId, } -#[derive(Default, Debug)] -pub(crate) struct ResourceMap { - root: Option, - named: HashMap, - patterns: Vec, -} - /// Resource router. -pub struct Router { - rmap: Rc, - named: HashMap, - resources: Vec<(T, Option)>, -} +pub struct Router(Vec<(ResourceDef, T, Option)>); impl Router { pub fn build() -> RouterBuilder { RouterBuilder { - rmap: ResourceMap::default(), - named: HashMap::new(), resources: Vec::new(), } } - 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(res.resource_path()) { - let info = ResourceInfo { - rmap: self.rmap.clone(), - resource: ResourceId::Normal(idx as u16), - }; - return Some((&self.resources[idx].0, info)); + pub fn recognize(&self, path: &mut R) -> Option<(&T, ResourceId)> + where + R: Resource

, + P: ResourcePath, + { + for item in self.0.iter() { + if item.0.match_path(path.resource_path()) { + return Some((&item.1, ResourceId(item.0.id()))); } } None } - pub fn recognize_mut, P: ResourcePath>( - &mut self, - res: &mut R, - ) -> Option<(&mut T, ResourceInfo)> { - for (idx, resource) in self.rmap.patterns.iter().enumerate() { - 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].0, info)); + pub fn recognize_mut(&mut self, res: &mut R) -> Option<(&mut T, ResourceId)> + where + R: Resource

, + P: ResourcePath, + { + for item in self.0.iter_mut() { + if item.0.match_path(res.resource_path()) { + return Some((&mut item.1, ResourceId(item.0.id()))); } } None } - pub fn recognize_mut_checked, P: ResourcePath, F>( + pub fn recognize_mut_checked( &mut self, res: &mut R, check: F, - ) -> Option<(&mut T, ResourceInfo)> + ) -> Option<(&mut T, ResourceId)> where F: Fn(&R, &Option) -> bool, + R: Resource

, + P: ResourcePath, { - 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)); + for item in self.0.iter_mut() { + if item.0.match_path(res.resource_path()) && check(res, &item.2) { + return Some((&mut item.1, ResourceId(item.0.id()))); } } None } } -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, 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() - } -} - -impl ResourceMap { - fn register(&mut self, pattern: ResourceDef) { - self.patterns.push(pattern); - } - - fn register_named(&mut self, name: String, pattern: ResourceDef) { - self.patterns.push(pattern.clone()); - self.named.insert(name, pattern); - } - - fn has_resource(&self, path: &str) -> bool { - unimplemented!() - } -} - pub struct RouterBuilder { - rmap: ResourceMap, - named: HashMap, - resources: Vec<(T, Option)>, + resources: Vec<(ResourceDef, T, Option)>, } 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, None)); + pub fn path(&mut self, path: &str, resource: T) -> &mut (ResourceDef, T, Option) { + 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) { - self.rmap.register(ResourceDef::prefix(prefix)); - self.resources.push((resource, None)); + pub fn prefix(&mut self, prefix: &str, resource: T) -> &mut (ResourceDef, T, Option) { + 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) { - self.rmap.register(rdef); - 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; + pub fn rdef(&mut self, rdef: ResourceDef, resource: T) -> &mut (ResourceDef, T, Option) { + self.resources.push((rdef, resource, None)); + self.resources.last_mut().unwrap() } /// Finish configuration and create router instance. pub fn finish(self) -> Router { - Router { - rmap: Rc::new(self.rmap), - named: self.named, - resources: self.resources, - } + Router(self.resources) } } @@ -176,14 +103,14 @@ mod tests { #[test] fn test_recognizer_1() { let mut router = Router::::build(); - router.path("/name", 10); - router.path("/name/{val}", 11); - router.path("/name/{val}/index.html", 12); - router.path("/file/{file}.{ext}", 13); - router.path("/v{val}/{val2}/index.html", 14); - router.path("/v/{tail:.*}", 15); - router.path("/test2/{test}.html", 16); - router.path("/{test}/index.html", 17); + 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); let mut router = router.finish(); let mut path = Path::new("/unknown"); @@ -192,52 +119,52 @@ mod tests { let mut path = Path::new("/name"); let (h, info) = router.recognize_mut(&mut path).unwrap(); assert_eq!(*h, 10); - assert_eq!(info.resource, ResourceId::Normal(0)); + 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.resource, ResourceId::Normal(1)); + 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.resource, ResourceId::Normal(2)); + 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.resource, ResourceId::Normal(3)); + 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.resource, ResourceId::Normal(4)); + 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.resource, ResourceId::Normal(5)); + 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.resource, ResourceId::Normal(6)); + 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.resource, ResourceId::Normal(7)); + assert_eq!(info, ResourceId(7)); assert_eq!(path.get("test").unwrap(), "bbb"); } @@ -260,8 +187,8 @@ mod tests { #[test] fn test_recognizer_with_prefix() { let mut router = Router::::build(); - router.path("/name", 10); - router.path("/name/{val}", 11); + 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"); @@ -275,9 +202,9 @@ mod tests { let mut path = Path::new("/test/name/value"); path.skip(5); - let (h, info) = router.recognize_mut(&mut path).unwrap(); + let (h, id) = router.recognize_mut(&mut path).unwrap(); assert_eq!(*h, 11); - assert_eq!(info.resource, ResourceId::Normal(1)); + assert_eq!(id, ResourceId(1)); assert_eq!(path.get("val").unwrap(), "value"); assert_eq!(&path["val"], "value"); @@ -306,134 +233,4 @@ mod tests { assert_eq!(*h, 11); assert_eq!(&path["val"], "ttt"); } - - // #[test] - // fn test_request_resource() { - // let mut router = Router::<()>::default(); - // let mut resource = Resource::new(ResourcePattern::new("/index.json")); - // resource.name("r1"); - // router.register_resource(resource); - // let mut resource = Resource::new(ResourcePattern::new("/test.json")); - // resource.name("r2"); - // router.register_resource(resource); - - // let req = TestRequest::with_uri("/index.json").finish(); - // let info = router.recognize(&req, &(), 0); - // assert_eq!(info.resource, ResourceId::Normal(0)); - - // assert_eq!(info.name(), "r1"); - - // let req = TestRequest::with_uri("/test.json").finish(); - // let info = router.recognize(&req, &(), 0); - // assert_eq!(info.resource, ResourceId::Normal(1)); - // assert_eq!(info.name(), "r2"); - // } - - // #[test] - // fn test_has_resource() { - // let mut router = Router::<()>::default(); - // let scope = Scope::new("/test").resource("/name", |_| "done"); - // router.register_scope(scope); - - // { - // let info = router.default_route_info(); - // assert!(!info.has_resource("/test")); - // assert!(info.has_resource("/test/name")); - // } - - // let scope = Scope::new("/test2").nested("/test10", |s| s.resource("/name", |_| "done")); - // router.register_scope(scope); - - // let info = router.default_route_info(); - // assert!(info.has_resource("/test2/test10/name")); - // } - - // #[test] - // fn test_url_for() { - // let mut router = Router::<()>::new(ResourcePattern::prefix("")); - - // let mut resource = Resource::new(ResourcePattern::new("/tttt")); - // resource.name("r0"); - // router.register_resource(resource); - - // let scope = Scope::new("/test").resource("/name", |r| { - // r.name("r1"); - // }); - // router.register_scope(scope); - - // let scope = - // Scope::new("/test2").nested("/test10", |s| s.resource("/name", |r| r.name("r2"))); - // router.register_scope(scope); - // router.finish(); - - // let req = TestRequest::with_uri("/test").request(); - // { - // let info = router.default_route_info(); - - // let res = info - // .url_for(&req, "r0", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/tttt"); - - // let res = info - // .url_for(&req, "r1", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/test/name"); - - // let res = info - // .url_for(&req, "r2", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/test2/test10/name"); - // } - - // let req = TestRequest::with_uri("/test/name").request(); - // let info = router.recognize(&req, &(), 0); - // assert_eq!(info.resource, ResourceId::Normal(1)); - - // let res = info - // .url_for(&req, "r0", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/tttt"); - - // let res = info - // .url_for(&req, "r1", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/test/name"); - - // let res = info - // .url_for(&req, "r2", Vec::<&'static str>::new()) - // .unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/test2/test10/name"); - // } - - // #[test] - // fn test_url_for_dynamic() { - // let mut router = Router::<()>::new(ResourcePattern::prefix("")); - - // let mut resource = Resource::new(ResourcePattern::new("/{name}/test/index.{ext}")); - // resource.name("r0"); - // router.register_resource(resource); - - // let scope = Scope::new("/{name1}").nested("/{name2}", |s| { - // s.resource("/{name3}/test/index.{ext}", |r| r.name("r2")) - // }); - // router.register_scope(scope); - // router.finish(); - - // let req = TestRequest::with_uri("/test").request(); - // { - // let info = router.default_route_info(); - - // let res = info.url_for(&req, "r0", vec!["sec1", "html"]).unwrap(); - // assert_eq!(res.as_str(), "http://localhost:8080/sec1/test/index.html"); - - // let res = info - // .url_for(&req, "r2", vec!["sec1", "sec2", "sec3", "html"]) - // .unwrap(); - // assert_eq!( - // res.as_str(), - // "http://localhost:8080/sec1/sec2/sec3/test/index.html" - // ); - // } - // } }