mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into remove-del-cookie
This commit is contained in:
commit
8cdef80bb5
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Provides a non-blocking service for serving static files from disk.
|
//! Provides a non-blocking service for serving static files from disk.
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Examples
|
||||||
//! ```
|
//! ```
|
||||||
//! use actix_web::App;
|
//! use actix_web::App;
|
||||||
//! use actix_files::Files;
|
//! use actix_files::Files;
|
||||||
|
|
|
@ -39,13 +39,18 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! # use actix_web::HttpResponse;
|
//! # use actix_web::HttpResponse;
|
||||||
//! # use actix_web_codegen::route;
|
//! # use actix_web_codegen::route;
|
||||||
//! #[route("/test", method="GET", method="HEAD")]
|
//! #[route("/test", method = "GET", method = "HEAD")]
|
||||||
//! async fn get_and_head_handler() -> HttpResponse {
|
//! async fn get_and_head_handler() -> HttpResponse {
|
||||||
//! HttpResponse::Ok().finish()
|
//! HttpResponse::Ok().finish()
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [actix-web attributes docs]: https://docs.rs/actix-web/*/actix_web/#attributes
|
//! # Multiple Path Handlers
|
||||||
|
//! There are no macros to generate multi-path handlers. Let us know in [this issue].
|
||||||
|
//!
|
||||||
|
//! [this issue]: https://github.com/actix/actix-web/issues/1709
|
||||||
|
//!
|
||||||
|
//! [actix-web attributes docs]: https://docs.rs/actix-web/latest/actix_web/#attributes
|
||||||
//! [GET]: macro@get
|
//! [GET]: macro@get
|
||||||
//! [POST]: macro@post
|
//! [POST]: macro@post
|
||||||
//! [PUT]: macro@put
|
//! [PUT]: macro@put
|
||||||
|
@ -73,22 +78,23 @@ mod route;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Attributes
|
/// # Attributes
|
||||||
/// - `"path"` - Raw literal string with path for which to register handler.
|
/// - `"path"`: Raw literal string with path for which to register handler.
|
||||||
/// - `name="resource_name"` - Specifies resource name for the handler. If not set, the function name of handler is used.
|
/// - `name = "resource_name"`: Specifies resource name for the handler. If not set, the function
|
||||||
/// - `method="HTTP_METHOD"` - Registers HTTP method to provide guard for. Upper-case string, "GET", "POST" for example.
|
/// name of handler is used.
|
||||||
/// - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard`
|
/// - `method = "HTTP_METHOD"`: Registers HTTP method to provide guard for. Upper-case string,
|
||||||
/// - `wrap="Middleware"` - Registers a resource middleware.
|
/// "GET", "POST" for example.
|
||||||
|
/// - `guard = "function_name"`: Registers function as guard using `actix_web::guard::fn_guard`.
|
||||||
|
/// - `wrap = "Middleware"`: Registers a resource middleware.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Notes
|
||||||
/// Function name can be specified as any expression that is going to be accessible to the generate
|
/// Function name can be specified as any expression that is going to be accessible to the generate
|
||||||
/// code, e.g `my_guard` or `my_module::my_guard`.
|
/// code, e.g `my_guard` or `my_module::my_guard`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::HttpResponse;
|
/// # use actix_web::HttpResponse;
|
||||||
/// # use actix_web_codegen::route;
|
/// # use actix_web_codegen::route;
|
||||||
/// #[route("/test", method="GET", method="HEAD")]
|
/// #[route("/test", method = "GET", method = "HEAD")]
|
||||||
/// async fn example() -> HttpResponse {
|
/// async fn example() -> HttpResponse {
|
||||||
/// HttpResponse::Ok().finish()
|
/// HttpResponse::Ok().finish()
|
||||||
/// }
|
/// }
|
||||||
|
@ -98,69 +104,54 @@ pub fn route(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
route::with_method(None, args, input)
|
route::with_method(None, args, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! doc_comment {
|
|
||||||
($x:expr; $($tt:tt)*) => {
|
|
||||||
#[doc = $x]
|
|
||||||
$($tt)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! method_macro {
|
macro_rules! method_macro {
|
||||||
(
|
($variant:ident, $method:ident) => {
|
||||||
$($variant:ident, $method:ident,)+
|
#[doc = concat!("Creates route handler with `actix_web::guard::", stringify!($variant), "`.")]
|
||||||
) => {
|
///
|
||||||
$(doc_comment! {
|
/// # Syntax
|
||||||
concat!("
|
/// ```plain
|
||||||
Creates route handler with `actix_web::guard::", stringify!($variant), "`.
|
#[doc = concat!("#[", stringify!($method), r#"("path"[, attributes])]"#)]
|
||||||
|
/// ```
|
||||||
# Syntax
|
///
|
||||||
```plain
|
/// # Attributes
|
||||||
#[", stringify!($method), r#"("path"[, attributes])]
|
/// - `"path"`: Raw literal string with path for which to register handler.
|
||||||
```
|
/// - `name = "resource_name"`: Specifies resource name for the handler. If not set, the function
|
||||||
|
/// name of handler is used.
|
||||||
# Attributes
|
/// - `guard = "function_name"`: Registers function as guard using `actix_web::guard::fn_guard`.
|
||||||
- `"path"` - Raw literal string with path for which to register handler.
|
/// - `wrap = "Middleware"`: Registers a resource middleware.
|
||||||
- `name="resource_name"` - Specifies resource name for the handler. If not set, the function name of handler is used.
|
///
|
||||||
- `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard`.
|
/// # Notes
|
||||||
- `wrap="Middleware"` - Registers a resource middleware.
|
/// Function name can be specified as any expression that is going to be accessible to the
|
||||||
|
/// generate code, e.g `my_guard` or `my_module::my_guard`.
|
||||||
# Notes
|
///
|
||||||
Function name can be specified as any expression that is going to be accessible to the generate
|
/// # Examples
|
||||||
code, e.g `my_guard` or `my_module::my_guard`.
|
/// ```
|
||||||
|
/// # use actix_web::HttpResponse;
|
||||||
# Example
|
#[doc = concat!("# use actix_web_codegen::", stringify!($method), ";")]
|
||||||
|
#[doc = concat!("#[", stringify!($method), r#"("/")]"#)]
|
||||||
```
|
/// async fn example() -> HttpResponse {
|
||||||
# use actix_web::HttpResponse;
|
/// HttpResponse::Ok().finish()
|
||||||
# use actix_web_codegen::"#, stringify!($method), ";
|
/// }
|
||||||
#[", stringify!($method), r#"("/")]
|
/// ```
|
||||||
async fn example() -> HttpResponse {
|
#[proc_macro_attribute]
|
||||||
HttpResponse::Ok().finish()
|
pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
route::with_method(Some(route::MethodType::$variant), args, input)
|
||||||
}
|
}
|
||||||
```
|
|
||||||
"#);
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
||||||
route::with_method(Some(route::MethodType::$variant), args, input)
|
|
||||||
}
|
|
||||||
})+
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
method_macro! {
|
method_macro!(Get, get);
|
||||||
Get, get,
|
method_macro!(Post, post);
|
||||||
Post, post,
|
method_macro!(Put, put);
|
||||||
Put, put,
|
method_macro!(Delete, delete);
|
||||||
Delete, delete,
|
method_macro!(Head, head);
|
||||||
Head, head,
|
method_macro!(Connect, connect);
|
||||||
Connect, connect,
|
method_macro!(Options, options);
|
||||||
Options, options,
|
method_macro!(Trace, trace);
|
||||||
Trace, trace,
|
method_macro!(Patch, patch);
|
||||||
Patch, patch,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marks async main function as the actix system entry-point.
|
|
||||||
|
|
||||||
|
/// Marks async main function as the Actix Web system entry-point.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// #[actix_web::main]
|
/// #[actix_web::main]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Type definitions required to use [`awc::Client`](super::Client) as a WebSocket client.
|
//! Type definitions required to use [`awc::Client`](super::Client) as a WebSocket client.
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use awc::{Client, ws};
|
//! use awc::{Client, ws};
|
||||||
|
|
|
@ -24,7 +24,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2")))
|
.wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2")))
|
||||||
.wrap(middleware::Compress::default())
|
.wrap(middleware::Compress::default())
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default().log_target("1234"))
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(no_params)
|
.service(no_params)
|
||||||
.service(
|
.service(
|
||||||
|
|
|
@ -16,7 +16,7 @@ crate::http::header::common_header! {
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `Tue, 15 Nov 1994 08:12:31 GMT`
|
/// * `Tue, 15 Nov 1994 08:12:31 GMT`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::SystemTime;
|
/// use std::time::SystemTime;
|
||||||
|
|
|
@ -19,7 +19,7 @@ crate::http::header::common_header! {
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `Thu, 01 Dec 1994 16:00:00 GMT`
|
/// * `Thu, 01 Dec 1994 16:00:00 GMT`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::{SystemTime, Duration};
|
/// use std::time::{SystemTime, Duration};
|
||||||
|
|
|
@ -18,7 +18,7 @@ crate::http::header::common_header! {
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::{SystemTime, Duration};
|
/// use std::time::{SystemTime, Duration};
|
||||||
|
|
|
@ -18,7 +18,7 @@ crate::http::header::common_header! {
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::{SystemTime, Duration};
|
/// use std::time::{SystemTime, Duration};
|
||||||
|
|
|
@ -17,7 +17,7 @@ crate::http::header::common_header! {
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
/// * `Sat, 29 Oct 1994 19:43:31 GMT`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::{SystemTime, Duration};
|
/// use std::time::{SystemTime, Duration};
|
||||||
|
|
|
@ -124,7 +124,7 @@ impl Logger {
|
||||||
///
|
///
|
||||||
/// It is convention to print "-" to indicate no output instead of an empty string.
|
/// It is convention to print "-" to indicate no output instead of an empty string.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::http::{header::HeaderValue};
|
/// # use actix_web::http::{header::HeaderValue};
|
||||||
/// # use actix_web::middleware::Logger;
|
/// # use actix_web::middleware::Logger;
|
||||||
|
@ -700,7 +700,6 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let s = format!("{}", FormatDisplay(&render));
|
let s = format!("{}", FormatDisplay(&render));
|
||||||
println!("{}", s);
|
|
||||||
assert!(s.contains("/test/route/yeah"));
|
assert!(s.contains("/test/route/yeah"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +793,6 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let s = format!("{}", FormatDisplay(&render));
|
let s = format!("{}", FormatDisplay(&render));
|
||||||
println!("{}", s);
|
|
||||||
assert!(s.contains("192.0.2.60"));
|
assert!(s.contains("192.0.2.60"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -508,10 +508,12 @@ mod tests {
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dev::{ResourceDef, ResourceMap};
|
use crate::{
|
||||||
use crate::http::{header, StatusCode};
|
dev::{ResourceDef, ResourceMap},
|
||||||
use crate::test::{call_service, init_service, read_body, TestRequest};
|
http::{header, StatusCode},
|
||||||
use crate::{web, App, HttpResponse};
|
test::{self, call_service, init_service, read_body, TestRequest},
|
||||||
|
web, App, HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_debug() {
|
fn test_debug() {
|
||||||
|
@ -865,4 +867,47 @@ mod tests {
|
||||||
let res = call_service(&srv, req).await;
|
let res = call_service(&srv, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn url_for_closest_named_resource() {
|
||||||
|
// we mount the route named 'nested' on 2 different scopes, 'a' and 'b'
|
||||||
|
let srv = test::init_service(
|
||||||
|
App::new()
|
||||||
|
.service(
|
||||||
|
web::scope("/foo")
|
||||||
|
.service(web::resource("/nested").name("nested").route(web::get().to(
|
||||||
|
|req: HttpRequest| {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.body(format!("{}", req.url_for_static("nested").unwrap()))
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
.service(web::scope("/baz").service(web::resource("deep")))
|
||||||
|
.service(web::resource("{foo_param}")),
|
||||||
|
)
|
||||||
|
.service(web::scope("/bar").service(
|
||||||
|
web::resource("/nested").name("nested").route(web::get().to(
|
||||||
|
|req: HttpRequest| {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.body(format!("{}", req.url_for_static("nested").unwrap()))
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let foo_resp =
|
||||||
|
test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
|
||||||
|
assert_eq!(foo_resp.status(), StatusCode::OK);
|
||||||
|
let body = read_body(foo_resp).await;
|
||||||
|
// `body` equals http://localhost:8080/bar/nested
|
||||||
|
// because nested from /bar overrides /foo's
|
||||||
|
// to do this any other way would require something like a custom tree search
|
||||||
|
assert_eq!(body, "http://localhost:8080/bar/nested");
|
||||||
|
|
||||||
|
let bar_resp =
|
||||||
|
test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
|
||||||
|
assert_eq!(bar_resp.status(), StatusCode::OK);
|
||||||
|
let body = read_body(bar_resp).await;
|
||||||
|
assert_eq!(body, "http://localhost:8080/bar/nested");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::{
|
||||||
/// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not
|
/// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not
|
||||||
/// provided to make this potential foot-gun more obvious.
|
/// provided to make this potential foot-gun more obvious.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _};
|
/// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _};
|
||||||
///
|
///
|
||||||
|
|
83
src/rmap.rs
83
src/rmap.rs
|
@ -1,6 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
fmt::Write as _,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,12 +11,14 @@ use url::Url;
|
||||||
|
|
||||||
use crate::{error::UrlGenerationError, request::HttpRequest};
|
use crate::{error::UrlGenerationError, request::HttpRequest};
|
||||||
|
|
||||||
|
const AVG_PATH_LEN: usize = 24;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ResourceMap {
|
pub struct ResourceMap {
|
||||||
pattern: ResourceDef,
|
pattern: ResourceDef,
|
||||||
|
|
||||||
/// Named resources within the tree or, for external resources,
|
/// Named resources within the tree or, for external resources, it points to isolated nodes
|
||||||
/// it points to isolated nodes outside the tree.
|
/// outside the tree.
|
||||||
named: AHashMap<String, Rc<ResourceMap>>,
|
named: AHashMap<String, Rc<ResourceMap>>,
|
||||||
|
|
||||||
parent: RefCell<Weak<ResourceMap>>,
|
parent: RefCell<Weak<ResourceMap>>,
|
||||||
|
@ -35,6 +38,35 @@ impl ResourceMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format resource map as tree structure (unfinished).
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn tree(&self) -> String {
|
||||||
|
let mut buf = String::new();
|
||||||
|
self._tree(&mut buf, 0);
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn _tree(&self, buf: &mut String, level: usize) {
|
||||||
|
if let Some(children) = &self.nodes {
|
||||||
|
for child in children {
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"{}{} {}",
|
||||||
|
"--".repeat(level),
|
||||||
|
child.pattern.pattern().unwrap(),
|
||||||
|
child
|
||||||
|
.pattern
|
||||||
|
.name()
|
||||||
|
.map(|name| format!("({})", name))
|
||||||
|
.unwrap_or_else(|| "".to_owned())
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ResourceMap::_tree(child, buf, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a (possibly nested) resource.
|
/// Adds a (possibly nested) resource.
|
||||||
///
|
///
|
||||||
/// To add a non-prefix pattern, `nested` must be `None`.
|
/// To add a non-prefix pattern, `nested` must be `None`.
|
||||||
|
@ -44,7 +76,11 @@ impl ResourceMap {
|
||||||
pattern.set_id(self.nodes.as_ref().unwrap().len() as u16);
|
pattern.set_id(self.nodes.as_ref().unwrap().len() as u16);
|
||||||
|
|
||||||
if let Some(new_node) = nested {
|
if let Some(new_node) = nested {
|
||||||
assert_eq!(&new_node.pattern, pattern, "`patern` and `nested` mismatch");
|
debug_assert_eq!(
|
||||||
|
&new_node.pattern, pattern,
|
||||||
|
"`pattern` and `nested` mismatch"
|
||||||
|
);
|
||||||
|
// parents absorb references to the named resources of children
|
||||||
self.named.extend(new_node.named.clone().into_iter());
|
self.named.extend(new_node.named.clone().into_iter());
|
||||||
self.nodes.as_mut().unwrap().push(new_node);
|
self.nodes.as_mut().unwrap().push(new_node);
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,7 +100,7 @@ impl ResourceMap {
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't add external resources to the tree
|
// don't add external resources to the tree
|
||||||
if !is_external {
|
if !is_external {
|
||||||
self.nodes.as_mut().unwrap().push(new_node);
|
self.nodes.as_mut().unwrap().push(new_node);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +114,7 @@ impl ResourceMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate url for named resource
|
/// Generate URL for named resource.
|
||||||
///
|
///
|
||||||
/// Check [`HttpRequest::url_for`] for detailed information.
|
/// Check [`HttpRequest::url_for`] for detailed information.
|
||||||
pub fn url_for<U, I>(
|
pub fn url_for<U, I>(
|
||||||
|
@ -97,7 +133,7 @@ impl ResourceMap {
|
||||||
.named
|
.named
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or(UrlGenerationError::ResourceNotFound)?
|
.ok_or(UrlGenerationError::ResourceNotFound)?
|
||||||
.root_rmap_fn(String::with_capacity(24), |mut acc, node| {
|
.root_rmap_fn(String::with_capacity(AVG_PATH_LEN), |mut acc, node| {
|
||||||
node.pattern
|
node.pattern
|
||||||
.resource_path_from_iter(&mut acc, &mut elements)
|
.resource_path_from_iter(&mut acc, &mut elements)
|
||||||
.then(|| acc)
|
.then(|| acc)
|
||||||
|
@ -128,6 +164,7 @@ impl ResourceMap {
|
||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if there is a resource that would match `path`.
|
||||||
pub fn has_resource(&self, path: &str) -> bool {
|
pub fn has_resource(&self, path: &str) -> bool {
|
||||||
self.find_matching_node(path).is_some()
|
self.find_matching_node(path).is_some()
|
||||||
}
|
}
|
||||||
|
@ -142,9 +179,10 @@ impl ResourceMap {
|
||||||
/// is possible.
|
/// is possible.
|
||||||
pub fn match_pattern(&self, path: &str) -> Option<String> {
|
pub fn match_pattern(&self, path: &str) -> Option<String> {
|
||||||
self.find_matching_node(path)?.root_rmap_fn(
|
self.find_matching_node(path)?.root_rmap_fn(
|
||||||
String::with_capacity(24),
|
String::with_capacity(AVG_PATH_LEN),
|
||||||
|mut acc, node| {
|
|mut acc, node| {
|
||||||
acc.push_str(node.pattern.pattern()?);
|
let pattern = node.pattern.pattern()?;
|
||||||
|
acc.push_str(pattern);
|
||||||
Some(acc)
|
Some(acc)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -490,4 +528,33 @@ mod tests {
|
||||||
"https://duck.com/abcd"
|
"https://duck.com/abcd"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_for_override_within_map() {
|
||||||
|
let mut root = ResourceMap::new(ResourceDef::prefix(""));
|
||||||
|
|
||||||
|
let mut foo_rdef = ResourceDef::prefix("/foo");
|
||||||
|
let mut foo_map = ResourceMap::new(foo_rdef.clone());
|
||||||
|
let mut nested_rdef = ResourceDef::new("/nested");
|
||||||
|
nested_rdef.set_name("nested");
|
||||||
|
foo_map.add(&mut nested_rdef, None);
|
||||||
|
root.add(&mut foo_rdef, Some(Rc::new(foo_map)));
|
||||||
|
|
||||||
|
let mut foo_rdef = ResourceDef::prefix("/bar");
|
||||||
|
let mut foo_map = ResourceMap::new(foo_rdef.clone());
|
||||||
|
let mut nested_rdef = ResourceDef::new("/nested");
|
||||||
|
nested_rdef.set_name("nested");
|
||||||
|
foo_map.add(&mut nested_rdef, None);
|
||||||
|
root.add(&mut foo_rdef, Some(Rc::new(foo_map)));
|
||||||
|
|
||||||
|
let rmap = Rc::new(root);
|
||||||
|
ResourceMap::finish(&rmap);
|
||||||
|
|
||||||
|
let req = crate::test::TestRequest::default().to_http_request();
|
||||||
|
|
||||||
|
let url = rmap.url_for(&req, "nested", &[""; 0]).unwrap().to_string();
|
||||||
|
assert_eq!(url, "http://localhost:8080/bar/nested");
|
||||||
|
|
||||||
|
assert!(rmap.url_for(&req, "missing", &["u123"]).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -831,6 +831,7 @@ mod tests {
|
||||||
let req = ServiceRequest::from_parts(req, pl);
|
let req = ServiceRequest::from_parts(req, pl);
|
||||||
svc.call(req)
|
svc.call(req)
|
||||||
})
|
})
|
||||||
|
.route("/", web::get().to(|| async { "" }))
|
||||||
.service(
|
.service(
|
||||||
web::resource("/resource1/{name}/index.html").route(web::get().to(index)),
|
web::resource("/resource1/{name}/index.html").route(web::get().to(index)),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue