Allow setting a custom rank on 'StaticFiles'.

Resolves #965.
This commit is contained in:
Sergio Benitez 2019-05-16 16:54:35 -07:00
parent 23f5ebcc4f
commit dbcb0a75b9
2 changed files with 52 additions and 12 deletions

View File

@ -29,14 +29,16 @@ use rocket::outcome::IntoOutcome;
/// * [`Options::None`] - Return only present, visible files. /// * [`Options::None`] - Return only present, visible files.
/// * [`Options::DotFiles`] - In addition to visible files, return dotfiles. /// * [`Options::DotFiles`] - In addition to visible files, return dotfiles.
/// * [`Options::Index`] - Render `index.html` pages for directory requests. /// * [`Options::Index`] - Render `index.html` pages for directory requests.
/// * [`Options::Rank(n)`](Options::Rank) - Mount the static files route(s)
/// with rank `n`.
/// ///
/// Two `Options` structures can be `or`d together to slect two or more options. /// `Options` structures can be `or`d together to select two or more options.
/// For instance, to request that both dot files and index pages be returned, /// For instance, to request that both dot files and index pages be returned,
/// use `Options::DotFiles | Options::Index`. /// use `Options::DotFiles | Options::Index`.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Options(u8); pub struct Options(u32);
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals, non_snake_case)]
impl Options { impl Options {
/// `Options` representing the empty set. No dotfiles or index pages are /// `Options` representing the empty set. No dotfiles or index pages are
/// rendered. This is different than the _default_, which enables `Index`. /// rendered. This is different than the _default_, which enables `Index`.
@ -54,6 +56,17 @@ impl Options {
/// directories beginning with `.`. This is _not_ enabled by default. /// directories beginning with `.`. This is _not_ enabled by default.
pub const DotFiles: Options = Options(0b0010); pub const DotFiles: Options = Options(0b0010);
/// `Options` setting a rank of `rank` on the [`StaticFiles`] route(s). By
/// default, the rank is set to `10`.
pub const fn Rank(rank: i16) -> Options {
Options((rank as u32) << 16)
}
/// Return the rank set in `self`.
fn rank(self) -> isize {
((self.0 >> 16) as i16) as isize
}
/// Returns `true` if `self` is a superset of `other`. In other words, /// Returns `true` if `self` is a superset of `other`. In other words,
/// returns `true` if all of the options in `other` are also in `self`. /// returns `true` if all of the options in `other` are also in `self`.
/// ///
@ -80,6 +93,12 @@ impl Options {
} }
} }
impl Default for Options {
fn default() -> Self {
Options::Index | Options::Rank(10)
}
}
impl ::std::ops::BitOr for Options { impl ::std::ops::BitOr for Options {
type Output = Self; type Output = Self;
@ -154,8 +173,9 @@ pub struct StaticFiles {
impl StaticFiles { impl StaticFiles {
/// Constructs a new `StaticFiles` that serves files from the file system /// Constructs a new `StaticFiles` that serves files from the file system
/// `path`. By default, [`Options::Index`] is enabled. To serve static files /// `path`. By default, [`Options::Index`] and
/// with other options, use [`StaticFiles::new()`]. /// [`Options::Rank(10)`](Options::Rank) are set. To serve static files with
/// other options, use [`StaticFiles::new()`].
/// ///
/// # Example /// # Example
/// ///
@ -176,7 +196,7 @@ impl StaticFiles {
/// } /// }
/// ``` /// ```
pub fn from<P: AsRef<Path>>(path: P) -> Self { pub fn from<P: AsRef<Path>>(path: P) -> Self {
StaticFiles::new(path, Options::Index) StaticFiles::new(path, Options::default())
} }
/// Constructs a new `StaticFiles` that serves files from the file system /// Constructs a new `StaticFiles` that serves files from the file system
@ -186,7 +206,8 @@ impl StaticFiles {
/// ///
/// Serve the static files in the `/www/public` local directory on path /// Serve the static files in the `/www/public` local directory on path
/// `/static` without serving index files or dot files. Additionally, serve /// `/static` without serving index files or dot files. Additionally, serve
/// the same files on `/pub` while also seriving index files and dot files. /// the same files on `/pub` with a route rank of -1 while also serving
/// index files and dot files.
/// ///
/// ```rust /// ```rust
/// # extern crate rocket; /// # extern crate rocket;
@ -195,7 +216,7 @@ impl StaticFiles {
/// ///
/// fn main() { /// fn main() {
/// # if false { /// # if false {
/// let options = Options::Index | Options::DotFiles; /// let options = Options::Index | Options::DotFiles | Options::Rank(-1);
/// rocket::ignite() /// rocket::ignite()
/// .mount("/static", StaticFiles::from("/www/public")) /// .mount("/static", StaticFiles::from("/www/public"))
/// .mount("/pub", StaticFiles::new("/www/public", options)) /// .mount("/pub", StaticFiles::new("/www/public", options))
@ -210,9 +231,10 @@ impl StaticFiles {
impl Into<Vec<Route>> for StaticFiles { impl Into<Vec<Route>> for StaticFiles {
fn into(self) -> Vec<Route> { fn into(self) -> Vec<Route> {
let non_index = Route::ranked(10, Method::Get, "/<path..>", self.clone()); let rank = self.options.rank();
let non_index = Route::ranked(rank, Method::Get, "/<path..>", self.clone());
if self.options.contains(Options::Index) { if self.options.contains(Options::Index) {
let index = Route::ranked(10, Method::Get, "/", self); let index = Route::ranked(rank, Method::Get, "/", self);
vec![index, non_index] vec![index, non_index]
} else { } else {
vec![non_index] vec![non_index]

View File

@ -3,12 +3,12 @@
extern crate rocket; extern crate rocket;
extern crate rocket_contrib; extern crate rocket_contrib;
#[cfg(feature = "static")] #[cfg(feature = "serve")]
mod static_tests { mod static_tests {
use std::{io::Read, fs::File}; use std::{io::Read, fs::File};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rocket::{self, Rocket}; use rocket::{self, Rocket, Route};
use rocket_contrib::serve::{StaticFiles, Options}; use rocket_contrib::serve::{StaticFiles, Options};
use rocket::http::Status; use rocket::http::Status;
use rocket::local::Client; use rocket::local::Client;
@ -105,4 +105,22 @@ mod static_tests {
assert_all(&client, "both", HIDDEN_FILES, true); assert_all(&client, "both", HIDDEN_FILES, true);
assert_all(&client, "both", INDEXED_DIRECTORIES, true); assert_all(&client, "both", INDEXED_DIRECTORIES, true);
} }
#[test]
fn test_ranking() {
let root = static_root();
for rank in (-128..127).chain([-32768, 32767].iter().cloned()) {
let opt_rank = Options::Rank(rank as i16);
let a = StaticFiles::new(&root, opt_rank);
let b = StaticFiles::new(&root, Options::None | opt_rank);
let c = StaticFiles::new(&root, Options::Index | opt_rank);
let d = StaticFiles::new(&root, Options::DotFiles | opt_rank);
let e = StaticFiles::new(&root, Options::Index | Options::DotFiles | opt_rank);
for handler in vec![a, b, c, d, e] {
let routes: Vec<Route> = handler.into();
assert!(routes.iter().all(|route| route.rank == rank), "{}", rank);
}
}
}
} }