mirror of https://github.com/rwf2/Rocket.git
Introduce 'RouteUri'.
Co-authored-by: Ben Sully <ben@bsull.io>
This commit is contained in:
parent
487485d108
commit
2463637d51
|
@ -95,7 +95,7 @@ fn bench_simple_routing(b: &mut Bencher) {
|
|||
// Hold all of the requests we're going to make during the benchmark.
|
||||
let mut requests = vec![];
|
||||
for route in client.rocket().routes() {
|
||||
let request = client.req(route.method, route.uri.path().as_str());
|
||||
let request = client.req(route.method, route.uri.path());
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -212,30 +212,3 @@ impl Drop for Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::http::uri;
|
||||
use crate::http::ext::IntoOwned;
|
||||
|
||||
/// Error returned by [`Route::map_base()`] on invalid URIs.
|
||||
#[derive(Debug)]
|
||||
pub enum RouteUriError {
|
||||
/// The route URI is not a valid URI.
|
||||
Uri(uri::Error<'static>),
|
||||
/// The base (mount point) contains dynamic segments.
|
||||
DynamicBase,
|
||||
}
|
||||
|
||||
impl<'a> From<uri::Error<'a>> for RouteUriError {
|
||||
fn from(error: uri::Error<'a>) -> Self {
|
||||
RouteUriError::Uri(error.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RouteUriError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RouteUriError::DynamicBase => write!(f, "Mount point contains dynamic parameters."),
|
||||
RouteUriError::Uri(error) => write!(f, "Malformed URI: {}", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -792,7 +792,7 @@ impl<'r> Request<'r> {
|
|||
#[inline]
|
||||
pub fn routed_segments(&self, n: RangeFrom<usize>) -> Segments<'_> {
|
||||
let mount_segments = self.route()
|
||||
.map(|r| r.base.path_segments().len())
|
||||
.map(|r| r.uri.metadata.base_segs.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
self.uri().path_segments().skip(mount_segments + n.start)
|
||||
|
|
|
@ -222,16 +222,16 @@ impl Rocket {
|
|||
Paint::magenta(":"));
|
||||
|
||||
for route in routes.into() {
|
||||
let old_route = route.clone();
|
||||
let route = route.map_base(|old| format!("{}{}", base, old))
|
||||
let mounted_route = route.clone()
|
||||
.map_base(|old| format!("{}{}", base, old))
|
||||
.unwrap_or_else(|e| {
|
||||
error_!("Route `{}` has a malformed URI.", old_route);
|
||||
error_!("Route `{}` has a malformed URI.", route);
|
||||
error_!("{}", e);
|
||||
panic!("Invalid route URI.");
|
||||
});
|
||||
|
||||
info_!("{}", route);
|
||||
self.router.add(route);
|
||||
info_!("{}", mounted_route);
|
||||
self.router.add(mounted_route);
|
||||
}
|
||||
|
||||
self
|
||||
|
|
|
@ -46,12 +46,12 @@ impl Route {
|
|||
}
|
||||
|
||||
fn paths_collide(route: &Route, other: &Route) -> bool {
|
||||
if route.metadata.wild_path || other.metadata.wild_path {
|
||||
if route.uri.metadata.wild_path || other.uri.metadata.wild_path {
|
||||
return true;
|
||||
}
|
||||
|
||||
let a_segments = &route.metadata.path_segs;
|
||||
let b_segments = &other.metadata.path_segs;
|
||||
let a_segments = &route.uri.metadata.path_segs;
|
||||
let b_segments = &other.uri.metadata.path_segs;
|
||||
for (seg_a, seg_b) in a_segments.iter().zip(b_segments.iter()) {
|
||||
if seg_a.trailing || seg_b.trailing {
|
||||
return true;
|
||||
|
@ -70,13 +70,13 @@ fn paths_collide(route: &Route, other: &Route) -> bool {
|
|||
}
|
||||
|
||||
fn paths_match(route: &Route, req: &Request<'_>) -> bool {
|
||||
let route_segments = &route.metadata.path_segs;
|
||||
let route_segments = &route.uri.metadata.path_segs;
|
||||
let req_segments = req.routed_segments(0..);
|
||||
if route_segments.len() > req_segments.len() + 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if route.metadata.wild_path {
|
||||
if route.uri.metadata.wild_path {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,11 +95,11 @@ fn paths_match(route: &Route, req: &Request<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn queries_match(route: &Route, req: &Request<'_>) -> bool {
|
||||
if route.metadata.wild_query {
|
||||
if route.uri.metadata.wild_query {
|
||||
return true;
|
||||
}
|
||||
|
||||
let route_query_fields = route.metadata.static_query_fields.iter()
|
||||
let route_query_fields = route.uri.metadata.static_query_fields.iter()
|
||||
.map(|(k, v)| (k.as_str(), v.as_str()));
|
||||
|
||||
for route_seg in route_query_fields {
|
||||
|
@ -482,7 +482,7 @@ mod tests {
|
|||
fn req_route_path_match(a: &'static str, b: &'static str) -> bool {
|
||||
let rocket = Rocket::custom(Config::default());
|
||||
let req = Request::new(&rocket, Get, Origin::parse(a).expect("valid URI"));
|
||||
let route = Route::ranked(0, Get, b.to_string(), dummy);
|
||||
let route = Route::ranked(0, Get, b, dummy);
|
||||
route.matches(&req)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod collider;
|
||||
mod route;
|
||||
mod segment;
|
||||
mod uri;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -10,6 +11,7 @@ use crate::handler::dummy;
|
|||
|
||||
pub use self::route::Route;
|
||||
pub use self::segment::Segment;
|
||||
pub use self::uri::RouteUri;
|
||||
|
||||
// type Selector = (Method, usize);
|
||||
type Selector = Method;
|
||||
|
@ -108,7 +110,7 @@ mod test {
|
|||
fn router_with_routes(routes: &[&'static str]) -> Router {
|
||||
let mut router = Router::new();
|
||||
for route in routes {
|
||||
let route = Route::new(Get, route.to_string(), dummy);
|
||||
let route = Route::new(Get, route, dummy);
|
||||
router.add(route);
|
||||
}
|
||||
|
||||
|
@ -118,7 +120,7 @@ mod test {
|
|||
fn router_with_ranked_routes(routes: &[(isize, &'static str)]) -> Router {
|
||||
let mut router = Router::new();
|
||||
for &(rank, route) in routes {
|
||||
let route = Route::ranked(rank, Get, route.to_string(), dummy);
|
||||
let route = Route::ranked(rank, Get, route, dummy);
|
||||
router.add(route);
|
||||
}
|
||||
|
||||
|
@ -128,7 +130,7 @@ mod test {
|
|||
fn router_with_rankless_routes(routes: &[&'static str]) -> Router {
|
||||
let mut router = Router::new();
|
||||
for route in routes {
|
||||
let route = Route::ranked(0, Get, route.to_string(), dummy);
|
||||
let route = Route::ranked(0, Get, route, dummy);
|
||||
router.add(route);
|
||||
}
|
||||
|
||||
|
@ -300,9 +302,9 @@ mod test {
|
|||
assert!(route(&router, Get, "/jdlk/asdij").is_some());
|
||||
|
||||
let mut router = Router::new();
|
||||
router.add(Route::new(Put, "/hello".to_string(), dummy));
|
||||
router.add(Route::new(Post, "/hello".to_string(), dummy));
|
||||
router.add(Route::new(Delete, "/hello".to_string(), dummy));
|
||||
router.add(Route::new(Put, "/hello", dummy));
|
||||
router.add(Route::new(Post, "/hello", dummy));
|
||||
router.add(Route::new(Delete, "/hello", dummy));
|
||||
assert!(route(&router, Put, "/hello").is_some());
|
||||
assert!(route(&router, Post, "/hello").is_some());
|
||||
assert!(route(&router, Delete, "/hello").is_some());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::fmt::{self, Display};
|
||||
use std::fmt;
|
||||
use std::convert::From;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -6,12 +6,8 @@ use yansi::Paint;
|
|||
|
||||
use crate::codegen::StaticRouteInfo;
|
||||
use crate::handler::Handler;
|
||||
use crate::http::{Method, MediaType};
|
||||
use crate::error::RouteUriError;
|
||||
use crate::http::ext::IntoOwned;
|
||||
use crate::http::uri::Origin;
|
||||
use crate::router::Segment;
|
||||
use crate::form::ValueField;
|
||||
use crate::http::{uri, Method, MediaType};
|
||||
use crate::router::RouteUri;
|
||||
|
||||
/// A route: a method, its handler, path, rank, and format/media type.
|
||||
#[derive(Clone)]
|
||||
|
@ -22,48 +18,12 @@ pub struct Route {
|
|||
pub method: Method,
|
||||
/// The function that should be called when the route matches.
|
||||
pub handler: Box<dyn Handler>,
|
||||
/// The base mount point of this `Route`.
|
||||
pub base: Origin<'static>,
|
||||
/// The path of this `Route` in Rocket's route format.
|
||||
pub(crate) path: Origin<'static>,
|
||||
/// The complete URI (in Rocket's route format) that should be matched
|
||||
/// against. This is `base` + `path`.
|
||||
pub uri: Origin<'static>,
|
||||
/// The route URI.
|
||||
pub uri: RouteUri<'static>,
|
||||
/// The rank of this route. Lower ranks have higher priorities.
|
||||
pub rank: isize,
|
||||
/// The media type this route matches against, if any.
|
||||
pub format: Option<MediaType>,
|
||||
/// Cached metadata that aids in routing later.
|
||||
pub(crate) metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct Metadata {
|
||||
pub path_segs: Vec<Segment>,
|
||||
pub query_segs: Vec<Segment>,
|
||||
pub static_query_fields: Vec<(String, String)>,
|
||||
pub static_path: bool,
|
||||
pub wild_path: bool,
|
||||
pub trailing_path: bool,
|
||||
pub wild_query: bool,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default_rank(route: &Route) -> isize {
|
||||
let static_path = route.metadata.static_path;
|
||||
let wild_query = route.uri.query().map(|_| route.metadata.wild_query);
|
||||
match (static_path, wild_query) {
|
||||
(true, Some(false)) => -6, // static path, partly static query
|
||||
(true, Some(true)) => -5, // static path, fully dynamic query
|
||||
(true, None) => -4, // static path, no query
|
||||
(false, Some(false)) => -3, // dynamic path, partly static query
|
||||
(false, Some(true)) => -2, // dynamic path, fully dynamic query
|
||||
(false, None) => -1, // dynamic path, no query
|
||||
}
|
||||
}
|
||||
|
||||
fn panic<U: Display, E: Display, T>(uri: U, e: E) -> T {
|
||||
panic!("invalid URI '{}' used to construct route: {}", uri, e)
|
||||
}
|
||||
|
||||
impl Route {
|
||||
|
@ -124,12 +84,8 @@ impl Route {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if `path` is not a valid origin URI or Rocket route URI.
|
||||
pub fn new<S, H>(method: Method, path: S, handler: H) -> Route
|
||||
where S: AsRef<str>, H: Handler
|
||||
{
|
||||
let mut route = Route::ranked(0, method, path, handler);
|
||||
route.rank = default_rank(&route);
|
||||
route
|
||||
pub fn new<H: Handler>(method: Method, uri: &str, handler: H) -> Route {
|
||||
Route::ranked(None, method, uri, handler)
|
||||
}
|
||||
|
||||
/// Creates a new route with the given rank, method, path, and handler with
|
||||
|
@ -150,108 +106,27 @@ impl Route {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if `path` is not a valid origin URI or Rocket route URI.
|
||||
pub fn ranked<S, H>(rank: isize, method: Method, path: S, handler: H) -> Route
|
||||
where S: AsRef<str>, H: Handler + 'static
|
||||
pub fn ranked<H, R>(rank: R, method: Method, uri: &str, handler: H) -> Route
|
||||
where H: Handler + 'static, R: Into<Option<isize>>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
let route_path = Origin::parse_route(path)
|
||||
.unwrap_or_else(|e| panic(path, e))
|
||||
.into_normalized()
|
||||
.into_owned();
|
||||
|
||||
let mut route = Route {
|
||||
path: route_path.clone(),
|
||||
uri: route_path,
|
||||
let uri = RouteUri::new("/", uri);
|
||||
let rank = rank.into().unwrap_or_else(|| uri.default_rank());
|
||||
Route {
|
||||
name: None,
|
||||
format: None,
|
||||
base: Origin::dummy(),
|
||||
handler: Box::new(handler),
|
||||
metadata: Metadata::default(),
|
||||
method, rank,
|
||||
};
|
||||
|
||||
route.update_metadata();
|
||||
route
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
let path_segs = self.uri.raw_path_segments()
|
||||
.map(Segment::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let query_segs = self.uri.raw_query_segments()
|
||||
.map(Segment::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Metadata {
|
||||
static_path: path_segs.iter().all(|s| !s.dynamic),
|
||||
wild_path: path_segs.iter().all(|s| s.dynamic)
|
||||
&& path_segs.last().map_or(false, |p| p.trailing),
|
||||
trailing_path: path_segs.last().map_or(false, |p| p.trailing),
|
||||
wild_query: query_segs.iter().all(|s| s.dynamic),
|
||||
static_query_fields: query_segs.iter().filter(|s| !s.dynamic)
|
||||
.map(|s| ValueField::parse(&s.value))
|
||||
.map(|f| (f.name.source().to_string(), f.value.to_string()))
|
||||
.collect(),
|
||||
path_segs,
|
||||
query_segs,
|
||||
rank, uri, method,
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the cached routing metadata. MUST be called whenver the route's
|
||||
/// URI is set or changes.
|
||||
fn update_metadata(&mut self) {
|
||||
self.metadata = self.metadata();
|
||||
}
|
||||
|
||||
/// Retrieves the path of the base mount point of this route as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Route;
|
||||
/// use rocket::http::Method;
|
||||
/// # use rocket::handler::dummy as handler;
|
||||
///
|
||||
/// let mut index = Route::new(Method::Get, "/", handler);
|
||||
/// assert_eq!(index.base(), "/");
|
||||
/// assert_eq!(index.base.path(), "/");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn base(&self) -> &str {
|
||||
// This is ~ok as the route path is assumed to be percent decoded.
|
||||
self.base.path().as_str()
|
||||
}
|
||||
|
||||
/// Retrieves this route's path.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Route;
|
||||
/// use rocket::http::Method;
|
||||
/// # use rocket::handler::dummy as handler;
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.path(), "/boo/foo/bar");
|
||||
/// assert_eq!(index.uri.query().unwrap(), "a=1");
|
||||
/// assert_eq!(index.base(), "/boo");
|
||||
/// assert_eq!(index.path().path(), "/foo/bar");
|
||||
/// assert_eq!(index.path().query().unwrap(), "a=1");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn path(&self) -> &Origin<'_> {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Maps the `base` of this route using `mapper`, returning a new `Route`
|
||||
/// with the returned base.
|
||||
///
|
||||
/// `mapper` is called with the current base. The returned `String` is used
|
||||
/// as the new base if it is a valid URI. If the returned base URI contains
|
||||
/// a query, it is ignored. Returns an if the base produced by `mapper` is
|
||||
/// not a valid origin URI.
|
||||
/// a query, it is ignored. Returns an error if the base produced by
|
||||
/// `mapper` is not a valid origin URI.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -270,15 +145,11 @@ impl Route {
|
|||
/// assert_eq!(index.path().path(), "/foo/bar");
|
||||
/// assert_eq!(index.uri.path(), "/boo/foo/bar");
|
||||
/// ```
|
||||
pub fn map_base<'a, F>(mut self, mapper: F) -> Result<Self, RouteUriError>
|
||||
where F: FnOnce(Origin<'static>) -> String
|
||||
pub fn map_base<'a, F>(mut self, mapper: F) -> Result<Self, uri::Error<'static>>
|
||||
where F: FnOnce(uri::Origin<'a>) -> String
|
||||
{
|
||||
self.base = Origin::parse_owned(mapper(self.base))?.into_normalized();
|
||||
self.base.clear_query();
|
||||
|
||||
let new_uri = format!("{}{}", self.base, self.path);
|
||||
self.uri = Origin::parse_route(&new_uri)?.into_owned().into_normalized();
|
||||
self.update_metadata();
|
||||
let base = mapper(self.uri.base);
|
||||
self.uri = RouteUri::try_new(&base, &self.uri.unmounted_origin.to_string())?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -290,11 +161,11 @@ impl fmt::Display for Route {
|
|||
}
|
||||
|
||||
write!(f, "{} ", Paint::green(&self.method))?;
|
||||
if self.base.path() != "/" {
|
||||
write!(f, "{}", Paint::blue(&self.base).underline())?;
|
||||
if self.uri.base() != "/" {
|
||||
write!(f, "{}", Paint::blue(self.uri.base()).underline())?;
|
||||
}
|
||||
|
||||
write!(f, "{}", Paint::blue(&self.path))?;
|
||||
write!(f, "{}", Paint::blue(&self.uri.unmounted_origin))?;
|
||||
|
||||
if self.rank > 1 {
|
||||
write!(f, " [{}]", Paint::default(&self.rank).bold())?;
|
||||
|
@ -313,11 +184,9 @@ impl fmt::Debug for Route {
|
|||
f.debug_struct("Route")
|
||||
.field("name", &self.name)
|
||||
.field("method", &self.method)
|
||||
.field("base", &self.base)
|
||||
.field("uri", &self.uri)
|
||||
.field("rank", &self.rank)
|
||||
.field("format", &self.format)
|
||||
.field("metadata", &self.metadata)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
use std::fmt;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::http::uri::{self, Origin};
|
||||
use crate::http::ext::IntoOwned;
|
||||
use crate::router::Segment;
|
||||
use crate::form::ValueField;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RouteUri<'a> {
|
||||
/// The source string for this URI.
|
||||
source: Cow<'a, str>,
|
||||
/// The mount point of this `Route`.
|
||||
pub base: Origin<'a>,
|
||||
/// The URI _without_ the `base`.
|
||||
pub unmounted_origin: Origin<'a>,
|
||||
/// The URI _with_ the base. This is the canoncical route URI.
|
||||
pub origin: Origin<'a>,
|
||||
/// Cached metadata about this URI.
|
||||
pub(crate) metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct Metadata {
|
||||
/// Segments in the base.
|
||||
pub base_segs: Vec<Segment>,
|
||||
/// Segments in the path, including base.
|
||||
pub path_segs: Vec<Segment>,
|
||||
/// Segments in the query.
|
||||
pub query_segs: Vec<Segment>,
|
||||
/// `(name, value)` of the query segments that are static.
|
||||
pub static_query_fields: Vec<(String, String)>,
|
||||
/// Whether the path is completely static.
|
||||
pub static_path: bool,
|
||||
/// Whether the path is completely dynamic.
|
||||
pub wild_path: bool,
|
||||
/// Whether the path has a `<trailing..>` parameter.
|
||||
pub trailing_path: bool,
|
||||
/// Whether the query is completely dynamic.
|
||||
pub wild_query: bool,
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, uri::Error<'static>>;
|
||||
|
||||
impl<'a> RouteUri<'a> {
|
||||
/// Create a new `RouteUri`. Don't panic.
|
||||
pub(crate) fn try_new(base: &str, uri: &str) -> Result<RouteUri<'static>> {
|
||||
let mut base = Origin::parse(base)
|
||||
.map_err(|e| e.into_owned())?
|
||||
.into_normalized()
|
||||
.into_owned();
|
||||
|
||||
base.clear_query();
|
||||
|
||||
let unmounted_origin = Origin::parse_route(uri)
|
||||
.map_err(|e| e.into_owned())?
|
||||
.into_normalized()
|
||||
.into_owned();
|
||||
|
||||
let origin = Origin::parse_route(&format!("{}/{}", base, unmounted_origin))
|
||||
.map_err(|e| e.into_owned())?
|
||||
.into_normalized()
|
||||
.into_owned();
|
||||
|
||||
let source = origin.to_string().into();
|
||||
let metadata = Metadata::from(&base, &origin);
|
||||
|
||||
Ok(RouteUri { source, unmounted_origin, base, origin, metadata })
|
||||
}
|
||||
|
||||
/// Create a new `RouteUri`. Panic.
|
||||
pub(crate) fn new(base: &str, uri: &str) -> RouteUri<'static> {
|
||||
Self::try_new(base, uri).expect("FIXME MSG")
|
||||
}
|
||||
|
||||
/// The path of the base mount point of this route URI as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::RouteUri;
|
||||
///
|
||||
/// let index = RouteUri::new("/foo/bar?a=1");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri(), "/boo/foo/bar");
|
||||
/// assert_eq!(index.base(), "/boo");
|
||||
/// assert_eq!(index.path(), "/foo/bar");
|
||||
/// assert_eq!(index.query().unwrap(), "a=1");
|
||||
/// assert_eq!(index.as_str(), "/boo/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn base(&self) -> &str {
|
||||
self.base.path().as_str()
|
||||
}
|
||||
|
||||
/// The path part of this route URI as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::RouteUri;
|
||||
///
|
||||
/// let index = RouteUri::new("/foo/bar?a=1");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri(), "/boo/foo/bar");
|
||||
/// assert_eq!(index.base(), "/boo");
|
||||
/// assert_eq!(index.path(), "/foo/bar");
|
||||
/// assert_eq!(index.query().unwrap(), "a=1");
|
||||
/// assert_eq!(index.as_str(), "/boo/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn path(&self) -> &str {
|
||||
self.origin.path().as_str()
|
||||
}
|
||||
|
||||
/// The query part of this route URI as an `Option<&str>`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::RouteUri;
|
||||
///
|
||||
/// let index = RouteUri::new("/foo/bar?a=1");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri(), "/boo/foo/bar");
|
||||
/// assert_eq!(index.base(), "/boo");
|
||||
/// assert_eq!(index.path(), "/foo/bar");
|
||||
/// assert_eq!(index.query().unwrap(), "a=1");
|
||||
/// assert_eq!(index.as_str(), "/boo/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.origin.query().map(|q| q.as_str())
|
||||
}
|
||||
|
||||
/// The full URI as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::RouteUri;
|
||||
///
|
||||
/// let index = RouteUri::new("/foo/bar?a=1");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri(), "/boo/foo/bar");
|
||||
/// assert_eq!(index.base(), "/boo");
|
||||
/// assert_eq!(index.path(), "/foo/bar");
|
||||
/// assert_eq!(index.query().unwrap(), "a=1");
|
||||
/// assert_eq!(index.as_str(), "/boo/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_origin(&self) -> &Origin<'a> {
|
||||
&self.origin
|
||||
}
|
||||
|
||||
pub fn default_rank(&self) -> isize {
|
||||
let static_path = self.metadata.static_path;
|
||||
let wild_query = self.query().map(|_| self.metadata.wild_query);
|
||||
match (static_path, wild_query) {
|
||||
(true, Some(false)) => -6, // static path, partly static query
|
||||
(true, Some(true)) => -5, // static path, fully dynamic query
|
||||
(true, None) => -4, // static path, no query
|
||||
(false, Some(false)) => -3, // dynamic path, partly static query
|
||||
(false, Some(true)) => -2, // dynamic path, fully dynamic query
|
||||
(false, None) => -1, // dynamic path, no query
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
fn from(base: &Origin<'_>, origin: &Origin<'_>) -> Self {
|
||||
let base_segs = base.raw_path_segments()
|
||||
.map(Segment::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let path_segs = origin.raw_path_segments()
|
||||
.map(Segment::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let query_segs = origin.raw_query_segments()
|
||||
.map(Segment::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Metadata {
|
||||
static_path: path_segs.iter().all(|s| !s.dynamic),
|
||||
wild_path: path_segs.iter().all(|s| s.dynamic)
|
||||
&& path_segs.last().map_or(false, |p| p.trailing),
|
||||
trailing_path: path_segs.last().map_or(false, |p| p.trailing),
|
||||
wild_query: query_segs.iter().all(|s| s.dynamic),
|
||||
static_query_fields: query_segs.iter().filter(|s| !s.dynamic)
|
||||
.map(|s| ValueField::parse(&s.value))
|
||||
.map(|f| (f.name.source().to_string(), f.value.to_string()))
|
||||
.collect(),
|
||||
path_segs,
|
||||
query_segs,
|
||||
base_segs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Deref for RouteUri<'a> {
|
||||
type Target = Origin<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_origin()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RouteUri<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.origin.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RouteUri<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("RouteUri")
|
||||
.field("base", &self.base)
|
||||
.field("uri", &self.as_origin())
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use rocket::Route;
|
|||
|
||||
#[get("/<path..>")]
|
||||
fn files(route: &Route, path: PathBuf) -> String {
|
||||
Path::new(route.base()).join(path).normalized_str().to_string()
|
||||
Path::new(route.uri.base()).join(path).normalized_str().to_string()
|
||||
}
|
||||
|
||||
mod route_guard_tests {
|
||||
|
|
Loading…
Reference in New Issue