Document Config. Cleaner lib/handler docs.

This commit is contained in:
Sergio Benitez 2016-12-19 16:51:59 -08:00
parent 123c684f62
commit 8d8d504b59
6 changed files with 101 additions and 26 deletions

View File

@ -22,7 +22,7 @@ pub struct Config {
pub env: Environment, pub env: Environment,
session_key: RwLock<Option<String>>, session_key: RwLock<Option<String>>,
extras: HashMap<String, Value>, extras: HashMap<String, Value>,
filename: String, filepath: String,
} }
macro_rules! parse { macro_rules! parse {
@ -34,10 +34,13 @@ macro_rules! parse {
} }
impl Config { impl Config {
pub fn default_for(env: Environment, filename: &str) -> config::Result<Config> { /// Returns the default configuration for the environment `env` given that
let file_path = Path::new(filename); /// the configuration was stored at `filepath`. If `filepath` is not an
/// absolute path, an `Err` of `ConfigError::BadFilePath` is returned.
pub fn default_for(env: Environment, filepath: &str) -> config::Result<Config> {
let file_path = Path::new(filepath);
if file_path.parent().is_none() { if file_path.parent().is_none() {
return Err(ConfigError::BadFilePath(filename.to_string(), return Err(ConfigError::BadFilePath(filepath.to_string(),
"Configuration files must be rooted in a directory.")); "Configuration files must be rooted in a directory."));
} }
@ -50,7 +53,7 @@ impl Config {
session_key: RwLock::new(None), session_key: RwLock::new(None),
extras: HashMap::new(), extras: HashMap::new(),
env: env, env: env,
filename: filename.to_string(), filepath: filepath.to_string(),
} }
} }
Staging => { Staging => {
@ -61,7 +64,7 @@ impl Config {
session_key: RwLock::new(None), session_key: RwLock::new(None),
extras: HashMap::new(), extras: HashMap::new(),
env: env, env: env,
filename: filename.to_string(), filepath: filepath.to_string(),
} }
} }
Production => { Production => {
@ -72,18 +75,34 @@ impl Config {
session_key: RwLock::new(None), session_key: RwLock::new(None),
extras: HashMap::new(), extras: HashMap::new(),
env: env, env: env,
filename: filename.to_string(), filepath: filepath.to_string(),
} }
} }
}) })
} }
/// Constructs a `BadType` error given the entry `name`, the invalid `val`
/// at that entry, and the `expect`ed type name.
#[inline(always)] #[inline(always)]
fn bad_type(&self, name: &str, val: &Value, expect: &'static str) -> ConfigError { fn bad_type(&self, name: &str, val: &Value, expect: &'static str) -> ConfigError {
let id = format!("{}.{}", self.env, name); let id = format!("{}.{}", self.env, name);
ConfigError::BadType(id, expect, val.type_str(), self.filename.clone()) ConfigError::BadType(id, expect, val.type_str(), self.filepath.clone())
} }
/// Sets the configuration `val` for the `name` entry. If the `name` is one
/// of "address", "port", "session_key", or "log" (the "default" values),
/// the appropriate value in the `self` Config structure is set. Otherwise,
/// the value is stored as an `extra`.
///
/// For each of the default values, the following `Value` variant is
/// expected. If a different variant is supplied, a `BadType` `Err` is
/// returned:
///
/// * **address**: String
/// * **port**: Integer
/// * **session_key**: String (192-bit base64)
/// * **log**: String
///
pub fn set(&mut self, name: &str, val: &Value) -> config::Result<()> { pub fn set(&mut self, name: &str, val: &Value) -> config::Result<()> {
if name == "address" { if name == "address" {
let address_str = parse!(self, name, val, as_str, "a string")?; let address_str = parse!(self, name, val, as_str, "a string")?;
@ -122,76 +141,124 @@ impl Config {
Ok(()) Ok(())
} }
/// Moves the session key string out of the `self` Config, if there is one.
/// Because the value is moved out, subsequent calls will result in a return
/// value of `None`.
///
/// # Example
///
/// ```rust
/// use rocket::config::{Config, Environment, Value};
///
/// // Create a new config with a session key.
/// let key = "adL5fFIPmZBrlyHk2YT4NLV3YCk2gFXz".to_string();
/// let config = Config::default_for(Environment::Staging, "/custom").unwrap()
/// .session_key(key.clone());
///
/// // Get the key for the first time.
/// let session_key = config.take_session_key();
/// assert_eq!(session_key, Some(key.clone()));
///
/// // Try to get the key again.
/// let session_key_again = config.take_session_key();
/// assert_eq!(session_key_again, None);
/// ```
#[inline(always)] #[inline(always)]
pub fn take_session_key(&self) -> Option<String> { pub fn take_session_key(&self) -> Option<String> {
let mut key = self.session_key.write().expect("couldn't lock session key"); let mut key = self.session_key.write().expect("couldn't lock session key");
key.take() key.take()
} }
/// Returns an iterator over the names and values of all of the extras in
/// the `self` Config.
#[inline(always)] #[inline(always)]
pub fn extras<'a>(&'a self) -> impl Iterator<Item=(&'a String, &'a Value)> { pub fn extras<'a>(&'a self) -> impl Iterator<Item=(&'a str, &'a Value)> {
self.extras.iter() self.extras.iter().map(|(k, v)| (k.as_str(), v))
} }
/// Attempts to retrieve the extra named `name` as a string. If an extra
/// with that name doesn't exist, returns an `Err` of `NotFound`. If an
/// extra with that name does exist but is not a string, returns a `BadType`
/// error.
pub fn get_str<'a>(&'a self, name: &str) -> config::Result<&'a str> { pub fn get_str<'a>(&'a self, name: &str) -> config::Result<&'a str> {
let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?; let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_str, "a string") parse!(self, name, value, as_str, "a string")
} }
/// Attempts to retrieve the extra named `name` as an integer. If an extra
/// with that name doesn't exist, returns an `Err` of `NotFound`. If an
/// extra with that name does exist but is not an integer, returns a
/// `BadType` error.
pub fn get_int(&self, name: &str) -> config::Result<i64> { pub fn get_int(&self, name: &str) -> config::Result<i64> {
let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?; let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_integer, "an integer") parse!(self, name, value, as_integer, "an integer")
} }
/// Attempts to retrieve the extra named `name` as a boolean. If an extra
/// with that name doesn't exist, returns an `Err` of `NotFound`. If an
/// extra with that name does exist but is not a boolean, returns a
/// `BadType` error.
pub fn get_bool(&self, name: &str) -> config::Result<bool> { pub fn get_bool(&self, name: &str) -> config::Result<bool> {
let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?; let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_bool, "a boolean") parse!(self, name, value, as_bool, "a boolean")
} }
/// Attempts to retrieve the extra named `name` as a float. If an extra
/// with that name doesn't exist, returns an `Err` of `NotFound`. If an
/// extra with that name does exist but is not a float, returns a
/// `BadType` error.
pub fn get_float(&self, name: &str) -> config::Result<f64> { pub fn get_float(&self, name: &str) -> config::Result<f64> {
let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?; let value = self.extras.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_float, "a float") parse!(self, name, value, as_float, "a float")
} }
/// Returns the path at which the configuration file for `self` is stored.
/// For instance, if the configuration file is at `/tmp/Rocket.toml`, the
/// path `/tmp` is returned.
pub fn root(&self) -> &Path { pub fn root(&self) -> &Path {
match Path::new(self.filename.as_str()).parent() { match Path::new(self.filepath.as_str()).parent() {
Some(parent) => parent, Some(parent) => parent,
None => panic!("root(): filename {} has no parent", self.filename) None => panic!("root(): filepath {} has no parent", self.filepath)
} }
} }
// Builder pattern below, mostly for testing. /// Sets the `address` in `self` to `var` and returns the structure.
#[inline(always)] #[inline(always)]
pub fn address(mut self, var: String) -> Self { pub fn address(mut self, var: String) -> Self {
self.address = var; self.address = var;
self self
} }
/// Sets the `port` in `self` to `var` and returns the structure.
#[inline(always)] #[inline(always)]
pub fn port(mut self, var: usize) -> Self { pub fn port(mut self, var: usize) -> Self {
self.port = var; self.port = var;
self self
} }
/// Sets the `log_level` in `self` to `var` and returns the structure.
#[inline(always)] #[inline(always)]
pub fn log_level(mut self, var: LoggingLevel) -> Self { pub fn log_level(mut self, var: LoggingLevel) -> Self {
self.log_level = var; self.log_level = var;
self self
} }
/// Sets the `session_key` in `self` to `var` and returns the structure.
#[inline(always)] #[inline(always)]
pub fn session_key(mut self, var: String) -> Self { pub fn session_key(mut self, var: String) -> Self {
self.session_key = RwLock::new(Some(var)); self.session_key = RwLock::new(Some(var));
self self
} }
/// Sets the `env` in `self` to `var` and returns the structure.
#[inline(always)] #[inline(always)]
pub fn env(mut self, var: Environment) -> Self { pub fn env(mut self, var: Environment) -> Self {
self.env = var; self.env = var;
self self
} }
/// Adds an extra configuration parameter with `name` and `value` to `self`
/// and returns the structure.
#[inline(always)] #[inline(always)]
pub fn extra(mut self, name: &str, value: &Value) -> Self { pub fn extra(mut self, name: &str, value: &Value) -> Self {
self.extras.insert(name.into(), value.clone()); self.extras.insert(name.into(), value.clone());
@ -214,7 +281,7 @@ impl PartialEq for Config {
&& self.log_level == other.log_level && self.log_level == other.log_level
&& self.env == other.env && self.env == other.env
&& self.extras == other.extras && self.extras == other.extras
&& self.filename == other.filename && self.filepath == other.filepath
} }
} }

View File

@ -137,7 +137,9 @@ pub use self::environment::Environment;
pub use self::config::Config; pub use self::config::Config;
use self::Environment::*; use self::Environment::*;
use toml::{self, Table}; use toml;
pub use toml::{Array, Table, Value};
use logger::{self, LoggingLevel}; use logger::{self, LoggingLevel};
static INIT: Once = ONCE_INIT; static INIT: Once = ONCE_INIT;

View File

@ -1,3 +1,5 @@
//! The types of request and error handlers and their return values.
use data::Data; use data::Data;
use request::Request; use request::Request;
use response::{self, Response, Responder}; use response::{self, Response, Responder};

View File

@ -13,7 +13,7 @@
//! as well as a [full, detailed guide](https://rocket.rs/guide). If you'd like //! as well as a [full, detailed guide](https://rocket.rs/guide). If you'd like
//! pointers on getting started, see the //! pointers on getting started, see the
//! [quickstart](https://rocket.rs/guide/quickstart) or [getting //! [quickstart](https://rocket.rs/guide/quickstart) or [getting
//! started](https://rocket.rs/guide/getting_started) chapters of the guide. //! started](https://rocket.rs/guide/getting-started) chapters of the guide.
//! //!
//! You may also be interested in looking at the [contrib API //! You may also be interested in looking at the [contrib API
//! documentation](../rocket_contrib), which contains JSON and templating //! documentation](../rocket_contrib), which contains JSON and templating
@ -42,10 +42,10 @@
//! rocket_codegen = "*" //! rocket_codegen = "*"
//! ``` //! ```
//! //!
//! If you'll be deploying your project to Crates.io, you'll need to change the //! If you'll be deploying your project to [crates.io](https://crates.io),
//! "*" to the current version of Rocket. //! you'll need to change the "*" to the current version of Rocket.
//! //!
//! Then, add the following to top of your `main.rs` file: //! Then, add the following to the top of your `main.rs` file:
//! //!
//! ```rust //! ```rust
//! #![feature(plugin)] //! #![feature(plugin)]
@ -79,14 +79,14 @@
//! //!
//! Rocket and Rocket libraries are configured via the `Rocket.toml` file. For //! Rocket and Rocket libraries are configured via the `Rocket.toml` file. For
//! more information on how to configure Rocket, see the [configuration //! more information on how to configure Rocket, see the [configuration
//! section](/guide/configuration) of the guide as well as the [config](config) //! section](https://rocket.rs/guide/getting-started/#configuration) of the
//! module documentation. //! guide as well as the [config](config) module documentation.
//! //!
//! ## Testing //! ## Testing
//! //!
//! Rocket includes a small testing library that can be used to test your Rocket //! Rocket includes a small testing library that can be used to test your Rocket
//! application. The library's API is unstable. For information on how to test //! application. For information on how to test your Rocket applications, the
//! your Rocket applications, the [testing module](testing) documentation. //! [testing module](testing) documentation.
//! //!
#[macro_use] extern crate log; #[macro_use] extern crate log;

View File

@ -155,6 +155,7 @@ impl Rocket {
} }
#[doc(hidden)] #[doc(hidden)]
#[inline(always)]
pub fn dispatch<'r>(&self, request: &'r mut Request, data: Data) -> Response<'r> { pub fn dispatch<'r>(&self, request: &'r mut Request, data: Data) -> Response<'r> {
// Do a bit of preprocessing before routing. // Do a bit of preprocessing before routing.
self.preprocess_request(request, &data); self.preprocess_request(request, &data);
@ -173,7 +174,9 @@ impl Rocket {
// Rust thinks `request` is still borrowed here, but it's // Rust thinks `request` is still borrowed here, but it's
// obviously not (data has nothing to do with it), so we // obviously not (data has nothing to do with it), so we
// convince it to give us another mutable reference. // convince it to give us another mutable reference.
// FIXME: Pay the cost to copy Request into UnsafeCell? // FIXME: Pay the cost to copy Request into UnsafeCell? Pay the
// cost to use RefCell? Move the call to `issue_response` here
// to move Request and move directly into a RefCell?
let request: &'r mut Request = unsafe { let request: &'r mut Request = unsafe {
&mut *(request as *const Request as *mut Request) &mut *(request as *const Request as *mut Request)
}; };
@ -198,7 +201,7 @@ impl Rocket {
/// returns a failure, or there are no matching handlers willing to accept /// returns a failure, or there are no matching handlers willing to accept
/// the request, this function returns an `Err` with the status code. /// the request, this function returns an `Err` with the status code.
#[doc(hidden)] #[doc(hidden)]
#[inline] #[inline(always)]
pub fn route<'r>(&self, request: &'r Request, mut data: Data) pub fn route<'r>(&self, request: &'r Request, mut data: Data)
-> handler::Outcome<'r> { -> handler::Outcome<'r> {
// Go through the list of matching routes until we fail or succeed. // Go through the list of matching routes until we fail or succeed.

View File

@ -42,6 +42,7 @@ impl Router {
.filter(|r| r.collides_with(req)) .filter(|r| r.collides_with(req))
.collect(); .collect();
// FIXME: Presort vector to avoid a sort on each route.
matches.sort_by(|a, b| a.rank.cmp(&b.rank)); matches.sort_by(|a, b| a.rank.cmp(&b.rank));
trace_!("All matches: {:?}", matches); trace_!("All matches: {:?}", matches);
matches matches