1
0
mirror of https://github.com/fafhrd91/actix-net synced 2024-11-27 21:22:57 +01:00

add zero cost profiling to router

This commit is contained in:
Rob Ede 2021-07-17 01:07:07 +01:00
parent 5687e81d9f
commit 95cba659ff
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
5 changed files with 262 additions and 20 deletions

View File

@ -21,15 +21,17 @@ default = ["http"]
[dependencies] [dependencies]
bytestring = ">=0.1.5, <2" bytestring = ">=0.1.5, <2"
firestorm = "0.4"
http = { version = "0.2.3", optional = true } http = { version = "0.2.3", optional = true }
log = "0.4" log = "0.4"
regex = "1.5" regex = "1.5"
serde = "1" serde = "1"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }
firestorm = { version = "0.4", features = ["enable_system_time"] }
http = "0.2.3" http = "0.2.3"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
criterion = { version = "0.3", features = ["html_reports"] }
[[bench]] [[bench]]
name = "router" name = "router"

View File

@ -0,0 +1,172 @@
macro_rules! register {
(brackets) => {{
register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
}};
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
let arr = [
concat!("/authorizations"),
concat!("/authorizations/", $p1),
concat!("/applications/", $p1, "/tokens/", $p2),
concat!("/events"),
concat!("/repos/", $p1, "/", $p2, "/events"),
concat!("/networks/", $p1, "/", $p2, "/events"),
concat!("/orgs/", $p1, "/events"),
concat!("/users/", $p1, "/received_events"),
concat!("/users/", $p1, "/received_events/public"),
concat!("/users/", $p1, "/events"),
concat!("/users/", $p1, "/events/public"),
concat!("/users/", $p1, "/events/orgs/", $p2),
concat!("/feeds"),
concat!("/notifications"),
concat!("/repos/", $p1, "/", $p2, "/notifications"),
concat!("/notifications/threads/", $p1),
concat!("/notifications/threads/", $p1, "/subscription"),
concat!("/repos/", $p1, "/", $p2, "/stargazers"),
concat!("/users/", $p1, "/starred"),
concat!("/user/starred"),
concat!("/user/starred/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/subscribers"),
concat!("/users/", $p1, "/subscriptions"),
concat!("/user/subscriptions"),
concat!("/repos/", $p1, "/", $p2, "/subscription"),
concat!("/user/subscriptions/", $p1, "/", $p2),
concat!("/users/", $p1, "/gists"),
concat!("/gists"),
concat!("/gists/", $p1),
concat!("/gists/", $p1, "/star"),
concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/refs"),
concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
concat!("/issues"),
concat!("/user/issues"),
concat!("/orgs/", $p1, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
concat!("/repos/", $p1, "/", $p2, "/assignees"),
concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
concat!("/repos/", $p1, "/", $p2, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
concat!("/repos/", $p1, "/", $p2, "/milestones/"),
concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
concat!("/emojis"),
concat!("/gitignore/templates"),
concat!("/gitignore/templates/", $p1),
concat!("/meta"),
concat!("/rate_limit"),
concat!("/users/", $p1, "/orgs"),
concat!("/user/orgs"),
concat!("/orgs/", $p1),
concat!("/orgs/", $p1, "/members"),
concat!("/orgs/", $p1, "/members", $p2),
concat!("/orgs/", $p1, "/public_members"),
concat!("/orgs/", $p1, "/public_members/", $p2),
concat!("/orgs/", $p1, "/teams"),
concat!("/teams/", $p1),
concat!("/teams/", $p1, "/members"),
concat!("/teams/", $p1, "/members", $p2),
concat!("/teams/", $p1, "/repos"),
concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
concat!("/user/teams"),
concat!("/repos/", $p1, "/", $p2, "/pulls"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
concat!("/user/repos"),
concat!("/users/", $p1, "/repos"),
concat!("/orgs/", $p1, "/repos"),
concat!("/repositories"),
concat!("/repos/", $p1, "/", $p2),
concat!("/repos/", $p1, "/", $p2, "/contributors"),
concat!("/repos/", $p1, "/", $p2, "/languages"),
concat!("/repos/", $p1, "/", $p2, "/teams"),
concat!("/repos/", $p1, "/", $p2, "/tags"),
concat!("/repos/", $p1, "/", $p2, "/branches"),
concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
concat!("/repos/", $p1, "/", $p2, "/collaborators"),
concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
concat!("/repos/", $p1, "/", $p2, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
concat!("/repos/", $p1, "/", $p2, "/commits"),
concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
concat!("/repos/", $p1, "/", $p2, "/readme"),
concat!("/repos/", $p1, "/", $p2, "/keys"),
concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
concat!("/repos/", $p1, "/", $p2, "/downloads"),
concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
concat!("/repos/", $p1, "/", $p2, "/forks"),
concat!("/repos/", $p1, "/", $p2, "/hooks"),
concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases"),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
concat!("/search/repositories"),
concat!("/search/code"),
concat!("/search/issues"),
concat!("/search/users"),
concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
concat!("/legacy/repos/search/", $p1),
concat!("/legacy/user/search/", $p1),
concat!("/legacy/user/email/", $p1),
concat!("/users/", $p1),
concat!("/user"),
concat!("/users"),
concat!("/user/emails"),
concat!("/users/", $p1, "/followers"),
concat!("/user/followers"),
concat!("/users/", $p1, "/following"),
concat!("/user/following"),
concat!("/user/following/", $p1),
concat!("/users/", $p1, "/following", $p2),
concat!("/users/", $p1, "/keys"),
concat!("/user/keys"),
concat!("/user/keys/", $p1),
];
std::array::IntoIter::new(arr)
}};
}
fn call() -> impl Iterator<Item = &'static str> {
let arr = [
"/authorizations",
"/user/repos",
"/repos/rust-lang/rust/stargazers",
"/orgs/rust-lang/public_members/nikomatsakis",
"/repos/rust-lang/rust/releases/1.51.0",
];
std::array::IntoIter::new(arr)
}
fn main() {
let mut router = actix_router::Router::<bool>::build();
for route in register!(brackets) {
router.path(route, true);
}
let actix = router.finish();
if firestorm::enabled() {
firestorm::bench("target", || {
for route in call() {
let mut path = actix_router::Path::new(route);
actix.recognize(&mut path).unwrap();
}
})
.unwrap();
}
}

View File

@ -1,6 +1,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::Index; use std::ops::Index;
use firestorm::profile_method;
use serde::de; use serde::de;
use crate::de::PathDeserializer; use crate::de::PathDeserializer;
@ -37,21 +38,23 @@ impl<T: ResourcePath> Path<T> {
} }
} }
/// Get reference to inner path instance /// Get reference to inner path instance.
#[inline] #[inline]
pub fn get_ref(&self) -> &T { pub fn get_ref(&self) -> &T {
&self.path &self.path
} }
/// Get mutable reference to inner path instance /// Get mutable reference to inner path instance.
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut T { pub fn get_mut(&mut self) -> &mut T {
&mut self.path &mut self.path
} }
/// Path /// Path.
#[inline] #[inline]
pub fn path(&self) -> &str { pub fn path(&self) -> &str {
profile_method!(path);
let skip = self.skip as usize; let skip = self.skip as usize;
let path = self.path.path(); let path = self.path.path();
if skip <= path.len() { if skip <= path.len() {
@ -61,7 +64,7 @@ impl<T: ResourcePath> Path<T> {
} }
} }
/// Set new path /// Set new path.
#[inline] #[inline]
pub fn set(&mut self, path: T) { pub fn set(&mut self, path: T) {
self.skip = 0; self.skip = 0;
@ -69,20 +72,22 @@ impl<T: ResourcePath> Path<T> {
self.segments.clear(); self.segments.clear();
} }
/// Reset state /// Reset state.
#[inline] #[inline]
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.skip = 0; self.skip = 0;
self.segments.clear(); self.segments.clear();
} }
/// Skip first `n` chars in path /// Skip first `n` chars in path.
#[inline] #[inline]
pub fn skip(&mut self, n: u16) { pub fn skip(&mut self, n: u16) {
self.skip += n; self.skip += n;
} }
pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) { pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) {
profile_method!(add);
match value { match value {
PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))), PathItem::Static(s) => self.segments.push((name.into(), PathItem::Static(s))),
PathItem::Segment(begin, end) => self.segments.push(( PathItem::Segment(begin, end) => self.segments.push((
@ -116,6 +121,8 @@ impl<T: ResourcePath> Path<T> {
/// Get matched parameter by name without type conversion /// Get matched parameter by name without type conversion
pub fn get(&self, key: &str) -> Option<&str> { pub fn get(&self, key: &str) -> Option<&str> {
profile_method!(get);
for item in self.segments.iter() { for item in self.segments.iter() {
if key == item.0 { if key == item.0 {
return match item.1 { return match item.1 {
@ -140,9 +147,10 @@ impl<T: ResourcePath> Path<T> {
/// Get matched parameter by name. /// Get matched parameter by name.
/// ///
/// If keyed parameter is not available empty string is used as default /// If keyed parameter is not available empty string is used as default value.
/// value.
pub fn query(&self, key: &str) -> &str { pub fn query(&self, key: &str) -> &str {
profile_method!(query);
if let Some(s) = self.get(key) { if let Some(s) = self.get(key) {
s s
} else { } else {
@ -150,7 +158,7 @@ impl<T: ResourcePath> Path<T> {
} }
} }
/// Return iterator to items in parameter container /// Return iterator to items in parameter container.
pub fn iter(&self) -> PathIter<'_, T> { pub fn iter(&self) -> PathIter<'_, T> {
PathIter { PathIter {
idx: 0, idx: 0,
@ -160,6 +168,7 @@ impl<T: ResourcePath> Path<T> {
/// Try to deserialize matching parameters to a specified type `U` /// Try to deserialize matching parameters to a specified type `U`
pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> { pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
profile_method!(load);
de::Deserialize::deserialize(PathDeserializer::new(self)) de::Deserialize::deserialize(PathDeserializer::new(self))
} }
} }

View File

@ -6,6 +6,7 @@ use std::{
mem, mem,
}; };
use firestorm::{profile_fn, profile_method, profile_section};
use regex::{escape, Regex, RegexSet}; use regex::{escape, Regex, RegexSet};
use crate::{ use crate::{
@ -78,6 +79,8 @@ impl ResourceDef {
/// ///
/// Panics if path pattern is malformed. /// Panics if path pattern is malformed.
pub fn new<T: IntoPatterns>(path: T) -> Self { pub fn new<T: IntoPatterns>(path: T) -> Self {
profile_method!(new);
match path.patterns() { match path.patterns() {
Patterns::Single(pattern) => ResourceDef::from_single_pattern(&pattern, false), Patterns::Single(pattern) => ResourceDef::from_single_pattern(&pattern, false),
@ -124,6 +127,7 @@ impl ResourceDef {
/// ///
/// Panics if path regex pattern is malformed. /// Panics if path regex pattern is malformed.
pub fn prefix(path: &str) -> Self { pub fn prefix(path: &str) -> Self {
profile_method!(prefix);
ResourceDef::from_single_pattern(path, true) ResourceDef::from_single_pattern(path, true)
} }
@ -134,6 +138,7 @@ impl ResourceDef {
/// ///
/// Panics if path regex pattern is malformed. /// Panics if path regex pattern is malformed.
pub fn root_prefix(path: &str) -> Self { pub fn root_prefix(path: &str) -> Self {
profile_method!(root_prefix);
ResourceDef::from_single_pattern(&insert_slash(path), true) ResourceDef::from_single_pattern(&insert_slash(path), true)
} }
@ -149,6 +154,8 @@ impl ResourceDef {
/// Parse path pattern and create a new instance /// Parse path pattern and create a new instance
fn from_single_pattern(pattern: &str, for_prefix: bool) -> Self { fn from_single_pattern(pattern: &str, for_prefix: bool) -> Self {
profile_method!(from_single_pattern);
let pattern = pattern.to_owned(); let pattern = pattern.to_owned();
let (pat_type, elements) = ResourceDef::parse(&pattern, for_prefix, false); let (pat_type, elements) = ResourceDef::parse(&pattern, for_prefix, false);
@ -179,6 +186,8 @@ impl ResourceDef {
/// Check if path matches this pattern. /// Check if path matches this pattern.
#[inline] #[inline]
pub fn is_match(&self, path: &str) -> bool { pub fn is_match(&self, path: &str) -> bool {
profile_method!(is_match);
match self.pat_type { match self.pat_type {
PatternType::Static(ref s) => s == path, PatternType::Static(ref s) => s == path,
PatternType::Prefix(ref s) => path.starts_with(s), PatternType::Prefix(ref s) => path.starts_with(s),
@ -189,6 +198,8 @@ impl ResourceDef {
/// Is prefix path a match against this resource. /// Is prefix path a match against this resource.
pub fn is_prefix_match(&self, path: &str) -> Option<usize> { pub fn is_prefix_match(&self, path: &str) -> Option<usize> {
profile_method!(is_prefix_match);
let path_len = path.len(); let path_len = path.len();
let path = if path.is_empty() { "/" } else { path }; let path = if path.is_empty() { "/" } else { path };
@ -245,6 +256,7 @@ impl ResourceDef {
/// Is the given path and parameters a match against this pattern. /// Is the given path and parameters a match against this pattern.
pub fn match_path<T: ResourcePath>(&self, path: &mut Path<T>) -> bool { pub fn match_path<T: ResourcePath>(&self, path: &mut Path<T>) -> bool {
profile_method!(match_path);
self.match_path_checked(path, &|_, _| true, &Some(())) self.match_path_checked(path, &|_, _| true, &Some(()))
} }
@ -260,11 +272,15 @@ impl ResourceDef {
R: Resource<T>, R: Resource<T>,
F: Fn(&R, &Option<U>) -> bool, F: Fn(&R, &Option<U>) -> bool,
{ {
profile_method!(match_path_checked);
let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] = Default::default(); let mut segments: [PathItem; MAX_DYNAMIC_SEGMENTS] = Default::default();
let path = res.resource_path(); let path = res.resource_path();
let (matched_len, matched_vars) = match self.pat_type { let (matched_len, matched_vars) = match self.pat_type {
PatternType::Static(ref segment) => { PatternType::Static(ref segment) => {
profile_section!(pattern_static);
if segment != path.path() { if segment != path.path() {
return false; return false;
} }
@ -273,6 +289,8 @@ impl ResourceDef {
} }
PatternType::Prefix(ref prefix) => { PatternType::Prefix(ref prefix) => {
profile_section!(pattern_dynamic);
let path_str = path.path(); let path_str = path.path();
let path_len = path_str.len(); let path_len = path_str.len();
@ -300,24 +318,39 @@ impl ResourceDef {
} }
PatternType::Dynamic(ref re, ref names) => { PatternType::Dynamic(ref re, ref names) => {
let captures = match re.captures(path.path()) { profile_section!(pattern_dynamic);
Some(captures) => captures,
_ => return false, let captures = {
profile_section!(pattern_dynamic_regex_exec);
match re.captures(path.path()) {
Some(captures) => captures,
_ => return false,
}
}; };
for (no, name) in names.iter().enumerate() { {
if let Some(m) = captures.name(&name) { profile_section!(pattern_dynamic_extract_captures);
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
} else { for (no, name) in names.iter().enumerate() {
log::error!("Dynamic path match but not all segments found: {}", name); if let Some(m) = captures.name(&name) {
return false; segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
} else {
log::error!(
"Dynamic path match but not all segments found: {}",
name
);
return false;
}
} }
} };
(captures[0].len(), Some(names)) (captures[0].len(), Some(names))
} }
PatternType::DynamicSet(ref re, ref params) => { PatternType::DynamicSet(ref re, ref params) => {
profile_section!(pattern_dynamic_set);
let path = path.path(); let path = path.path();
let (pattern, names) = match re.matches(path).into_iter().next() { let (pattern, names) = match re.matches(path).into_iter().next() {
Some(idx) => &params[idx], Some(idx) => &params[idx],
@ -394,6 +427,7 @@ impl ResourceDef {
U: Iterator<Item = I>, U: Iterator<Item = I>,
I: AsRef<str>, I: AsRef<str>,
{ {
profile_method!(resource_path_from_iter);
self.build_resource_path(path, |_| elements.next()) self.build_resource_path(path, |_| elements.next())
} }
@ -404,6 +438,7 @@ impl ResourceDef {
U: Iterator<Item = I>, U: Iterator<Item = I>,
I: AsRef<str>, I: AsRef<str>,
{ {
profile_method!(build_resource_path);
self.resource_path_from_iter(path, elements) self.resource_path_from_iter(path, elements)
} }
@ -420,6 +455,7 @@ impl ResourceDef {
V: AsRef<str>, V: AsRef<str>,
S: BuildHasher, S: BuildHasher,
{ {
profile_method!(resource_path_from_map);
self.build_resource_path(path, |name| { self.build_resource_path(path, |name| {
name.and_then(|name| elements.get(name).map(AsRef::<str>::as_ref)) name.and_then(|name| elements.get(name).map(AsRef::<str>::as_ref))
}) })
@ -458,6 +494,7 @@ impl ResourceDef {
S: BuildHasher, S: BuildHasher,
T: AsRef<str>, T: AsRef<str>,
{ {
profile_method!(resource_path_from_map_with_tail);
self.build_resource_path(path, |name| match name { self.build_resource_path(path, |name| match name {
Some(name) => elements.get(name).map(AsRef::<str>::as_ref), Some(name) => elements.get(name).map(AsRef::<str>::as_ref),
None => Some(tail.as_ref()), None => Some(tail.as_ref()),
@ -465,6 +502,8 @@ impl ResourceDef {
} }
fn parse_param(pattern: &str) -> (PatternElement, String, &str) { fn parse_param(pattern: &str) -> (PatternElement, String, &str) {
profile_method!(parse_param);
const DEFAULT_PATTERN: &str = "[^/]+"; const DEFAULT_PATTERN: &str = "[^/]+";
const DEFAULT_PATTERN_TAIL: &str = ".*"; const DEFAULT_PATTERN_TAIL: &str = ".*";
@ -526,6 +565,8 @@ impl ResourceDef {
for_prefix: bool, for_prefix: bool,
force_dynamic: bool, force_dynamic: bool,
) -> (PatternType, Vec<PatternElement>) { ) -> (PatternType, Vec<PatternElement>) {
profile_method!(parse);
let mut unprocessed = pattern; let mut unprocessed = pattern;
if !force_dynamic && unprocessed.find('{').is_none() && !unprocessed.ends_with('*') { if !force_dynamic && unprocessed.find('{').is_none() && !unprocessed.ends_with('*') {
@ -637,6 +678,8 @@ impl From<String> for ResourceDef {
} }
pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> { pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> {
profile_fn!(insert_slash);
if !path.is_empty() && !path.starts_with('/') { if !path.is_empty() && !path.starts_with('/') {
let mut new_path = String::with_capacity(path.len() + 1); let mut new_path = String::with_capacity(path.len() + 1);
new_path.push('/'); new_path.push('/');

View File

@ -1,3 +1,5 @@
use firestorm::profile_method;
use crate::{IntoPatterns, Resource, ResourceDef, ResourcePath}; use crate::{IntoPatterns, Resource, ResourceDef, ResourcePath};
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
@ -24,6 +26,8 @@ impl<T, U> Router<T, U> {
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath,
{ {
profile_method!(recognize);
for item in self.0.iter() { for item in self.0.iter() {
if item.0.match_path(resource.resource_path()) { if item.0.match_path(resource.resource_path()) {
return Some((&item.1, ResourceId(item.0.id()))); return Some((&item.1, ResourceId(item.0.id())));
@ -37,6 +41,8 @@ impl<T, U> Router<T, U> {
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath,
{ {
profile_method!(recognize_mut);
for item in self.0.iter_mut() { for item in self.0.iter_mut() {
if item.0.match_path(resource.resource_path()) { if item.0.match_path(resource.resource_path()) {
return Some((&mut item.1, ResourceId(item.0.id()))); return Some((&mut item.1, ResourceId(item.0.id())));
@ -55,6 +61,8 @@ impl<T, U> Router<T, U> {
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath,
{ {
profile_method!(recognize_checked);
for item in self.0.iter() { for item in self.0.iter() {
if item.0.match_path_checked(resource, &check, &item.2) { if item.0.match_path_checked(resource, &check, &item.2) {
return Some((&item.1, ResourceId(item.0.id()))); return Some((&item.1, ResourceId(item.0.id())));
@ -73,6 +81,8 @@ impl<T, U> Router<T, U> {
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath,
{ {
profile_method!(recognize_mut_checked);
for item in self.0.iter_mut() { for item in self.0.iter_mut() {
if item.0.match_path_checked(resource, &check, &item.2) { if item.0.match_path_checked(resource, &check, &item.2) {
return Some((&mut item.1, ResourceId(item.0.id()))); return Some((&mut item.1, ResourceId(item.0.id())));
@ -93,6 +103,8 @@ impl<T, U> RouterBuilder<T, U> {
path: P, path: P,
resource: T, resource: T,
) -> &mut (ResourceDef, T, Option<U>) { ) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(path);
self.resources self.resources
.push((ResourceDef::new(path), resource, None)); .push((ResourceDef::new(path), resource, None));
self.resources.last_mut().unwrap() self.resources.last_mut().unwrap()
@ -100,6 +112,8 @@ impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path prefix. /// Register resource for specified path prefix.
pub fn prefix(&mut self, prefix: &str, resource: T) -> &mut (ResourceDef, T, Option<U>) { pub fn prefix(&mut self, prefix: &str, resource: T) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(prefix);
self.resources self.resources
.push((ResourceDef::prefix(prefix), resource, None)); .push((ResourceDef::prefix(prefix), resource, None));
self.resources.last_mut().unwrap() self.resources.last_mut().unwrap()
@ -107,6 +121,8 @@ impl<T, U> RouterBuilder<T, U> {
/// Register resource for ResourceDef /// Register resource for ResourceDef
pub fn rdef(&mut self, rdef: ResourceDef, resource: T) -> &mut (ResourceDef, T, Option<U>) { pub fn rdef(&mut self, rdef: ResourceDef, resource: T) -> &mut (ResourceDef, T, Option<U>) {
profile_method!(rdef);
self.resources.push((rdef, resource, None)); self.resources.push((rdef, resource, None));
self.resources.last_mut().unwrap() self.resources.last_mut().unwrap()
} }