Struct actix_web::dev::ResourceDef
source · pub struct ResourceDef { /* private fields */ }
Expand description
Describes the set of paths that match to a resource.
ResourceDef
s are effectively a way to transform the a custom resource pattern syntax into
suitable regular expressions from which to check matches with paths and capture portions of a
matched path into variables. Common cases are on a fast path that avoids going through the
regex engine.
Pattern Format and Matching Behavior
Resource pattern is defined as a string of zero or more segments where each segment is
preceded by 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. For example, the pattern
"/user/"
has two segments: ["user", ""]
A key point to understand is that ResourceDef
matches segments, not strings. Segments are
matched individually. 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"]
.
This definition is consistent with the definition of absolute URL path in RFC 3986 §3.3
Static Resources
A static resource is the most basic type of definition. Pass a pattern to new. Conforming paths must match the pattern exactly.
Examples
let resource = ResourceDef::new("/home");
assert!(resource.is_match("/home"));
assert!(!resource.is_match("/home/"));
assert!(!resource.is_match("/home/new"));
assert!(!resource.is_match("/homes"));
assert!(!resource.is_match("/search"));
Dynamic Segments
Also known as “path parameters”. Resources can define sections of a pattern that be extracted from a conforming path, if it conforms to (one of) the resource pattern(s).
The marker for a dynamic segment is curly braces wrapping an identifier. For example,
/user/{id}
would match paths like /user/123
or /user/james
and be able to extract the user
IDs “123” and “james”, respectively.
However, this resource pattern (/user/{id}
) would, not cover /user/123/stars
(unless
constructed as a prefix; see next section) since the default pattern for segments matches all
characters until it finds a /
character (or the end of the path). Custom segment patterns are
covered further down.
Dynamic segments do not need to be delimited by /
characters, they can be defined within a
path segment. For example, /rust-is-{opinion}
can match the paths /rust-is-cool
and
/rust-is-hard
.
For information on capturing segment values from paths or other custom resource types,
see capture_match_info
and capture_match_info_fn
.
A resource definition can contain at most 16 dynamic segments.
Examples
use actix_router::{Path, ResourceDef};
let resource = ResourceDef::prefix("/user/{id}");
assert!(resource.is_match("/user/123"));
assert!(!resource.is_match("/user"));
assert!(!resource.is_match("/user/"));
let mut path = Path::new("/user/123");
resource.capture_match_info(&mut path);
assert_eq!(path.get("id").unwrap(), "123");
Prefix Resources
A prefix resource is defined as pattern that can match just the start of a path, up to a segment boundary.
Prefix patterns with a trailing slash may have an unexpected, though correct, behavior. They define and therefore require an empty segment in order to match. It is easier to understand this behavior after reading the matching behavior section. Examples are given below.
The empty pattern (""
), as a prefix, matches any path.
Prefix resources can contain dynamic segments.
Examples
let resource = ResourceDef::prefix("/home");
assert!(resource.is_match("/home"));
assert!(resource.is_match("/home/new"));
assert!(!resource.is_match("/homes"));
// prefix pattern with a trailing slash
let resource = ResourceDef::prefix("/user/{id}/");
assert!(resource.is_match("/user/123/"));
assert!(resource.is_match("/user/123//stars"));
assert!(!resource.is_match("/user/123/stars"));
assert!(!resource.is_match("/user/123"));
Custom Regex Segments
Dynamic segments can be customised to only match a specific regular expression. It can be helpful to do this if resource definitions would otherwise conflict and cause one to be inaccessible.
The regex used when capturing segment values can be specified explicitly using this syntax:
{name:regex}
. For example, /user/{id:\d+}
will only match paths where the user ID
is numeric.
The regex could potentially match multiple segments. If this is not wanted, then care must be
taken to avoid matching a slash /
. It is guaranteed, however, that the match ends at a
segment boundary; the pattern r"(/|$)
is always appended to the regex.
By default, dynamic segments use this regex: [^/]+
. This shows why it is the case, as shown in
the earlier section, that segments capture a slice of the path up to the next /
character.
Custom regex segments can be used in static and prefix resource definition variants.
Examples
let resource = ResourceDef::new(r"/user/{id:\d+}");
assert!(resource.is_match("/user/123"));
assert!(resource.is_match("/user/314159"));
assert!(!resource.is_match("/user/abc"));
Tail Segments
As a shortcut to defining a custom regex for matching all remaining characters (not just those
up until a /
character), there is a special pattern to match (and capture) the remaining
path portion.
To do this, use the segment pattern: {name}*
. Since a tail segment also has a name, values are
extracted in the same way as non-tail dynamic segments.
Examples
let resource = ResourceDef::new("/blob/{tail}*");
assert!(resource.is_match("/blob/HEAD/Cargo.toml"));
assert!(resource.is_match("/blob/HEAD/README.md"));
let mut path = Path::new("/blob/main/LICENSE");
resource.capture_match_info(&mut path);
assert_eq!(path.get("tail").unwrap(), "main/LICENSE");
Multi-Pattern Resources
For resources that can map to multiple distinct paths, it may be suitable to use
multi-pattern resources by passing an array/vec to new
. They will be combined
into a regex set which is usually quicker to check matches on than checking each
pattern individually.
Multi-pattern resources can contain dynamic segments just like single pattern ones. However, take care to use consistent and semantically-equivalent segment names; it could affect expectations in the router using these definitions and cause runtime panics.
Examples
let resource = ResourceDef::new(["/home", "/index"]);
assert!(resource.is_match("/home"));
assert!(resource.is_match("/index"));
Trailing Slashes
It should be noted that this library takes no steps to normalize intra-path or trailing slashes. As such, all resource definitions implicitly expect a pre-processing step to normalize paths if they you wish to accommodate “recoverable” path errors. Below are several examples of resource-path pairs that would not be compatible.
Examples
assert!(!ResourceDef::new("/root").is_match("/root/"));
assert!(!ResourceDef::new("/root/").is_match("/root"));
assert!(!ResourceDef::prefix("/root/").is_match("/root"));
Implementations§
source§impl ResourceDef
impl ResourceDef
sourcepub fn new<T>(paths: T) -> ResourceDefwhere
T: IntoPatterns,
pub fn new<T>(paths: T) -> ResourceDefwhere T: IntoPatterns,
Constructs a new resource definition from patterns.
Multi-pattern resources can be constructed by providing a slice (or vec) of patterns.
Panics
Panics if path pattern is malformed.
Examples
use actix_router::ResourceDef;
let resource = ResourceDef::new("/user/{id}");
assert!(resource.is_match("/user/123"));
assert!(!resource.is_match("/user/123/stars"));
assert!(!resource.is_match("user/1234"));
assert!(!resource.is_match("/foo"));
let resource = ResourceDef::new(["/profile", "/user/{id}"]);
assert!(resource.is_match("/profile"));
assert!(resource.is_match("/user/123"));
assert!(!resource.is_match("user/123"));
assert!(!resource.is_match("/foo"));
sourcepub fn prefix<T>(paths: T) -> ResourceDefwhere
T: IntoPatterns,
pub fn prefix<T>(paths: T) -> ResourceDefwhere T: IntoPatterns,
Constructs a new resource definition using a pattern that performs prefix matching.
More specifically, the regular expressions generated for matching are different when using
this method vs using new
; they will not be appended with the $
meta-character that
matches the end of an input.
Although it will compile and run correctly, it is meaningless to construct a prefix
resource definition with a tail segment; use new
in this case.
Panics
Panics if path pattern is malformed.
Examples
use actix_router::ResourceDef;
let resource = ResourceDef::prefix("/user/{id}");
assert!(resource.is_match("/user/123"));
assert!(resource.is_match("/user/123/stars"));
assert!(!resource.is_match("user/123"));
assert!(!resource.is_match("user/123/stars"));
assert!(!resource.is_match("/foo"));
sourcepub fn root_prefix(path: &str) -> ResourceDef
pub fn root_prefix(path: &str) -> ResourceDef
Constructs a new resource definition using a string pattern that performs prefix matching,
ensuring a leading /
if pattern is not empty.
Panics
Panics if path pattern is malformed.
Examples
use actix_router::ResourceDef;
let resource = ResourceDef::root_prefix("user/{id}");
assert_eq!(&resource, &ResourceDef::prefix("/user/{id}"));
assert_eq!(&resource, &ResourceDef::root_prefix("/user/{id}"));
assert_ne!(&resource, &ResourceDef::new("user/{id}"));
assert_ne!(&resource, &ResourceDef::new("/user/{id}"));
assert!(resource.is_match("/user/123"));
assert!(!resource.is_match("user/123"));
sourcepub fn set_id(&mut self, id: u16)
pub fn set_id(&mut self, id: u16)
Set numeric resource ID.
Examples
let mut resource = ResourceDef::new("/root");
resource.set_id(42);
assert_eq!(resource.id(), 42);
sourcepub fn name(&self) -> Option<&str>
pub fn name(&self) -> Option<&str>
Returns resource definition name, if set.
Examples
let mut resource = ResourceDef::new("/root");
assert!(resource.name().is_none());
resource.set_name("root");
assert_eq!(resource.name().unwrap(), "root");
sourcepub fn is_prefix(&self) -> bool
pub fn is_prefix(&self) -> bool
Returns true
if pattern type is prefix.
Examples
assert!(ResourceDef::prefix("/user").is_prefix());
assert!(!ResourceDef::new("/user").is_prefix());
sourcepub fn pattern(&self) -> Option<&str>
pub fn pattern(&self) -> Option<&str>
Returns the pattern string that generated the resource definition.
If definition is constructed with multiple patterns, the first pattern is returned. To get
all patterns, use patterns_iter
. If resource has 0 patterns,
returns None
.
Examples
let mut resource = ResourceDef::new("/user/{id}");
assert_eq!(resource.pattern().unwrap(), "/user/{id}");
let mut resource = ResourceDef::new(["/profile", "/user/{id}"]);
assert_eq!(resource.pattern(), Some("/profile"));
sourcepub fn pattern_iter(&self) -> impl Iterator<Item = &str>
pub fn pattern_iter(&self) -> impl Iterator<Item = &str>
Returns iterator of pattern strings that generated the resource definition.
Examples
let mut resource = ResourceDef::new("/root");
let mut iter = resource.pattern_iter();
assert_eq!(iter.next().unwrap(), "/root");
assert!(iter.next().is_none());
let mut resource = ResourceDef::new(["/root", "/backup"]);
let mut iter = resource.pattern_iter();
assert_eq!(iter.next().unwrap(), "/root");
assert_eq!(iter.next().unwrap(), "/backup");
assert!(iter.next().is_none());
sourcepub fn join(&self, other: &ResourceDef) -> ResourceDef
pub fn join(&self, other: &ResourceDef) -> ResourceDef
Joins two resources.
Resulting resource is prefix if other
is prefix.
Examples
let joined = ResourceDef::prefix("/root").join(&ResourceDef::prefix("/seg"));
assert_eq!(joined, ResourceDef::prefix("/root/seg"));
sourcepub fn is_match(&self, path: &str) -> bool
pub fn is_match(&self, path: &str) -> bool
Returns true
if path
matches this resource.
The behavior of this method depends on how the ResourceDef
was constructed. For example,
static resources will not be able to match as many paths as dynamic and prefix resources.
See ResourceDef
struct docs for details on resource definition types.
This method will always agree with find_match
on whether the path
matches or not.
Examples
use actix_router::ResourceDef;
// static resource
let resource = ResourceDef::new("/user");
assert!(resource.is_match("/user"));
assert!(!resource.is_match("/users"));
assert!(!resource.is_match("/user/123"));
assert!(!resource.is_match("/foo"));
// dynamic resource
let resource = ResourceDef::new("/user/{user_id}");
assert!(resource.is_match("/user/123"));
assert!(!resource.is_match("/user/123/stars"));
// prefix resource
let resource = ResourceDef::prefix("/root");
assert!(resource.is_match("/root"));
assert!(resource.is_match("/root/leaf"));
assert!(!resource.is_match("/roots"));
// more examples are shown in the `ResourceDef` struct docs
sourcepub fn find_match(&self, path: &str) -> Option<usize>
pub fn find_match(&self, path: &str) -> Option<usize>
Tries to match path
to this resource, returning the position in the path where the
match ends.
This method will always agree with is_match
on whether the path matches
or not.
Examples
use actix_router::ResourceDef;
// static resource
let resource = ResourceDef::new("/user");
assert_eq!(resource.find_match("/user"), Some(5));
assert!(resource.find_match("/user/").is_none());
assert!(resource.find_match("/user/123").is_none());
assert!(resource.find_match("/foo").is_none());
// constant prefix resource
let resource = ResourceDef::prefix("/user");
assert_eq!(resource.find_match("/user"), Some(5));
assert_eq!(resource.find_match("/user/"), Some(5));
assert_eq!(resource.find_match("/user/123"), Some(5));
// dynamic prefix resource
let resource = ResourceDef::prefix("/user/{id}");
assert_eq!(resource.find_match("/user/123"), Some(9));
assert_eq!(resource.find_match("/user/1234/"), Some(10));
assert_eq!(resource.find_match("/user/12345/stars"), Some(11));
assert!(resource.find_match("/user/").is_none());
// multi-pattern resource
let resource = ResourceDef::new(["/user/{id}", "/profile/{id}"]);
assert_eq!(resource.find_match("/user/123"), Some(9));
assert_eq!(resource.find_match("/profile/1234"), Some(13));
sourcepub fn capture_match_info<R>(&self, resource: &mut R) -> boolwhere
R: Resource,
pub fn capture_match_info<R>(&self, resource: &mut R) -> boolwhere R: Resource,
Collects dynamic segment values into resource
.
Returns true
if path
matches this resource.
Examples
use actix_router::{Path, ResourceDef};
let resource = ResourceDef::prefix("/user/{id}");
let mut path = Path::new("/user/123/stars");
assert!(resource.capture_match_info(&mut path));
assert_eq!(path.get("id").unwrap(), "123");
assert_eq!(path.unprocessed(), "/stars");
let resource = ResourceDef::new("/blob/{path}*");
let mut path = Path::new("/blob/HEAD/Cargo.toml");
assert!(resource.capture_match_info(&mut path));
assert_eq!(path.get("path").unwrap(), "HEAD/Cargo.toml");
assert_eq!(path.unprocessed(), "");
sourcepub fn capture_match_info_fn<R, F>(&self, resource: &mut R, check_fn: F) -> boolwhere
R: Resource,
F: FnOnce(&R) -> bool,
pub fn capture_match_info_fn<R, F>(&self, resource: &mut R, check_fn: F) -> boolwhere R: Resource, F: FnOnce(&R) -> bool,
Collects dynamic segment values into resource
after matching paths and executing
check function.
The check function is given a reference to the passed resource and optional arbitrary data. This is useful if you want to conditionally match on some non-path related aspect of the resource type.
Returns true
if resource path matches this resource definition and satisfies the
given check function.
Examples
use actix_router::{Path, ResourceDef};
fn try_match(resource: &ResourceDef, path: &mut Path<&str>) -> bool {
let admin_allowed = std::env::var("ADMIN_ALLOWED").is_ok();
resource.capture_match_info_fn(
path,
// when env var is not set, reject when path contains "admin"
|path| !(!admin_allowed && path.as_str().contains("admin")),
)
}
let resource = ResourceDef::prefix("/user/{id}");
// path matches; segment values are collected into path
let mut path = Path::new("/user/james/stars");
assert!(try_match(&resource, &mut path));
assert_eq!(path.get("id").unwrap(), "james");
assert_eq!(path.unprocessed(), "/stars");
// path matches but fails check function; no segments are collected
let mut path = Path::new("/user/admin/stars");
assert!(!try_match(&resource, &mut path));
assert_eq!(path.unprocessed(), "/user/admin/stars");
sourcepub fn resource_path_from_iter<I>(&self, path: &mut String, values: I) -> boolwhere
I: IntoIterator,
<I as IntoIterator>::Item: AsRef<str>,
pub fn resource_path_from_iter<I>(&self, path: &mut String, values: I) -> boolwhere I: IntoIterator, <I as IntoIterator>::Item: AsRef<str>,
Assembles full resource path from iterator of dynamic segment values.
Returns true
on success.
For multi-pattern resources, the first pattern is used under the assumption that it would be equivalent to any other choice.
Examples
let mut s = String::new();
let resource = ResourceDef::new("/user/{id}/post/{title}");
assert!(resource.resource_path_from_iter(&mut s, &["123", "my-post"]));
assert_eq!(s, "/user/123/post/my-post");
sourcepub fn resource_path_from_map<K, V, S>(
&self,
path: &mut String,
values: &HashMap<K, V, S>
) -> boolwhere
K: Borrow<str> + Eq + Hash,
V: AsRef<str>,
S: BuildHasher,
pub fn resource_path_from_map<K, V, S>( &self, path: &mut String, values: &HashMap<K, V, S> ) -> boolwhere K: Borrow<str> + Eq + Hash, V: AsRef<str>, S: BuildHasher,
Assembles resource path from map of dynamic segment values.
Returns true
on success.
For multi-pattern resources, the first pattern is used under the assumption that it would be equivalent to any other choice.
Examples
let mut s = String::new();
let resource = ResourceDef::new("/user/{id}/post/{title}");
let mut map = HashMap::new();
map.insert("id", "123");
map.insert("title", "my-post");
assert!(resource.resource_path_from_map(&mut s, &map));
assert_eq!(s, "/user/123/post/my-post");
Trait Implementations§
source§impl Clone for ResourceDef
impl Clone for ResourceDef
source§fn clone(&self) -> ResourceDef
fn clone(&self) -> ResourceDef
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for ResourceDef
impl Debug for ResourceDef
source§impl<'a> From<&'a str> for ResourceDef
impl<'a> From<&'a str> for ResourceDef
source§fn from(path: &'a str) -> ResourceDef
fn from(path: &'a str) -> ResourceDef
source§impl From<String> for ResourceDef
impl From<String> for ResourceDef
source§fn from(path: String) -> ResourceDef
fn from(path: String) -> ResourceDef
source§impl Hash for ResourceDef
impl Hash for ResourceDef
source§impl PartialEq<ResourceDef> for ResourceDef
impl PartialEq<ResourceDef> for ResourceDef
source§fn eq(&self, other: &ResourceDef) -> bool
fn eq(&self, other: &ResourceDef) -> bool
self
and other
values to be equal, and is used
by ==
.impl Eq for ResourceDef
Auto Trait Implementations§
impl RefUnwindSafe for ResourceDef
impl Send for ResourceDef
impl Sync for ResourceDef
impl Unpin for ResourceDef
impl UnwindSafe for ResourceDef
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> CallHasher for Twhere
T: Hash + ?Sized,
impl<T> CallHasher for Twhere T: Hash + ?Sized,
source§impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
impl<Q, K> Equivalent<K> for Qwhere Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key
and return true
if they are equal.