Use 'RelativePathBuf' as 'Config.temp_dir' type.

This makes a relative 'temp_dir' declared in a config file relative to
the config file itself.
This commit is contained in:
Sergio Benitez 2021-06-25 11:42:49 -07:00
parent c3ee34e295
commit 76ec847a58
6 changed files with 28 additions and 23 deletions

View File

@ -46,7 +46,7 @@ atomic = "0.5"
parking_lot = "0.11" parking_lot = "0.11"
ubyte = {version = "0.10", features = ["serde"] } ubyte = {version = "0.10", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
figment = { version = "0.10.4", features = ["toml", "env"] } figment = { version = "0.10.6", features = ["toml", "env"] }
rand = "0.8" rand = "0.8"
either = "1" either = "1"
pin-project-lite = "0.2" pin-project-lite = "0.2"

View File

@ -1,9 +1,8 @@
use std::path::PathBuf;
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
use figment::{Figment, Profile, Provider, Metadata, error::Result}; use figment::{Figment, Profile, Provider, Metadata, error::Result};
use figment::providers::{Serialized, Env, Toml, Format}; use figment::providers::{Serialized, Env, Toml, Format};
use figment::value::{Map, Dict}; use figment::value::{Map, Dict, magic::RelativePathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yansi::Paint; use yansi::Paint;
@ -87,7 +86,8 @@ pub struct Config {
pub secret_key: SecretKey, pub secret_key: SecretKey,
/// Directory to store temporary files in. **(default: /// Directory to store temporary files in. **(default:
/// [`std::env::temp_dir()`])** /// [`std::env::temp_dir()`])**
pub temp_dir: PathBuf, #[serde(serialize_with = "RelativePathBuf::serialize_relative")]
pub temp_dir: RelativePathBuf,
/// Max level to log. **(default: _debug_ `normal` / _release_ `critical`)** /// Max level to log. **(default: _debug_ `normal` / _release_ `critical`)**
pub log_level: LogLevel, pub log_level: LogLevel,
/// Graceful shutdown configuration. **(default: [`Shutdown::default()`])** /// Graceful shutdown configuration. **(default: [`Shutdown::default()`])**
@ -167,7 +167,7 @@ impl Config {
ident: Ident::default(), ident: Ident::default(),
#[cfg(feature = "secrets")] #[cfg(feature = "secrets")]
secret_key: SecretKey::zero(), secret_key: SecretKey::zero(),
temp_dir: std::env::temp_dir(), temp_dir: std::env::temp_dir().into(),
log_level: LogLevel::Normal, log_level: LogLevel::Normal,
shutdown: Shutdown::default(), shutdown: Shutdown::default(),
cli_colors: true, cli_colors: true,
@ -335,7 +335,7 @@ impl Config {
} }
} }
launch_info_!("temp dir: {}", Paint::default(&self.temp_dir.display()).bold()); launch_info_!("temp dir: {}", Paint::default(&self.temp_dir.relative().display()).bold());
launch_info_!("log level: {}", Paint::default(self.log_level).bold()); launch_info_!("log level: {}", Paint::default(self.log_level).bold());
launch_info_!("cli colors: {}", Paint::default(&self.cli_colors).bold()); launch_info_!("cli colors: {}", Paint::default(&self.cli_colors).bold());
launch_info_!("shutdown: {}", Paint::default(&self.shutdown).bold()); launch_info_!("shutdown: {}", Paint::default(&self.shutdown).bold());
@ -369,6 +369,7 @@ impl Config {
} }
} }
/// Associated constants for default profiles.
impl Config { impl Config {
/// The default debug profile: `debug`. /// The default debug profile: `debug`.
pub const DEBUG_PROFILE: Profile = Profile::const_new("debug"); pub const DEBUG_PROFILE: Profile = Profile::const_new("debug");
@ -386,6 +387,7 @@ impl Config {
} }
/// Associated constants for stringy versions of configuration parameters.
impl Config { impl Config {
/// The stringy parameter name for setting/extracting [`Config::profile`]. /// The stringy parameter name for setting/extracting [`Config::profile`].
/// ///
@ -421,6 +423,9 @@ impl Config {
/// The stringy parameter name for setting/extracting [`Config::shutdown`]. /// The stringy parameter name for setting/extracting [`Config::shutdown`].
pub const SHUTDOWN: &'static str = "shutdown"; pub const SHUTDOWN: &'static str = "shutdown";
/// The stringy parameter name for setting/extracting [`Config::cli_colors`].
pub const CLI_COLORS: &'static str = "cli_colors";
} }
impl Provider for Config { impl Provider for Config {

View File

@ -45,7 +45,7 @@ use either::Either;
/// ///
/// | Name | Default | Description | /// | Name | Default | Description |
/// |--------------------|---------------------|-----------------------------------------| /// |--------------------|---------------------|-----------------------------------------|
/// | `temp_dir` | [`env::temp_dir()`] | Directory files are temporarily stored. | /// | `temp_dir` | [`env::temp_dir()`] | Directory for temporary file storage. |
/// | `limits.file` | 1MiB | Default limit for all file extensions. | /// | `limits.file` | 1MiB | Default limit for all file extensions. |
/// | `limits.file/$ext` | _N/A_ | Limit for files with extension `$ext`. | /// | `limits.file/$ext` | _N/A_ | Limit for files with extension `$ext`. |
/// ///
@ -451,7 +451,7 @@ impl<'v> TempFile<'v> {
.or_else(|| req.limits().get("file")) .or_else(|| req.limits().get("file"))
.unwrap_or(Limits::FILE); .unwrap_or(Limits::FILE);
let temp_dir = req.rocket().config().temp_dir.clone(); let temp_dir = req.rocket().config().temp_dir.relative();
let file = tokio::task::spawn_blocking(move || { let file = tokio::task::spawn_blocking(move || {
NamedTempFile::new_in(temp_dir) NamedTempFile::new_in(temp_dir)
}).await.map_err(|_| { }).await.map_err(|_| {

View File

@ -182,7 +182,7 @@ impl Rocket<Build> {
/// let config = Config { /// let config = Config {
/// port: 7777, /// port: 7777,
/// address: Ipv4Addr::new(18, 127, 0, 1).into(), /// address: Ipv4Addr::new(18, 127, 0, 1).into(),
/// temp_dir: PathBuf::from("/tmp/config-example"), /// temp_dir: "/tmp/config-example".into(),
/// ..Config::debug_default() /// ..Config::debug_default()
/// }; /// };
/// ///
@ -190,7 +190,7 @@ impl Rocket<Build> {
/// let rocket = rocket::custom(&config).ignite().await?; /// let rocket = rocket::custom(&config).ignite().await?;
/// assert_eq!(rocket.config().port, 7777); /// assert_eq!(rocket.config().port, 7777);
/// assert_eq!(rocket.config().address, Ipv4Addr::new(18, 127, 0, 1)); /// assert_eq!(rocket.config().address, Ipv4Addr::new(18, 127, 0, 1));
/// assert_eq!(rocket.config().temp_dir, Path::new("/tmp/config-example")); /// assert_eq!(rocket.config().temp_dir.relative(), Path::new("/tmp/config-example"));
/// ///
/// // Create a new figment which modifies _some_ keys the existing figment: /// // Create a new figment which modifies _some_ keys the existing figment:
/// let figment = rocket.figment().clone() /// let figment = rocket.figment().clone()
@ -203,7 +203,7 @@ impl Rocket<Build> {
/// ///
/// assert_eq!(rocket.config().port, 8888); /// assert_eq!(rocket.config().port, 8888);
/// assert_eq!(rocket.config().address, Ipv4Addr::new(171, 64, 200, 10)); /// assert_eq!(rocket.config().address, Ipv4Addr::new(171, 64, 200, 10));
/// assert_eq!(rocket.config().temp_dir, Path::new("/tmp/config-example")); /// assert_eq!(rocket.config().temp_dir.relative(), Path::new("/tmp/config-example"));
/// # Ok(()) /// # Ok(())
/// # }); /// # });
/// ``` /// ```

View File

@ -1,8 +1,6 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::env;
use rocket::{Request, Route, Catcher, route, catcher}; use rocket::{Request, Route, Catcher, route, catcher};
use rocket::data::{Data, ToByteUnit}; use rocket::data::{Data, ToByteUnit};
use rocket::http::{Status, Method::{Get, Post}}; use rocket::http::{Status, Method::{Get, Post}};
@ -43,7 +41,8 @@ fn upload<'r>(req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> {
return route::Outcome::failure(Status::BadRequest); return route::Outcome::failure(Status::BadRequest);
} }
let file = File::create(env::temp_dir().join("upload.txt")).await; let path = req.rocket().config().temp_dir.relative().join("upload.txt");
let file = File::create(path).await;
if let Ok(file) = file { if let Ok(file) = file {
if let Ok(n) = data.open(2.mebibytes()).stream_to(file).await { if let Ok(n) = data.open(2.mebibytes()).stream_to(file).await {
return route::Outcome::from(req, format!("OK: {} bytes uploaded.", n)); return route::Outcome::from(req, format!("OK: {} bytes uploaded.", n));
@ -59,7 +58,8 @@ fn upload<'r>(req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> {
} }
fn get_upload<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { fn get_upload<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> {
route::Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()).pin() let path = req.rocket().config().temp_dir.relative().join("upload.txt");
route::Outcome::from(req, std::fs::File::open(path).ok()).pin()
} }
fn not_found_handler<'r>(_: Status, req: &'r Request) -> catcher::BoxFuture<'r> { fn not_found_handler<'r>(_: Status, req: &'r Request) -> catcher::BoxFuture<'r> {

View File

@ -6,10 +6,10 @@
use std::{io, env}; use std::{io, env};
use rocket::tokio::fs; use rocket::Config;
use rocket::data::Capped; use rocket::data::Capped;
use rocket::fs::{NamedFile, TempFile}; use rocket::fs::{NamedFile, TempFile};
use rocket::tokio::fs;
// Upload your `big_file.dat` by POSTing it to /upload. // Upload your `big_file.dat` by POSTing it to /upload.
// try `curl --data-binary @file.txt http://127.0.0.1:8000/stream/file` // try `curl --data-binary @file.txt http://127.0.0.1:8000/stream/file`
@ -17,19 +17,19 @@ const FILENAME: &str = "big_file.dat";
// This is a *raw* file upload, _not_ a multipart upload! // This is a *raw* file upload, _not_ a multipart upload!
#[post("/file", data = "<file>")] #[post("/file", data = "<file>")]
async fn upload(mut file: Capped<TempFile<'_>>) -> io::Result<String> { async fn upload(mut file: Capped<TempFile<'_>>, config: &Config) -> io::Result<String> {
file.persist_to(env::temp_dir().join(FILENAME)).await?; file.persist_to(config.temp_dir.relative().join(FILENAME)).await?;
Ok(format!("{} bytes at {}", file.n.written, file.path().unwrap().display())) Ok(format!("{} bytes at {}", file.n.written, file.path().unwrap().display()))
} }
#[get("/file")] #[get("/file")]
async fn file() -> Option<NamedFile> { async fn file(config: &Config) -> Option<NamedFile> {
NamedFile::open(env::temp_dir().join(FILENAME)).await.ok() NamedFile::open(config.temp_dir.relative().join(FILENAME)).await.ok()
} }
#[delete("/file")] #[delete("/file")]
async fn delete() -> Option<()> { async fn delete(config: &Config) -> Option<()> {
fs::remove_file(env::temp_dir().join(FILENAME)).await.ok() fs::remove_file(config.temp_dir.relative().join(FILENAME)).await.ok()
} }
/***************************** `Stream` Responder *****************************/ /***************************** `Stream` Responder *****************************/