Always return 'Segments' from 'Request::segments()'.

The iterator may be empty. This changes the return type of
'Request::segments()' from 'Option<Segments>' to simply 'Segments'.

Internally also adds a 'Client::debug()' for easier request testing.
This commit is contained in:
Sergio Benitez 2021-03-04 21:47:29 -08:00
parent 5977fe1236
commit 58f365dac4
3 changed files with 30 additions and 35 deletions

View File

@ -385,8 +385,7 @@ impl Handler for StaticFiles {
// Otherwise, we're handling segments. Get the segments as a `PathBuf`, // Otherwise, we're handling segments. Get the segments as a `PathBuf`,
// only allowing dotfiles if the user allowed it. // only allowing dotfiles if the user allowed it.
let allow_dotfiles = self.options.contains(Options::DotFiles); let allow_dotfiles = self.options.contains(Options::DotFiles);
let path = req.segments::<Segments<'_>>(0..) let path = req.segments::<Segments<'_>>(0..).ok()
.and_then(|res| res.ok())
.and_then(|segments| segments.to_path_buf(allow_dotfiles).ok()) .and_then(|segments| segments.to_path_buf(allow_dotfiles).ok())
.map(|path| self.root.join(path)); .map(|path| self.root.join(path));

View File

@ -95,6 +95,14 @@ macro_rules! pub_client_impl {
Self::_new(rocket, false) $(.$suffix)? Self::_new(rocket, false) $(.$suffix)?
} }
#[doc(hidden)]
pub $($prefix)? fn debug(base: &str, routes: Vec<crate::Route>) -> Result<Self, Error> {
let mut config = crate::Config::debug_default();
config.log_level = crate::config::LogLevel::Debug;
let rocket = crate::custom(config).mount(base, routes);
Self::tracked(rocket) $(.$suffix)?
}
/// Deprecated alias to [`Client::tracked()`]. /// Deprecated alias to [`Client::tracked()`].
#[deprecated( #[deprecated(
since = "0.5", since = "0.5",

View File

@ -646,53 +646,41 @@ impl<'r> Request<'r> {
} }
/// Retrieves and parses into `T` all of the path segments in the request /// Retrieves and parses into `T` all of the path segments in the request
/// URI beginning and including the 0-indexed `n`th non-empty segment. `T` /// URI beginning and including the 0-indexed `n`th non-empty segment
/// must implement [`FromSegments`], which is used to parse the segments. /// _after_ the mount point.,that is, the `n`th segment _after_ the mount
/// point. If the request has not been routed, then this is simply the `n`th
/// non-empty request URI segment.
///
/// `T` must implement [`FromSegments`], which is used to parse the
/// segments. If there are no non-empty segments, the `Segments` iterator
/// will be empty.
/// ///
/// This method exists only to be used by manual routing. To retrieve /// This method exists only to be used by manual routing. To retrieve
/// segments from a request, use Rocket's code generation facilities. /// segments from a request, use Rocket's code generation facilities.
/// ///
/// # Error
///
/// If there are fewer than `n` non-empty segments, returns `None`. If
/// parsing the segments failed, returns `Some(Err(T:Error))`.
///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use rocket::{Request, http::Method};
/// use std::path::PathBuf; /// use std::path::PathBuf;
/// ///
/// use rocket::http::uri::Origin; /// # let c = rocket::local::blocking::Client::debug("/", vec![]).unwrap();
/// # let get = |uri| c.get(uri);
/// assert_eq!(get("/").segments(0..), Ok(PathBuf::new()));
/// assert_eq!(get("/").segments(2..), Ok(PathBuf::new()));
/// ///
/// # Request::example(Method::Get, "/", |req| { /// // Empty segments are skipped.
/// fn path<'s>(req: &'s mut Request, uri: &'static str, n: usize) -> PathBuf { /// assert_eq!(get("///").segments(2..), Ok(PathBuf::new()));
/// req.set_uri(Origin::parse(uri).unwrap()); /// assert_eq!(get("/a/b/c").segments(0..), Ok(PathBuf::from("a/b/c")));
/// /// assert_eq!(get("/a/b/c").segments(1..), Ok(PathBuf::from("b/c")));
/// req.segments(n..) /// assert_eq!(get("/a/b/c").segments(2..), Ok(PathBuf::from("c")));
/// .and_then(|r| r.ok()) /// assert_eq!(get("/a/b/c").segments(3..), Ok(PathBuf::new()));
/// .unwrap_or_else(|| "whoops".into()) /// assert_eq!(get("/a/b/c").segments(4..), Ok(PathBuf::new()));
/// }
///
/// assert_eq!(path(req, "/", 0), PathBuf::from("whoops"));
/// assert_eq!(path(req, "/a/", 0), PathBuf::from("a"));
/// assert_eq!(path(req, "/a/b/c", 0), PathBuf::from("a/b/c"));
/// assert_eq!(path(req, "/a/b/c", 1), PathBuf::from("b/c"));
/// assert_eq!(path(req, "/a/b/c", 2), PathBuf::from("c"));
/// assert_eq!(path(req, "/a/b/c", 6), PathBuf::from("whoops"));
/// # });
/// ``` /// ```
#[inline] #[inline]
pub fn segments<'a, T>(&'a self, n: RangeFrom<usize>) -> Option<Result<T, T::Error>> pub fn segments<'a, T>(&'a self, n: RangeFrom<usize>) -> Result<T, T::Error>
where T: FromSegments<'a> where T: FromSegments<'a>
{ {
// FIXME: https://github.com/SergioBenitez/Rocket/issues/985. T::from_segments(self.routed_segments(n))
let segments = self.routed_segments(n);
if segments.is_empty() {
None
} else {
Some(T::from_segments(segments))
}
} }
/// Retrieves and parses into `T` the query value with field name `name`. /// Retrieves and parses into `T` the query value with field name `name`.