rework IntoPatterns trait and codegen

This commit is contained in:
Rob Ede 2021-07-16 23:07:28 +01:00
parent a0fe2a9b2e
commit 5360b9eb58
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
4 changed files with 91 additions and 104 deletions

View File

@ -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"

View File

@ -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<String>;
/// Helper trait for type that could be converted to one or more path pattern.
pub trait IntoPatterns {
fn patterns(&self) -> Either<String, Vec<String>>;
}
impl IntoPattern for String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.clone()]
impl IntoPatterns for String {
fn patterns(&self) -> Either<String, Vec<String>> {
Either::Left(self.clone())
}
}
impl<'a> IntoPattern for &'a String {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![self.as_str().to_string()]
impl<'a> IntoPatterns for &'a String {
fn patterns(&self) -> Either<String, Vec<String>> {
Either::Left((*self).clone())
}
}
impl<'a> IntoPattern for &'a str {
fn is_single(&self) -> bool {
true
}
fn patterns(&self) -> Vec<String> {
vec![(*self).to_string()]
impl<'a> IntoPatterns for &'a str {
fn patterns(&self) -> Either<String, Vec<String>> {
Either::Left((*self).to_owned())
}
}
impl<T: AsRef<str>> IntoPattern for Vec<T> {
fn is_single(&self) -> bool {
self.len() == 1
}
impl<T: AsRef<str>> IntoPatterns for Vec<T> {
fn patterns(&self) -> Either<String, Vec<String>> {
let mut patterns = self.iter().map(|v| v.as_ref().to_owned());
fn patterns(&self) -> Vec<String> {
self.iter().map(|v| v.as_ref().to_string()).collect()
match patterns.size_hint() {
(1, _) => Either::Left(patterns.next().unwrap()),
_ => Either::Right(patterns.collect()),
}
}
}
macro_rules! array_patterns (($tp:ty, $num:tt) => {
impl IntoPattern for [$tp; $num] {
fn is_single(&self) -> bool {
$num == 1
}
fn patterns(&self) -> Vec<String> {
self.iter().map(|v| v.to_string()).collect()
macro_rules! array_patterns_single (($tp:ty) => {
impl IntoPatterns for [$tp; 1] {
fn patterns(&self) -> Either<String, Vec<String>> {
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<String, Vec<String>> {
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()

View File

@ -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<T: IntoPattern>(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<T: IntoPatterns>(path: T) -> Self {
match path.patterns() {
Either::Left(pattern) => ResourceDef::from_single_pattern(&pattern, false),
for pattern in path.patterns() {
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,
};
}
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());
data.push((re, names));
pattern_data.push((re, names));
}
_ => unreachable!(),
}
}
let pattern_re_set = RegexSet::new(re_set).unwrap();
ResourceDef {
id: 0,
pat_type: PatternType::DynamicSet(RegexSet::new(re_set).unwrap(), data),
elements: None,
name: String::new(),
pattern: "".to_owned(),
pattern: String::new(),
pat_type: PatternType::DynamicSet(pattern_re_set, pattern_data),
elements: None,
}
}
}
}

View File

@ -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<T, U = ()> {
impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path.
pub fn path<P: IntoPattern>(
pub fn path<P: IntoPatterns>(
&mut self,
path: P,
resource: T,