mirror of
https://github.com/actix/actix-web.git
synced 2025-02-08 23:32:20 +00:00
ResourceDef: support multiple-patterns as prefix (#2356)
Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
parent
c50eef6166
commit
7d01ece355
2 changed files with 128 additions and 129 deletions
|
@ -7,6 +7,9 @@
|
||||||
* Improve malformed path error message. [#384]
|
* Improve malformed path error message. [#384]
|
||||||
* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355]
|
||||||
* Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
* Prefix segments with trailing slashes define a trailing empty segment. [#2355]
|
||||||
|
* Support multi-pattern prefixes and joins. [#2356]
|
||||||
|
* `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356]
|
||||||
|
* Support `build_resource_path` on multi-pattern resources. [#2356]
|
||||||
* Minimum supported Rust version (MSRV) is now 1.51.
|
* Minimum supported Rust version (MSRV) is now 1.51.
|
||||||
|
|
||||||
[#378]: https://github.com/actix/actix-net/pull/378
|
[#378]: https://github.com/actix/actix-net/pull/378
|
||||||
|
@ -14,6 +17,7 @@
|
||||||
[#380]: https://github.com/actix/actix-net/pull/380
|
[#380]: https://github.com/actix/actix-net/pull/380
|
||||||
[#384]: https://github.com/actix/actix-net/pull/384
|
[#384]: https://github.com/actix/actix-net/pull/384
|
||||||
[#2355]: https://github.com/actix/actix-web/pull/2355
|
[#2355]: https://github.com/actix/actix-web/pull/2355
|
||||||
|
[#2356]: https://github.com/actix/actix-web/pull/2356
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.1 - 2021-07-20
|
## 0.5.0-beta.1 - 2021-07-20
|
||||||
|
|
|
@ -31,13 +31,13 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
||||||
/// # Pattern Format and Matching Behavior
|
/// # Pattern Format and Matching Behavior
|
||||||
///
|
///
|
||||||
/// Resource pattern is defined as a string of zero or more _segments_ where each segment is
|
/// Resource pattern is defined as a string of zero or more _segments_ where each segment is
|
||||||
/// preceeded by a slash `/`.
|
/// preceded by a slash `/`.
|
||||||
///
|
///
|
||||||
/// This means that pattern string __must__ either be empty or begin with a slash (`/`).
|
/// This means that pattern string __must__ either be empty or begin with a slash (`/`).
|
||||||
/// This also implies that a trailing slash in pattern defines an empty segment.
|
/// This also implies that a trailing slash in pattern defines an empty segment.
|
||||||
/// For example, the pattern `"/user/"` has two segments: `["user", ""]`
|
/// For example, the pattern `"/user/"` has two segments: `["user", ""]`
|
||||||
///
|
///
|
||||||
/// A key point to undertand is that `ResourceDef` matches segments, not strings.
|
/// A key point to underhand is that `ResourceDef` matches segments, not strings.
|
||||||
/// It matches segments individually.
|
/// It matches segments individually.
|
||||||
/// For example, the pattern `/user/` is not considered a prefix for the path `/user/123/456`,
|
/// For example, the pattern `/user/` is not considered a prefix for the path `/user/123/456`,
|
||||||
/// because the second segment doesn't match: `["user", ""]` vs `["user", "123", "456"]`.
|
/// because the second segment doesn't match: `["user", ""]` vs `["user", "123", "456"]`.
|
||||||
|
@ -220,17 +220,15 @@ pub struct ResourceDef {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
/// Pattern that generated the resource definition.
|
/// Pattern that generated the resource definition.
|
||||||
///
|
|
||||||
/// `None` when pattern type is `DynamicSet`.
|
|
||||||
patterns: Patterns,
|
patterns: Patterns,
|
||||||
|
|
||||||
|
is_prefix: bool,
|
||||||
|
|
||||||
/// Pattern type.
|
/// Pattern type.
|
||||||
pat_type: PatternType,
|
pat_type: PatternType,
|
||||||
|
|
||||||
/// List of segments that compose the pattern, in order.
|
/// List of segments that compose the pattern, in order.
|
||||||
///
|
segments: Vec<PatternSegment>,
|
||||||
/// `None` when pattern type is `DynamicSet`.
|
|
||||||
segments: Option<Vec<PatternSegment>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -248,9 +246,6 @@ enum PatternType {
|
||||||
/// Single constant/literal segment.
|
/// Single constant/literal segment.
|
||||||
Static(String),
|
Static(String),
|
||||||
|
|
||||||
/// Single constant/literal prefix segment.
|
|
||||||
Prefix(String),
|
|
||||||
|
|
||||||
/// Single regular expression and list of dynamic segment names.
|
/// Single regular expression and list of dynamic segment names.
|
||||||
Dynamic(Regex, Vec<&'static str>),
|
Dynamic(Regex, Vec<&'static str>),
|
||||||
|
|
||||||
|
@ -284,45 +279,7 @@ impl ResourceDef {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new<T: IntoPatterns>(paths: T) -> Self {
|
pub fn new<T: IntoPatterns>(paths: T) -> Self {
|
||||||
profile_method!(new);
|
profile_method!(new);
|
||||||
|
Self::new2(paths, false)
|
||||||
match paths.patterns() {
|
|
||||||
Patterns::Single(pattern) => ResourceDef::from_single_pattern(&pattern, false),
|
|
||||||
|
|
||||||
// since zero length pattern sets are possible
|
|
||||||
// just return a useless `ResourceDef`
|
|
||||||
Patterns::List(patterns) if patterns.is_empty() => ResourceDef {
|
|
||||||
id: 0,
|
|
||||||
name: None,
|
|
||||||
patterns: Patterns::List(patterns),
|
|
||||||
pat_type: PatternType::DynamicSet(RegexSet::empty(), Vec::new()),
|
|
||||||
segments: None,
|
|
||||||
},
|
|
||||||
|
|
||||||
Patterns::List(patterns) => {
|
|
||||||
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: None,
|
|
||||||
patterns: Patterns::List(patterns),
|
|
||||||
pat_type: PatternType::DynamicSet(pattern_re_set, pattern_data),
|
|
||||||
segments: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new resource definition using a pattern that performs prefix matching.
|
/// Constructs a new resource definition using a pattern that performs prefix matching.
|
||||||
|
@ -348,9 +305,9 @@ impl ResourceDef {
|
||||||
/// assert!(!resource.is_match("user/123/stars"));
|
/// assert!(!resource.is_match("user/123/stars"));
|
||||||
/// assert!(!resource.is_match("/foo"));
|
/// assert!(!resource.is_match("/foo"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn prefix(path: &str) -> Self {
|
pub fn prefix<T: IntoPatterns>(paths: T) -> Self {
|
||||||
profile_method!(prefix);
|
profile_method!(prefix);
|
||||||
ResourceDef::from_single_pattern(path, true)
|
ResourceDef::new2(paths, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new resource definition using a string pattern that performs prefix matching,
|
/// Constructs a new resource definition using a string pattern that performs prefix matching,
|
||||||
|
@ -375,7 +332,7 @@ impl ResourceDef {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn root_prefix(path: &str) -> Self {
|
pub fn root_prefix(path: &str) -> Self {
|
||||||
profile_method!(root_prefix);
|
profile_method!(root_prefix);
|
||||||
ResourceDef::prefix(&insert_slash(path))
|
ResourceDef::prefix(insert_slash(path).into_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a numeric resource ID.
|
/// Returns a numeric resource ID.
|
||||||
|
@ -453,17 +410,14 @@ impl ResourceDef {
|
||||||
/// assert!(!ResourceDef::new("/user").is_prefix());
|
/// assert!(!ResourceDef::new("/user").is_prefix());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_prefix(&self) -> bool {
|
pub fn is_prefix(&self) -> bool {
|
||||||
match &self.pat_type {
|
self.is_prefix
|
||||||
PatternType::Prefix(_) => true,
|
|
||||||
PatternType::Dynamic(re, _) if !re.as_str().ends_with('$') => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pattern string that generated the resource definition.
|
/// Returns the pattern string that generated the resource definition.
|
||||||
///
|
///
|
||||||
/// Returns `None` if definition was constructed with multiple patterns.
|
/// If definition is constructed with multiple patterns, the first pattern is returned. To get
|
||||||
/// See [`patterns_iter`][Self::pattern_iter].
|
/// all patterns, use [`patterns_iter`][Self::pattern_iter]. If resource has 0 patterns,
|
||||||
|
/// returns `None`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -472,11 +426,11 @@ impl ResourceDef {
|
||||||
/// assert_eq!(resource.pattern().unwrap(), "/user/{id}");
|
/// assert_eq!(resource.pattern().unwrap(), "/user/{id}");
|
||||||
///
|
///
|
||||||
/// let mut resource = ResourceDef::new(["/profile", "/user/{id}"]);
|
/// let mut resource = ResourceDef::new(["/profile", "/user/{id}"]);
|
||||||
/// assert!(resource.pattern().is_none());
|
/// assert_eq!(resource.pattern(), Some("/profile"));
|
||||||
pub fn pattern(&self) -> Option<&str> {
|
pub fn pattern(&self) -> Option<&str> {
|
||||||
match &self.patterns {
|
match &self.patterns {
|
||||||
Patterns::Single(pattern) => Some(pattern.as_str()),
|
Patterns::Single(pattern) => Some(pattern.as_str()),
|
||||||
Patterns::List(_) => None,
|
Patterns::List(patterns) => patterns.first().map(AsRef::as_ref),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,8 +517,8 @@ impl ResourceDef {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
match patterns.len() {
|
match patterns.len() {
|
||||||
1 => ResourceDef::from_single_pattern(&patterns[0], other.is_prefix()),
|
1 => ResourceDef::new2(&patterns[0], other.is_prefix()),
|
||||||
_ => ResourceDef::new(patterns),
|
_ => ResourceDef::new2(patterns, other.is_prefix()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,11 +563,10 @@ impl ResourceDef {
|
||||||
// `self.find_match(path).is_some()`
|
// `self.find_match(path).is_some()`
|
||||||
// but this skips some checks and uses potentially faster regex methods
|
// but this skips some checks and uses potentially faster regex methods
|
||||||
|
|
||||||
match self.pat_type {
|
match &self.pat_type {
|
||||||
PatternType::Static(ref s) => s == path,
|
PatternType::Static(pattern) => self.static_match(pattern, path).is_some(),
|
||||||
PatternType::Prefix(ref prefix) => is_prefix(prefix, path),
|
PatternType::Dynamic(re, _) => re.is_match(path),
|
||||||
PatternType::Dynamic(ref re, _) => re.is_match(path),
|
PatternType::DynamicSet(re, _) => re.is_match(path),
|
||||||
PatternType::DynamicSet(ref re, _) => re.is_match(path),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,11 +609,7 @@ impl ResourceDef {
|
||||||
profile_method!(find_match);
|
profile_method!(find_match);
|
||||||
|
|
||||||
match &self.pat_type {
|
match &self.pat_type {
|
||||||
PatternType::Static(segment) if path == segment => Some(segment.len()),
|
PatternType::Static(pattern) => self.static_match(pattern, path),
|
||||||
PatternType::Static(_) => None,
|
|
||||||
|
|
||||||
PatternType::Prefix(prefix) if is_prefix(prefix, path) => Some(prefix.len()),
|
|
||||||
PatternType::Prefix(_) => None,
|
|
||||||
|
|
||||||
PatternType::Dynamic(re, _) => Some(re.captures(path)?[1].len()),
|
PatternType::Dynamic(re, _) => Some(re.captures(path)?[1].len()),
|
||||||
|
|
||||||
|
@ -753,10 +702,10 @@ impl ResourceDef {
|
||||||
let path_str = path.path();
|
let path_str = path.path();
|
||||||
|
|
||||||
let (matched_len, matched_vars) = match &self.pat_type {
|
let (matched_len, matched_vars) = match &self.pat_type {
|
||||||
PatternType::Static(_) | PatternType::Prefix(_) => {
|
PatternType::Static(pattern) => {
|
||||||
profile_section!(pattern_static_or_prefix);
|
profile_section!(pattern_static_or_prefix);
|
||||||
|
|
||||||
match self.find_match(path_str) {
|
match self.static_match(pattern, path_str) {
|
||||||
Some(len) => (len, None),
|
Some(len) => (len, None),
|
||||||
None => return false,
|
None => return false,
|
||||||
}
|
}
|
||||||
|
@ -844,13 +793,10 @@ impl ResourceDef {
|
||||||
F: FnMut(&str) -> Option<I>,
|
F: FnMut(&str) -> Option<I>,
|
||||||
I: AsRef<str>,
|
I: AsRef<str>,
|
||||||
{
|
{
|
||||||
for el in match self.segments {
|
for segment in &self.segments {
|
||||||
Some(ref segments) => segments,
|
match segment {
|
||||||
None => return false,
|
PatternSegment::Const(val) => path.push_str(val),
|
||||||
} {
|
PatternSegment::Var(name) => match vars(name) {
|
||||||
match *el {
|
|
||||||
PatternSegment::Const(ref val) => path.push_str(val),
|
|
||||||
PatternSegment::Var(ref name) => match vars(name) {
|
|
||||||
Some(val) => path.push_str(val.as_ref()),
|
Some(val) => path.push_str(val.as_ref()),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
},
|
},
|
||||||
|
@ -864,8 +810,8 @@ impl ResourceDef {
|
||||||
///
|
///
|
||||||
/// Returns `true` on success.
|
/// Returns `true` on success.
|
||||||
///
|
///
|
||||||
/// Resource paths can not be built from multi-pattern resources; this call will always return
|
/// For multi-pattern resources, the first pattern is used under the assumption that it would be
|
||||||
/// false and will not add anything to the string buffer.
|
/// equivalent to any other choice.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -890,8 +836,8 @@ impl ResourceDef {
|
||||||
///
|
///
|
||||||
/// Returns `true` on success.
|
/// Returns `true` on success.
|
||||||
///
|
///
|
||||||
/// Resource paths can not be built from multi-pattern resources; this call will always return
|
/// For multi-pattern resources, the first pattern is used under the assumption that it would be
|
||||||
/// false and will not add anything to the string buffer.
|
/// equivalent to any other choice.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -921,19 +867,69 @@ impl ResourceDef {
|
||||||
self.build_resource_path(path, |name| values.get(name).map(AsRef::<str>::as_ref))
|
self.build_resource_path(path, |name| values.get(name).map(AsRef::<str>::as_ref))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse path pattern and create a new instance.
|
/// Returns true if `prefix` acts as a proper prefix (i.e., separated by a slash) in `path`.
|
||||||
fn from_single_pattern(pattern: &str, is_prefix: bool) -> Self {
|
fn static_match(&self, pattern: &str, path: &str) -> Option<usize> {
|
||||||
profile_method!(from_single_pattern);
|
let rem = path.strip_prefix(pattern)?;
|
||||||
|
|
||||||
let pattern = pattern.to_owned();
|
match self.is_prefix {
|
||||||
let (pat_type, segments) = ResourceDef::parse(&pattern, is_prefix, false);
|
// resource is not a prefix so an exact match is needed
|
||||||
|
false if rem.is_empty() => Some(pattern.len()),
|
||||||
|
|
||||||
|
// resource is a prefix so rem should start with a path delimiter
|
||||||
|
true if rem.is_empty() || rem.starts_with('/') => Some(pattern.len()),
|
||||||
|
|
||||||
|
// otherwise, no match
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new2<T: IntoPatterns>(paths: T, is_prefix: bool) -> Self {
|
||||||
|
profile_method!(new2);
|
||||||
|
|
||||||
|
let patterns = paths.patterns();
|
||||||
|
let (pat_type, segments) = match &patterns {
|
||||||
|
Patterns::Single(pattern) => ResourceDef::parse(pattern, is_prefix, false),
|
||||||
|
|
||||||
|
// since zero length pattern sets are possible
|
||||||
|
// just return a useless `ResourceDef`
|
||||||
|
Patterns::List(patterns) if patterns.is_empty() => (
|
||||||
|
PatternType::DynamicSet(RegexSet::empty(), Vec::new()),
|
||||||
|
Vec::new(),
|
||||||
|
),
|
||||||
|
|
||||||
|
Patterns::List(patterns) => {
|
||||||
|
let mut re_set = Vec::with_capacity(patterns.len());
|
||||||
|
let mut pattern_data = Vec::new();
|
||||||
|
let mut segments = None;
|
||||||
|
|
||||||
|
for pattern in patterns {
|
||||||
|
match ResourceDef::parse(pattern, is_prefix, true) {
|
||||||
|
(PatternType::Dynamic(re, names), segs) => {
|
||||||
|
re_set.push(re.as_str().to_owned());
|
||||||
|
pattern_data.push((re, names));
|
||||||
|
segments.get_or_insert(segs);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pattern_re_set = RegexSet::new(re_set).unwrap();
|
||||||
|
let segments = segments.unwrap_or_else(Vec::new);
|
||||||
|
|
||||||
|
(
|
||||||
|
PatternType::DynamicSet(pattern_re_set, pattern_data),
|
||||||
|
segments,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ResourceDef {
|
ResourceDef {
|
||||||
id: 0,
|
id: 0,
|
||||||
name: None,
|
name: None,
|
||||||
patterns: Patterns::Single(pattern),
|
patterns,
|
||||||
|
is_prefix,
|
||||||
pat_type,
|
pat_type,
|
||||||
segments: Some(segments),
|
segments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,20 +1019,15 @@ impl ResourceDef {
|
||||||
) -> (PatternType, Vec<PatternSegment>) {
|
) -> (PatternType, Vec<PatternSegment>) {
|
||||||
profile_method!(parse);
|
profile_method!(parse);
|
||||||
|
|
||||||
let mut unprocessed = pattern;
|
if !force_dynamic && pattern.find('{').is_none() && !pattern.ends_with('*') {
|
||||||
|
|
||||||
if !force_dynamic && unprocessed.find('{').is_none() && !unprocessed.ends_with('*') {
|
|
||||||
// pattern is static
|
// pattern is static
|
||||||
|
return (
|
||||||
let tp = if is_prefix {
|
PatternType::Static(pattern.to_owned()),
|
||||||
PatternType::Prefix(unprocessed.to_owned())
|
vec![PatternSegment::Const(pattern.to_owned())],
|
||||||
} else {
|
);
|
||||||
PatternType::Static(unprocessed.to_owned())
|
|
||||||
};
|
|
||||||
|
|
||||||
return (tp, vec![PatternSegment::Const(unprocessed.to_owned())]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut unprocessed = pattern;
|
||||||
let mut segments = Vec::new();
|
let mut segments = Vec::new();
|
||||||
let mut re = format!("{}^", REGEX_FLAGS);
|
let mut re = format!("{}^", REGEX_FLAGS);
|
||||||
let mut dyn_segment_count = 0;
|
let mut dyn_segment_count = 0;
|
||||||
|
@ -1137,18 +1128,7 @@ impl Eq for ResourceDef {}
|
||||||
|
|
||||||
impl PartialEq for ResourceDef {
|
impl PartialEq for ResourceDef {
|
||||||
fn eq(&self, other: &ResourceDef) -> bool {
|
fn eq(&self, other: &ResourceDef) -> bool {
|
||||||
self.patterns == other.patterns
|
self.patterns == other.patterns && self.is_prefix == other.is_prefix
|
||||||
&& match &self.pat_type {
|
|
||||||
PatternType::Static(_) => matches!(&other.pat_type, PatternType::Static(_)),
|
|
||||||
PatternType::Prefix(_) => matches!(&other.pat_type, PatternType::Prefix(_)),
|
|
||||||
PatternType::Dynamic(re, _) => match &other.pat_type {
|
|
||||||
PatternType::Dynamic(other_re, _) => re.as_str() == other_re.as_str(),
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
PatternType::DynamicSet(_, _) => {
|
|
||||||
matches!(&other.pat_type, PatternType::DynamicSet(..))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1183,15 +1163,6 @@ pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if `prefix` acts as a proper prefix (i.e., separated by a slash) in `path`.
|
|
||||||
fn is_prefix(prefix: &str, path: &str) -> bool {
|
|
||||||
match path.strip_prefix(prefix) {
|
|
||||||
// Ensure the match ends at segment boundary
|
|
||||||
Some(rem) if rem.is_empty() || rem.starts_with('/') => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1376,6 +1347,24 @@ mod tests {
|
||||||
assert!(!re.is_match("/user/2345/sdg"));
|
assert!(!re.is_match("/user/2345/sdg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dynamic_set_prefix() {
|
||||||
|
let re = ResourceDef::prefix(vec!["/u/{id}", "/{id:[[:digit:]]{3}}"]);
|
||||||
|
|
||||||
|
assert_eq!(re.find_match("/u/abc"), Some(6));
|
||||||
|
assert_eq!(re.find_match("/u/abc/123"), Some(6));
|
||||||
|
assert_eq!(re.find_match("/s/user/profile"), None);
|
||||||
|
|
||||||
|
assert_eq!(re.find_match("/123"), Some(4));
|
||||||
|
assert_eq!(re.find_match("/123/456"), Some(4));
|
||||||
|
assert_eq!(re.find_match("/12345"), None);
|
||||||
|
|
||||||
|
let mut path = Path::new("/151/res");
|
||||||
|
assert!(re.capture_match_info(&mut path));
|
||||||
|
assert_eq!(path.get("id").unwrap(), "151");
|
||||||
|
assert_eq!(path.unprocessed(), "/res");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_tail() {
|
fn parse_tail() {
|
||||||
let re = ResourceDef::new("/user/-{id}*");
|
let re = ResourceDef::new("/user/-{id}*");
|
||||||
|
@ -1602,10 +1591,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_pattern_cannot_build_path() {
|
fn multi_pattern_build_path() {
|
||||||
let resource = ResourceDef::new(["/user/{id}", "/profile/{id}"]);
|
let resource = ResourceDef::new(["/user/{id}", "/profile/{id}"]);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
assert!(!resource.resource_path_from_iter(&mut s, &mut ["123"].iter()));
|
assert!(resource.resource_path_from_iter(&mut s, &mut ["123"].iter()));
|
||||||
|
assert_eq!(s, "/user/123");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1738,8 +1728,12 @@ mod tests {
|
||||||
|
|
||||||
join_test!("", "" => "", "/hello", "/");
|
join_test!("", "" => "", "/hello", "/");
|
||||||
join_test!("/user", "" => "", "/user", "/user/123", "/user11", "user", "user/123");
|
join_test!("/user", "" => "", "/user", "/user/123", "/user11", "user", "user/123");
|
||||||
join_test!("", "/user"=> "", "/user", "foo", "/user11", "user", "user/123");
|
join_test!("", "/user" => "", "/user", "foo", "/user11", "user", "user/123");
|
||||||
join_test!("/user", "/xx"=> "", "", "/", "/user", "/xx", "/userxx", "/user/xx");
|
join_test!("/user", "/xx" => "", "", "/", "/user", "/xx", "/userxx", "/user/xx");
|
||||||
|
|
||||||
|
join_test!(["/ver/{v}", "/v{v}"], ["/req/{req}", "/{req}"] => "/v1/abc",
|
||||||
|
"/ver/1/abc", "/v1/req/abc", "/ver/1/req/abc", "/v1/abc/def",
|
||||||
|
"/ver1/req/abc/def", "", "/", "/v1/");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1777,6 +1771,7 @@ mod tests {
|
||||||
match_methods_agree!(prefix "" => "", "/", "/foo");
|
match_methods_agree!(prefix "" => "", "/", "/foo");
|
||||||
match_methods_agree!(prefix "/user" => "user", "/user", "/users", "/user/123", "/foo");
|
match_methods_agree!(prefix "/user" => "user", "/user", "/users", "/user/123", "/foo");
|
||||||
match_methods_agree!(prefix r"/id/{id:\d{3}}" => "/id/123", "/id/1234");
|
match_methods_agree!(prefix r"/id/{id:\d{3}}" => "/id/123", "/id/1234");
|
||||||
|
match_methods_agree!(["/v{v}", "/ver/{v}"] => "", "s/v", "/v1", "/v1/xx", "/ver/i3/5", "/ver/1");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue