mirror of https://github.com/rwf2/Rocket.git
Hide 'RouteUri' fields to ensure URI coherence.
Prior to this commit, several `RouteUri` fields were public, allowing those values to be changed at will. These changes were at times not reflected by the rest of the library, meaning that the values in the route URI structure for a route became incoherent with the reflected values. This commit makes all fields private, forcing all changes to go through methods that can ensure coherence. All values remain accessible via getter methods.
This commit is contained in:
parent
51ed332127
commit
3a44b1b28e
|
@ -45,13 +45,13 @@ fn generate_matching_requests<'c>(client: &'c Client, routes: &[Route]) -> Vec<L
|
|||
}
|
||||
|
||||
fn request_for_route<'c>(client: &'c Client, route: &Route) -> LocalRequest<'c> {
|
||||
let path = route.uri.uri.path()
|
||||
let path = route.uri.path()
|
||||
.raw_segments()
|
||||
.map(staticify_segment)
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
|
||||
let query = route.uri.uri.query()
|
||||
let query = route.uri.query()
|
||||
.map(|q| q.raw_segments())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
|
|
|
@ -1009,6 +1009,13 @@ impl AsRef<str> for RawStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<std::ffi::OsStr> for RawStr {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &std::ffi::OsStr {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<RawStr> for str {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &RawStr {
|
||||
|
|
|
@ -548,7 +548,7 @@ impl<'a> Origin<'a> {
|
|||
|
||||
impl_serde!(Origin<'a>, "an origin-form URI");
|
||||
|
||||
impl_traits!(Origin, path, query);
|
||||
impl_traits!(Origin [parse_route], path, query);
|
||||
|
||||
impl std::fmt::Display for Origin<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
|
@ -416,6 +416,12 @@ macro_rules! impl_traits {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<std::ffi::OsStr> for $T<'_> {
|
||||
fn as_ref(&self) -> &std::ffi::OsStr {
|
||||
self.raw().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $T<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.raw())
|
||||
|
|
|
@ -391,7 +391,10 @@ macro_rules! impl_serde {
|
|||
/// Implements traits from `impl_base_traits` and IntoOwned for a URI.
|
||||
macro_rules! impl_traits {
|
||||
($T:ident, $($field:ident),* $(,)?) => {
|
||||
impl_base_traits!($T, $($field),*);
|
||||
impl_traits!($T [parse], $($field),*);
|
||||
};
|
||||
($T:ident [$partial_eq_parse:ident], $($field:ident),* $(,)?) => {
|
||||
impl_base_traits!($T [$partial_eq_parse], $($field),*);
|
||||
|
||||
impl crate::ext::IntoOwned for $T<'_> {
|
||||
type Owned = $T<'static>;
|
||||
|
@ -409,6 +412,9 @@ macro_rules! impl_traits {
|
|||
/// Implements PartialEq, Eq, Hash, and TryFrom.
|
||||
macro_rules! impl_base_traits {
|
||||
($T:ident, $($field:ident),* $(,)?) => {
|
||||
impl_base_traits!($T [parse], $($field),*);
|
||||
};
|
||||
($T:ident [$partial_eq_parse:ident], $($field:ident),* $(,)?) => {
|
||||
impl std::convert::TryFrom<String> for $T<'static> {
|
||||
type Error = Error<'static>;
|
||||
|
||||
|
@ -442,7 +448,7 @@ macro_rules! impl_base_traits {
|
|||
|
||||
impl PartialEq<str> for $T<'_> {
|
||||
fn eq(&self, string: &str) -> bool {
|
||||
$T::parse(string).map_or(false, |v| &v == self)
|
||||
$T::$partial_eq_parse(string).map_or(false, |v| &v == self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ impl std::fmt::Debug for ArbitraryRouteData<'_> {
|
|||
f.debug_struct("ArbitraryRouteData")
|
||||
.field("method", &self.method.0)
|
||||
.field("base", &self.uri.0.base())
|
||||
.field("path", &self.uri.0.unmounted_origin.to_string())
|
||||
.field("uri", &self.uri.0.uri.to_string())
|
||||
.field("unmounted", &self.uri.0.unmounted().to_string())
|
||||
.field("uri", &self.uri.0.to_string())
|
||||
.field("format", &self.format.as_ref().map(|v| v.0.to_string()))
|
||||
.finish()
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ impl<'c, 'a: 'c> ArbitraryRequestData<'a> {
|
|||
|
||||
impl<'a> ArbitraryRouteData<'a> {
|
||||
fn into_route(self) -> Route {
|
||||
let mut r = Route::ranked(0, self.method.0, self.uri.0.as_str(), dummy_handler);
|
||||
let mut r = Route::ranked(0, self.method.0, &self.uri.0.to_string(), dummy_handler);
|
||||
r.format = self.format.map(|f| f.0);
|
||||
r
|
||||
}
|
||||
|
|
|
@ -200,6 +200,11 @@ impl Route {
|
|||
///
|
||||
/// Panics if `path` is not a valid Rocket route URI.
|
||||
///
|
||||
/// A valid route URI is any valid [`Origin`](uri::Origin) URI that is
|
||||
/// normalized, that is, does not contain any empty segments except for an
|
||||
/// optional trailing slash. Unlike a strict `Origin`, route URIs are also
|
||||
/// allowed to contain any UTF-8 characters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
|
@ -207,7 +212,7 @@ impl Route {
|
|||
/// use rocket::http::Method;
|
||||
/// # use rocket::route::dummy_handler as handler;
|
||||
///
|
||||
/// // this is a rank 1 route matching requests to `GET /`
|
||||
/// // this is a route matching requests to `GET /`
|
||||
/// let index = Route::new(Method::Get, "/", handler);
|
||||
/// assert_eq!(index.rank, -9);
|
||||
/// assert_eq!(index.method, Method::Get);
|
||||
|
@ -226,6 +231,11 @@ impl Route {
|
|||
///
|
||||
/// Panics if `path` is not a valid Rocket route URI.
|
||||
///
|
||||
/// A valid route URI is any valid [`Origin`](uri::Origin) URI that is
|
||||
/// normalized, that is, does not contain any empty segments except for an
|
||||
/// optional trailing slash. Unlike a strict `Origin`, route URIs are also
|
||||
/// allowed to contain any UTF-8 characters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
|
@ -275,12 +285,12 @@ impl Route {
|
|||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar", handler);
|
||||
/// assert_eq!(index.uri.base(), "/");
|
||||
/// assert_eq!(index.uri.unmounted_origin.path(), "/foo/bar");
|
||||
/// assert_eq!(index.uri.unmounted().path(), "/foo/bar");
|
||||
/// assert_eq!(index.uri.path(), "/foo/bar");
|
||||
///
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.base(), "/boo");
|
||||
/// assert_eq!(index.uri.unmounted_origin.path(), "/foo/bar");
|
||||
/// assert_eq!(index.uri.unmounted().path(), "/foo/bar");
|
||||
/// assert_eq!(index.uri.path(), "/boo/foo/bar");
|
||||
/// ```
|
||||
pub fn map_base<'a, F>(mut self, mapper: F) -> Result<Self, uri::Error<'static>>
|
||||
|
@ -309,7 +319,7 @@ impl fmt::Display for Route {
|
|||
write!(f, "{}", Paint::blue(self.uri.base()).underline())?;
|
||||
}
|
||||
|
||||
write!(f, "{}", Paint::blue(&self.uri.unmounted_origin))?;
|
||||
write!(f, "{}", Paint::blue(&self.uri.unmounted()))?;
|
||||
|
||||
if self.rank > 1 {
|
||||
write!(f, " [{}]", Paint::default(&self.rank).bold())?;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::fmt;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::http::uri::{self, Origin};
|
||||
use crate::http::uri::{self, Origin, Path};
|
||||
use crate::http::ext::IntoOwned;
|
||||
use crate::form::ValueField;
|
||||
use crate::route::Segment;
|
||||
|
@ -53,16 +52,14 @@ use crate::route::Segment;
|
|||
///
|
||||
/// [`Rocket::mount()`]: crate::Rocket::mount()
|
||||
/// [`Route::new()`]: crate::Route::new()
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RouteUri<'a> {
|
||||
/// The source string for this URI.
|
||||
source: Cow<'a, str>,
|
||||
/// The mount point.
|
||||
pub base: Origin<'a>,
|
||||
pub(crate) base: Origin<'a>,
|
||||
/// The URI _without_ the `base` mount point.
|
||||
pub unmounted_origin: Origin<'a>,
|
||||
pub(crate) unmounted_origin: Origin<'a>,
|
||||
/// The URI _with_ the base mount point. This is the canonical route URI.
|
||||
pub uri: Origin<'a>,
|
||||
pub(crate) uri: Origin<'a>,
|
||||
/// Cached metadata about this URI.
|
||||
pub(crate) metadata: Metadata,
|
||||
}
|
||||
|
@ -98,6 +95,14 @@ type Result<T, E = uri::Error<'static>> = std::result::Result<T, E>;
|
|||
impl<'a> RouteUri<'a> {
|
||||
/// Create a new `RouteUri`.
|
||||
///
|
||||
/// Panics if `base` or `uri` cannot be parsed as `Origin`s.
|
||||
#[track_caller]
|
||||
pub(crate) fn new(base: &str, uri: &str) -> RouteUri<'static> {
|
||||
Self::try_new(base, uri).expect("Expected valid URIs")
|
||||
}
|
||||
|
||||
/// Creates a new `RouteUri` from a `base` mount point and a route `uri`.
|
||||
///
|
||||
/// This is a fallible variant of [`RouteUri::new`] which returns an `Err`
|
||||
/// if `base` or `uri` cannot be parsed as [`Origin`]s.
|
||||
/// INTERNAL!
|
||||
|
@ -129,21 +134,15 @@ impl<'a> RouteUri<'a> {
|
|||
.into_normalized()
|
||||
.into_owned();
|
||||
|
||||
let source = uri.to_string().into();
|
||||
let metadata = Metadata::from(&base, &uri);
|
||||
|
||||
Ok(RouteUri { source, base, unmounted_origin: origin, uri, metadata })
|
||||
Ok(RouteUri { base, unmounted_origin: origin, uri, metadata })
|
||||
}
|
||||
|
||||
/// Create a new `RouteUri`.
|
||||
/// Returns the complete route URI.
|
||||
///
|
||||
/// Panics if `base` or `uri` cannot be parsed as `Origin`s.
|
||||
#[track_caller]
|
||||
pub(crate) fn new(base: &str, uri: &str) -> RouteUri<'static> {
|
||||
Self::try_new(base, uri).expect("Expected valid URIs")
|
||||
}
|
||||
|
||||
/// The path of the base mount point of this route URI as an `&str`.
|
||||
/// **Note:** `RouteURI` derefs to the `Origin` returned by this method, so
|
||||
/// this method should rarely be called directly.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -152,17 +151,19 @@ impl<'a> RouteUri<'a> {
|
|||
/// use rocket::http::Method;
|
||||
/// # use rocket::route::dummy_handler as handler;
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// assert_eq!(index.uri.base(), "/");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.base(), "/boo");
|
||||
/// let route = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
///
|
||||
/// // Use `inner()` directly:
|
||||
/// assert_eq!(route.uri.inner().query().unwrap(), "a=1");
|
||||
///
|
||||
/// // Use the deref implementation. This is preferred:
|
||||
/// assert_eq!(route.uri.query().unwrap(), "a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn base(&self) -> &str {
|
||||
self.base.path().as_str()
|
||||
pub fn inner(&self) -> &Origin<'a> {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
/// The path part of this route URI as an `&str`.
|
||||
/// The base mount point of this route URI.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -171,17 +172,18 @@ impl<'a> RouteUri<'a> {
|
|||
/// use rocket::http::Method;
|
||||
/// # use rocket::route::dummy_handler as handler;
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// assert_eq!(index.uri.path(), "/foo/bar");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.path(), "/boo/foo/bar");
|
||||
/// let route = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// assert_eq!(route.uri.base(), "/");
|
||||
///
|
||||
/// let route = route.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(route.uri.base(), "/boo");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn path(&self) -> &str {
|
||||
self.uri.path().as_str()
|
||||
pub fn base(&self) -> Path<'_> {
|
||||
self.base.path()
|
||||
}
|
||||
|
||||
/// The query part of this route URI, if there is one.
|
||||
/// The route URI _without_ the base mount point.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -190,41 +192,16 @@ impl<'a> RouteUri<'a> {
|
|||
/// use rocket::http::Method;
|
||||
/// # use rocket::route::dummy_handler as handler;
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar", handler);
|
||||
/// assert!(index.uri.query().is_none());
|
||||
/// let route = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// let route = route.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
///
|
||||
/// // Normalization clears the empty '?'.
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?", handler);
|
||||
/// assert_eq!(index.uri.query().unwrap(), "");
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// assert_eq!(index.uri.query().unwrap(), "a=1");
|
||||
///
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.query().unwrap(), "a=1");
|
||||
/// assert_eq!(route.uri, "/boo/foo/bar?a=1");
|
||||
/// assert_eq!(route.uri.base(), "/boo");
|
||||
/// assert_eq!(route.uri.unmounted(), "/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.uri.query().map(|q| q.as_str())
|
||||
}
|
||||
|
||||
/// The full URI as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Route;
|
||||
/// use rocket::http::Method;
|
||||
/// # use rocket::route::dummy_handler as handler;
|
||||
///
|
||||
/// let index = Route::new(Method::Get, "/foo/bar?a=1", handler);
|
||||
/// assert_eq!(index.uri.as_str(), "/foo/bar?a=1");
|
||||
/// let index = index.map_base(|base| format!("{}{}", "/boo", base)).unwrap();
|
||||
/// assert_eq!(index.uri.as_str(), "/boo/foo/bar?a=1");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.source
|
||||
pub fn unmounted(&self) -> &Origin<'a> {
|
||||
&self.unmounted_origin
|
||||
}
|
||||
|
||||
/// Get the default rank of a route with this URI.
|
||||
|
@ -306,35 +283,24 @@ impl<'a> std::ops::Deref for RouteUri<'a> {
|
|||
type Target = Origin<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.uri
|
||||
self.inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RouteUri<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.uri.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("unmounted_origin", &self.unmounted_origin)
|
||||
.field("origin", &self.uri)
|
||||
.field("metadata", &self.metadata)
|
||||
.finish()
|
||||
self.inner().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> PartialEq<Origin<'b>> for RouteUri<'a> {
|
||||
fn eq(&self, other: &Origin<'b>) -> bool { &self.uri == other }
|
||||
fn eq(&self, other: &Origin<'b>) -> bool { self.inner() == other }
|
||||
}
|
||||
|
||||
impl PartialEq<str> for RouteUri<'_> {
|
||||
fn eq(&self, other: &str) -> bool { self.as_str() == other }
|
||||
fn eq(&self, other: &str) -> bool { self.inner() == other }
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for RouteUri<'_> {
|
||||
fn eq(&self, other: &&str) -> bool { self.as_str() == *other }
|
||||
fn eq(&self, other: &&str) -> bool { self.inner() == *other }
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use rocket::Route;
|
|||
|
||||
#[get("/<path..>")]
|
||||
fn files(route: &Route, path: PathBuf) -> String {
|
||||
Path::new(route.uri.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