diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index 216d4bff..b0bea9b6 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -20,11 +20,12 @@ path = "src/lib.rs" default = ["http"] [dependencies] +bytestring = ">=0.1.5, <2" +either = "1.6" +http = { version = "0.2.3", optional = true } +log = "0.4" regex = "1.5" serde = "1" -bytestring = ">=0.1.5, <2" -log = "0.4" -http = { version = "0.2.3", optional = true } [dev-dependencies] http = "0.2.3" diff --git a/actix-router/src/lib.rs b/actix-router/src/lib.rs index 5850b103..72b03900 100644 --- a/actix-router/src/lib.rs +++ b/actix-router/src/lib.rs @@ -4,6 +4,8 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] +use either::Either; + mod de; mod path; mod resource; @@ -40,98 +42,64 @@ impl ResourcePath for bytestring::ByteString { } } -/// Helper trait for type that could be converted to path pattern -pub trait IntoPattern { - fn is_single(&self) -> bool; - - fn patterns(&self) -> Vec; +/// Helper trait for type that could be converted to one or more path pattern. +pub trait IntoPatterns { + fn patterns(&self) -> Either>; } -impl IntoPattern for String { - fn is_single(&self) -> bool { - true - } - - fn patterns(&self) -> Vec { - vec![self.clone()] +impl IntoPatterns for String { + fn patterns(&self) -> Either> { + Either::Left(self.clone()) } } -impl<'a> IntoPattern for &'a String { - fn is_single(&self) -> bool { - true - } - - fn patterns(&self) -> Vec { - vec![self.as_str().to_string()] +impl<'a> IntoPatterns for &'a String { + fn patterns(&self) -> Either> { + Either::Left((*self).clone()) } } -impl<'a> IntoPattern for &'a str { - fn is_single(&self) -> bool { - true - } - - fn patterns(&self) -> Vec { - vec![(*self).to_string()] +impl<'a> IntoPatterns for &'a str { + fn patterns(&self) -> Either> { + Either::Left((*self).to_owned()) } } -impl> IntoPattern for Vec { - fn is_single(&self) -> bool { - self.len() == 1 - } +impl> IntoPatterns for Vec { + fn patterns(&self) -> Either> { + let mut patterns = self.iter().map(|v| v.as_ref().to_owned()); - fn patterns(&self) -> Vec { - self.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 + match patterns.size_hint() { + (1, _) => Either::Left(patterns.next().unwrap()), + _ => Either::Right(patterns.collect()), } + } +} - fn patterns(&self) -> Vec { - self.iter().map(|v| v.to_string()).collect() +macro_rules! array_patterns_single (($tp:ty) => { + impl IntoPatterns for [$tp; 1] { + fn patterns(&self) -> Either> { + Either::Left(self[0].to_owned()) } } }); -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); +macro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => { + // for each array length specified in $num + $( + impl IntoPatterns for [$tp; $num] { + fn patterns(&self) -> Either> { + Either::Right(self.iter().map($str_fn).collect()) + } + } + )+ +}); -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); +array_patterns_single!(&str); +array_patterns_multiple!(&str, |&v| v.to_owned(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); + +array_patterns_single!(String); +array_patterns_multiple!(String, |v| v.clone(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); #[cfg(feature = "http")] mod url; @@ -140,10 +108,11 @@ mod url; pub use self::url::{Quoter, Url}; #[cfg(feature = "http")] -mod http_support { - use super::ResourcePath; +mod http_impls { use http::Uri; + use super::ResourcePath; + impl ResourcePath for Uri { fn path(&self) -> &str { self.path() diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index ad3d47e2..3d402c1b 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -6,10 +6,11 @@ use std::{ mem, }; +use either::Either; use regex::{escape, Regex, RegexSet}; use crate::path::{Path, PathItem}; -use crate::{IntoPattern, Resource, ResourcePath}; +use crate::{IntoPatterns, Resource, ResourcePath}; const MAX_DYNAMIC_SEGMENTS: usize = 16; @@ -25,9 +26,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; pub struct ResourceDef { id: u16, - /// Pattern type. - pat_type: PatternType, - /// Optional name of resource definition. Defaults to "". name: String, @@ -35,6 +33,9 @@ pub struct ResourceDef { // TODO: Sort of, in dynamic set pattern type it is blank, consider change to option. pattern: String, + /// Pattern type. + pat_type: PatternType, + /// List of elements that compose the pattern, in order. /// /// `None` with pattern type is DynamicSet. @@ -75,29 +76,45 @@ impl ResourceDef { /// Parse path pattern and create new `Pattern` instance. /// /// Panics if path pattern is malformed. - pub fn new(path: T) -> Self { - if path.is_single() { - ResourceDef::from_single_pattern(&path.patterns()[0], false) - } else { - let mut data = Vec::new(); - let mut re_set = Vec::new(); + pub fn new(path: T) -> Self { + match path.patterns() { + Either::Left(pattern) => ResourceDef::from_single_pattern(&pattern, false), - for pattern in path.patterns() { - match ResourceDef::parse(&pattern, false, true) { - (PatternType::Dynamic(re, names), _) => { - re_set.push(re.as_str().to_owned()); - data.push((re, names)); - } - _ => unreachable!(), + Either::Right(patterns) => { + if patterns.is_empty() { + // since zero length pattern sets are possible, return a useless `ResourceDef` + + return ResourceDef { + id: 0, + name: String::new(), + pattern: String::new(), + pat_type: PatternType::DynamicSet(RegexSet::empty(), Vec::new()), + elements: None, + }; } - } - ResourceDef { - id: 0, - pat_type: PatternType::DynamicSet(RegexSet::new(re_set).unwrap(), data), - elements: None, - name: String::new(), - pattern: "".to_owned(), + let mut re_set = Vec::with_capacity(patterns.len()); + let mut pattern_data = Vec::new(); + + for pattern in patterns { + match ResourceDef::parse(&pattern, false, true) { + (PatternType::Dynamic(re, names), _) => { + re_set.push(re.as_str().to_owned()); + pattern_data.push((re, names)); + } + _ => unreachable!(), + } + } + + let pattern_re_set = RegexSet::new(re_set).unwrap(); + + ResourceDef { + id: 0, + name: String::new(), + pattern: String::new(), + pat_type: PatternType::DynamicSet(pattern_re_set, pattern_data), + elements: None, + } } } } diff --git a/actix-router/src/router.rs b/actix-router/src/router.rs index aeb1aa36..007ef495 100644 --- a/actix-router/src/router.rs +++ b/actix-router/src/router.rs @@ -1,4 +1,4 @@ -use crate::{IntoPattern, Resource, ResourceDef, ResourcePath}; +use crate::{IntoPatterns, Resource, ResourceDef, ResourcePath}; #[derive(Debug, Copy, Clone, PartialEq)] pub struct ResourceId(pub u16); @@ -88,7 +88,7 @@ pub struct RouterBuilder { impl RouterBuilder { /// Register resource for specified path. - pub fn path( + pub fn path( &mut self, path: P, resource: T,