From a115eaa633856eb0b09f4019952f866e6b4ef96d Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 23 Jul 2020 16:48:26 -0700 Subject: [PATCH] Add 'serve::crate_relative!' for relative paths. The macro generates crate-relative paths for easier serving of files stored relative to the crate root. --- contrib/lib/src/serve.rs | 56 +++++++++++++++++++++----- contrib/lib/tests/static_files.rs | 10 ++--- examples/form_kitchen_sink/src/main.rs | 6 ++- examples/form_validation/src/main.rs | 4 +- examples/static_files/src/main.rs | 4 +- examples/todo/src/main.rs | 4 +- 6 files changed, 61 insertions(+), 23 deletions(-) diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 106d0dde..92823efe 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -21,6 +21,44 @@ use rocket::http::{Method, uri::Segments, ext::IntoOwned}; use rocket::handler::{Handler, Outcome}; use rocket::response::{NamedFile, Redirect}; +/// Generates a crate-relative version of `$path`. +/// +/// This macro is primarily intended for use with [`StaticFiles`] to serve files +/// from a path relative to the crate root. The macro accepts one parameter, +/// `$path`, an absolute or relative path. It returns a path (an `&'static str`) +/// prefixed with the path to the crate root. Use `Path::new()` to retrieve an +/// `&'static Path`. +/// +/// See the [relative paths `StaticFiles` +/// documentation](`StaticFiles`#relative-paths) for an example. +/// +/// # Example +/// +/// ```rust +/// use rocket_contrib::serve::{StaticFiles, crate_relative}; +/// +/// let manual = concat!(env!("CARGO_MANIFEST_DIR"), "/static"); +/// let automatic = crate_relative!("static"); +/// assert_eq!(manual, automatic); +/// +/// use std::path::Path; +/// +/// let manual = Path::new(env!("CARGO_MANIFEST_DIR")).join("static"); +/// let automatic_1 = Path::new(crate_relative!("static")); +/// let automatic_2 = Path::new(crate_relative!("/static")); +/// assert_eq!(manual, automatic_1); +/// assert_eq!(automatic_1, automatic_2); +/// ``` +#[macro_export] +macro_rules! crate_relative { + ($path:expr) => { + concat!(env!("CARGO_MANIFEST_DIR"), "/", $path) + }; +} + +#[doc(inline)] +pub use crate_relative; + /// A bitset representing configurable options for the [`StaticFiles`] handler. /// /// The valid options are: @@ -146,22 +184,22 @@ impl std::ops::BitOr for Options { /// `/public/` will be handled by returning the contents of /// `/static//index.html`. /// -/// `/static` is an absolute path. If your static files are stored relative to -/// your crate and your project is managed by Cargo, you should either use a -/// relative path and ensure that your server is started in the crate's root -/// directory or use the `CARGO_MANIFEST_DIR` environment variable to create an -/// absolute path relative to your crate root. For example, to serve files in -/// the `static` subdirectory of your crate at `/`, you might write: +/// ## Relative Paths +/// +/// In the example above, `/static` is an absolute path. If your static files +/// are stored relative to your crate and your project is managed by Cargo, use +/// the [`crate_relative!`] macro to obtain a path that is relative to your +/// crate's root. For example, to serve files in the `static` subdirectory of +/// your crate at `/`, you might write: /// /// ```rust,no_run /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; -/// use rocket_contrib::serve::StaticFiles; +/// use rocket_contrib::serve::{StaticFiles, crate_relative}; /// /// #[launch] /// fn rocket() -> rocket::Rocket { -/// rocket::ignite() -/// .mount("/", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static"))) +/// rocket::ignite().mount("/", StaticFiles::from(crate_relative!("/static"))) /// } /// ``` #[derive(Clone)] diff --git a/contrib/lib/tests/static_files.rs b/contrib/lib/tests/static_files.rs index b4cbb391..4827294e 100644 --- a/contrib/lib/tests/static_files.rs +++ b/contrib/lib/tests/static_files.rs @@ -1,17 +1,15 @@ #[cfg(feature = "serve")] mod static_tests { use std::{io::Read, fs::File}; - use std::path::{Path, PathBuf}; + use std::path::Path; use rocket::{self, Rocket, Route}; - use rocket_contrib::serve::{StaticFiles, Options}; + use rocket_contrib::serve::{StaticFiles, Options, crate_relative}; use rocket::http::Status; use rocket::local::blocking::Client; - fn static_root() -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("static") + fn static_root() -> &'static Path { + Path::new(crate_relative!("/tests/static")) } fn rocket() -> Rocket { diff --git a/examples/form_kitchen_sink/src/main.rs b/examples/form_kitchen_sink/src/main.rs index fa5ea565..2a5481b3 100644 --- a/examples/form_kitchen_sink/src/main.rs +++ b/examples/form_kitchen_sink/src/main.rs @@ -3,7 +3,7 @@ use rocket::request::{Form, FormError, FormDataError}; use rocket::http::RawStr; -use rocket_contrib::serve::StaticFiles; +use rocket_contrib::serve::{StaticFiles, crate_relative}; #[cfg(test)] mod tests; @@ -37,5 +37,7 @@ fn sink(sink: Result>, FormError<'_>>) -> String { #[launch] fn rocket() -> rocket::Rocket { - rocket::ignite().mount("/", routes![sink]).mount("/", StaticFiles::from("static/")) + rocket::ignite() + .mount("/", routes![sink]) + .mount("/", StaticFiles::from(crate_relative!("/static"))) } diff --git a/examples/form_validation/src/main.rs b/examples/form_validation/src/main.rs index ddc68fb3..b253ffef 100644 --- a/examples/form_validation/src/main.rs +++ b/examples/form_validation/src/main.rs @@ -6,7 +6,7 @@ use rocket::response::Redirect; use rocket::request::{Form, FromFormValue}; use rocket::http::RawStr; -use rocket_contrib::serve::StaticFiles; +use rocket_contrib::serve::{StaticFiles, crate_relative}; #[derive(Debug)] struct StrongPassword<'r>(&'r str); @@ -79,5 +79,5 @@ fn user_page(username: &RawStr) -> String { fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![user_page, login]) - .mount("/", StaticFiles::from("static/")) + .mount("/", StaticFiles::from(crate_relative!("/static"))) } diff --git a/examples/static_files/src/main.rs b/examples/static_files/src/main.rs index e39c331f..5bf135fe 100644 --- a/examples/static_files/src/main.rs +++ b/examples/static_files/src/main.rs @@ -2,7 +2,7 @@ #[cfg(test)] mod tests; -use rocket_contrib::serve::StaticFiles; +use rocket_contrib::serve::{StaticFiles, crate_relative}; // If we wanted or needed to serve files manually, we'd use `NamedFile`. Always // prefer to use `StaticFiles`! @@ -19,5 +19,5 @@ mod manual { fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![manual::icon]) - .mount("/", StaticFiles::from("static")) + .mount("/", StaticFiles::from(crate_relative!("/static"))) } diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index f9dc6257..67563f96 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -11,7 +11,7 @@ use rocket::Rocket; use rocket::fairing::AdHoc; use rocket::request::{Form, FlashMessage}; use rocket::response::{Flash, Redirect}; -use rocket_contrib::{templates::Template, serve::StaticFiles}; +use rocket_contrib::{templates::Template, serve::{StaticFiles, crate_relative}}; use diesel::SqliteConnection; use crate::task::{Task, Todo}; @@ -106,7 +106,7 @@ fn rocket() -> Rocket { rocket::ignite() .attach(DbConn::fairing()) .attach(AdHoc::on_attach("Database Migrations", run_db_migrations)) - .mount("/", StaticFiles::from("static/")) + .mount("/", StaticFiles::from(crate_relative!("/static"))) .mount("/", routes![index]) .mount("/todo", routes![new, toggle, delete]) .attach(Template::fairing())