Struct actix_router::ResourceDef

source ·
pub struct ResourceDef { /* private fields */ }
Expand description

Describes the set of paths that match to a resource.

ResourceDefs 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 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

source

pub fn new<T: IntoPatterns>(paths: T) -> Self

Constructs a new resource definition from patterns.

Multi-pattern resources can be constructed by providing a slice (or vec) of patterns.

§Panics

Panics if any path patterns are 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"));
source

pub fn prefix<T: IntoPatterns>(paths: T) -> Self

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"));
source

pub fn root_prefix(path: &str) -> Self

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"));
source

pub fn id(&self) -> u16

Returns a numeric resource ID.

If not explicitly set using set_id, this will return 0.

§Examples
let mut resource = ResourceDef::new("/root");
assert_eq!(resource.id(), 0);

resource.set_id(42);
assert_eq!(resource.id(), 42);
source

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);
source

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");
source

pub fn set_name(&mut self, name: impl Into<String>)

Assigns a new name to the resource.

§Panics

Panics if name is an empty string.

§Examples
let mut resource = ResourceDef::new("/root");
resource.set_name("root");
assert_eq!(resource.name().unwrap(), "root");
source

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());
source

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"));
source

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());
source

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"));
source

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
source

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));
source

pub fn capture_match_info<R: Resource>(&self, resource: &mut R) -> bool

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(), "");
source

pub fn capture_match_info_fn<R, F>(&self, resource: &mut R, check_fn: F) -> bool
where 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");
source

pub fn resource_path_from_iter<I>(&self, path: &mut String, values: I) -> bool
where I: IntoIterator, I::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");
source

pub fn resource_path_from_map<K, V, S>( &self, path: &mut String, values: &HashMap<K, V, S> ) -> bool
where 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

source§

fn clone(&self) -> ResourceDef

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ResourceDef

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a> From<&'a str> for ResourceDef

source§

fn from(path: &'a str) -> ResourceDef

Converts to this type from the input type.
source§

impl From<String> for ResourceDef

source§

fn from(path: String) -> ResourceDef

Converts to this type from the input type.
source§

impl Hash for ResourceDef

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl PartialEq for ResourceDef

source§

fn eq(&self, other: &ResourceDef) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Eq for ResourceDef

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more