From b9ec5d89149f56f2f1b8ad5a74d56ce6e6fe68cd Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 26 Jun 2020 19:59:40 +0100 Subject: [PATCH] find nested resource names --- src/request.rs | 6 +++-- src/rmap.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++----- src/service.rs | 2 +- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/request.rs b/src/request.rs index 0eef47692..332489614 100644 --- a/src/request.rs +++ b/src/request.rs @@ -137,9 +137,11 @@ impl HttpRequest { self.0.rmap.match_pattern(self.path()) } - /// Checks if a given path matches a route + /// The resource name that matched the path. Useful for logging and metrics. + /// + /// Returns a None when no resource is fully matched, including default services. #[inline] - pub fn match_name(&self) -> Option { + pub fn match_name(&self) -> Option<&str> { self.0.rmap.match_name(self.path()) } diff --git a/src/rmap.rs b/src/rmap.rs index a081713b6..5e79830ec 100644 --- a/src/rmap.rs +++ b/src/rmap.rs @@ -92,14 +92,22 @@ impl ResourceMap { } false } - /// Returns the name of the route that matches the given path or None if no match is - /// found. - pub fn match_name(&self, path: &str) -> Option { + + /// Returns the name of the route that matches the given path or None if no full match + /// is possible. + pub fn match_name(&self, path: &str) -> Option<&str> { let path = if path.is_empty() { "/" } else { path }; - for (pattern, _) in &self.patterns { - if pattern.pattern() == path { - return Some(pattern.name().to_string()); + for (pattern, rmap) in &self.patterns { + if let Some(ref rmap) = rmap { + if let Some(plen) = pattern.is_prefix_match(path) { + return rmap.match_name(&path[plen..]); + } + } else if pattern.is_match(path) { + return match pattern.name() { + "" => None, + s => Some(s), + }; } } @@ -315,4 +323,48 @@ mod tests { Some("/user/{id}/post/{post_id}/comment/{comment_id}".to_owned()) ); } + + #[test] + fn extract_matched_name() { + let mut root = ResourceMap::new(ResourceDef::root_prefix("")); + + let mut rdef = ResourceDef::new("/info"); + *rdef.name_mut() = "root_info".to_owned(); + root.add(&mut rdef, None); + + let mut user_map = ResourceMap::new(ResourceDef::root_prefix("")); + let mut rdef = ResourceDef::new("/"); + user_map.add(&mut rdef, None); + + let mut rdef = ResourceDef::new("/post/{post_id}"); + *rdef.name_mut() = "user_post".to_owned(); + user_map.add(&mut rdef, None); + + root.add( + &mut ResourceDef::root_prefix("/user/{id}"), + Some(Rc::new(user_map)), + ); + + let root = Rc::new(root); + root.finish(Rc::clone(&root)); + + // sanity check resource map setup + + assert!(root.has_resource("/info")); + assert!(!root.has_resource("/bar")); + + assert!(root.has_resource("/user/22")); + assert!(root.has_resource("/user/22/")); + assert!(root.has_resource("/user/22/post/55")); + + // extract patterns from paths + + assert!(root.match_name("/bar").is_none()); + assert!(root.match_name("/v44").is_none()); + + assert_eq!(root.match_name("/info"), Some("root_info")); + assert_eq!(root.match_name("/user/22"), None); + assert_eq!(root.match_name("/user/22/"), None); + assert_eq!(root.match_name("/user/22/post/55"), Some("user_post")); + } } diff --git a/src/service.rs b/src/service.rs index eb1b1c4f0..cba852a9c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -198,7 +198,7 @@ impl ServiceRequest { /// Counterpart to [`HttpRequest::match_name`](../struct.HttpRequest.html#method.match_name). #[inline] - pub fn match_name(&self) -> Option { + pub fn match_name(&self) -> Option<&str> { self.0.match_name() }