Add `config::get()`, for global config access. Use it for `Template`.

This commit is contained in:
Sergio Benitez 2016-10-14 18:57:36 -07:00
parent 722f613686
commit 1323e7a420
9 changed files with 320 additions and 176 deletions

View File

@ -16,7 +16,7 @@ use self::glob::glob;
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use rocket::Rocket;
use rocket::config;
use rocket::response::{Content, ResponseOutcome, Responder};
use rocket::http::hyper::FreshHyperResponse;
use rocket::http::{ContentType, StatusCode};
@ -114,11 +114,22 @@ pub struct TemplateInfo {
data_type: Option<String>
}
const DEFAULT_TEMPLATE_DIR: &'static str = "templates";
lazy_static! {
static ref TEMPLATES: HashMap<String, TemplateInfo> = discover_templates();
// FIXME: Ensure template_dir is an absolute path starting at crate root.
static ref TEMPLATE_DIR: String =
Rocket::config("template_dir").unwrap_or("templates").to_string();
static ref TEMPLATE_DIR: String = {
config::active().map(|config| {
let dir = config.get_str("template_dir").map_err(|e| {
if !e.is_not_found() {
e.pretty_print();
warn_!("Using default directory '{}'", DEFAULT_TEMPLATE_DIR);
}
}).unwrap_or(DEFAULT_TEMPLATE_DIR);
config.root().join(dir).to_string_lossy().into_owned()
}).unwrap_or("templates".to_string())
};
}
impl Template {
@ -133,6 +144,7 @@ impl Template {
let template = TEMPLATES.get(name);
if template.is_none() {
error_!("Template '{}' does not exist.", name);
info_!("Searched in '{}'.", *TEMPLATE_DIR);
return Template(None, None);
}

View File

@ -5,6 +5,8 @@
address = "localhost"
port = 8000
log = "normal"
hi = "Hello!"
is_extra = true
[staging]
address = "0.0.0.0"

12
examples/todo/Rocket.toml Normal file
View File

@ -0,0 +1,12 @@
# TODO: Allow a `global` pseudo-environment that overrides all environments.
# [global]
# template_dir = "static"
[dev]
template_dir = "static"
[stage]
template_dir = "static"
[prod]
template_dir = "static"

View File

@ -1,8 +1,9 @@
use std::collections::HashMap;
use std::net::ToSocketAddrs;
use std::path::Path;
use super::Environment::*;
use super::Environment;
use config::Environment::*;
use config::{self, Environment, ConfigError};
use logger::LoggingLevel;
use toml::Value;
@ -13,28 +14,28 @@ pub struct Config {
pub port: usize,
pub log_level: LoggingLevel,
pub session_key: Option<String>,
pub extra: HashMap<String, Value>,
pub env: Environment,
extra: HashMap<String, Value>,
filename: String,
}
macro_rules! parse {
($val:expr, as_str) => (
match $val.as_str() {
Some(v) => v,
None => return Err("a string")
}
);
($val:expr, as_integer) => (
match $val.as_integer() {
Some(v) => v,
None => return Err("an integer")
}
($conf:expr, $name:expr, $val:expr, $method:ident, $expect: expr) => (
$val.$method().ok_or_else(|| {
$conf.bad_type($name, $val, $expect)
})
);
}
impl Config {
pub fn default_for(env: Environment) -> Config {
match env {
pub fn default_for(env: Environment, filename: &str) -> config::Result<Config> {
let file_path = Path::new(filename);
if file_path.parent().is_none() {
return Err(ConfigError::BadFilePath(filename.to_string(),
"Configuration files must be rooted in a directory."));
}
Ok(match env {
Development => {
Config {
address: "localhost".to_string(),
@ -42,6 +43,8 @@ impl Config {
log_level: LoggingLevel::Normal,
session_key: None,
extra: HashMap::new(),
env: env,
filename: filename.to_string(),
}
}
Staging => {
@ -51,6 +54,8 @@ impl Config {
log_level: LoggingLevel::Normal,
session_key: None,
extra: HashMap::new(),
env: env,
filename: filename.to_string(),
}
}
Production => {
@ -60,44 +65,118 @@ impl Config {
log_level: LoggingLevel::Critical,
session_key: None,
extra: HashMap::new(),
env: env,
filename: filename.to_string(),
}
}
}
})
}
pub fn set(&mut self, name: &str, value: &Value) -> Result<(), &'static str> {
#[inline(always)]
fn bad_type(&self, name: &str, val: &Value, expect: &'static str) -> ConfigError {
let id = format!("{}.{}", self.env, name);
ConfigError::BadType(id, expect, val.type_str(), self.filename.clone())
}
pub fn set(&mut self, name: &str, val: &Value) -> config::Result<()> {
if name == "address" {
let address_str = parse!(value, as_str).to_string();
let address_str = parse!(self, name, val, as_str, "a string")?;
if address_str.contains(":") {
return Err("an IP address with no port")
return Err(self.bad_type(name, val, "an IP address with no port"));
} else if format!("{}:{}", address_str, 80).to_socket_addrs().is_err() {
return Err("a valid IP address")
return Err(self.bad_type(name, val, "a valid IP address"));
}
self.address = address_str;
self.address = address_str.to_string();
} else if name == "port" {
let port = parse!(value, as_integer);
let port = parse!(self, name, val, as_integer, "an integer")?;
if port < 0 {
return Err("an unsigned integer");
return Err(self.bad_type(name, val, "an unsigned integer"));
}
self.port = port as usize;
} else if name == "session_key" {
let key = parse!(value, as_str);
let key = parse!(self, name, val, as_str, "a string")?;
if key.len() != 32 {
return Err("a 192-bit base64 encoded string")
return Err(self.bad_type(name, val, "a 192-bit base64 string"));
}
self.session_key = Some(key.to_string());
} else if name == "log" {
self.log_level = match parse!(value, as_str).parse() {
let level_str = parse!(self, name, val, as_str, "a string")?;
self.log_level = match level_str.parse() {
Ok(level) => level,
Err(_) => return Err("log level ('normal', 'critical', 'debug')"),
Err(_) => return Err(self.bad_type(name, val,
"log level ('normal', 'critical', 'debug')"))
};
} else {
self.extra.insert(name.into(), value.clone());
self.extra.insert(name.into(), val.clone());
}
Ok(())
}
pub fn get_str<'a>(&'a self, name: &str) -> config::Result<&'a str> {
let value = self.extra.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_str, "a string")
}
pub fn get_int<'a>(&'a self, name: &str) -> config::Result<i64> {
let value = self.extra.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_integer, "an integer")
}
pub fn get_bool<'a>(&'a self, name: &str) -> config::Result<bool> {
let value = self.extra.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_bool, "a boolean")
}
pub fn get_float<'a>(&'a self, name: &str) -> config::Result<f64> {
let value = self.extra.get(name).ok_or_else(|| ConfigError::NotFound)?;
parse!(self, name, value, as_float, "a float")
}
pub fn root(&self) -> &Path {
match Path::new(self.filename.as_str()).parent() {
Some(parent) => &parent,
None => panic!("root(): filename {} has no parent", self.filename)
}
}
#[inline(always)]
pub fn extras<'a>(&'a self) -> impl Iterator<Item=(&'a String, &'a Value)> {
self.extra.iter()
}
// Builder pattern below, mostly for testing.
#[inline(always)]
pub fn address(mut self, var: String) -> Self {
self.address = var;
self
}
#[inline(always)]
pub fn port(mut self, var: usize) -> Self {
self.port = var;
self
}
#[inline(always)]
pub fn log_level(mut self, var: LoggingLevel) -> Self {
self.log_level = var;
self
}
#[inline(always)]
pub fn session_key(mut self, var: String) -> Self {
self.session_key = Some(var);
self
}
#[inline(always)]
pub fn env(mut self, var: Environment) -> Self {
self.env = var;
self
}
}

View File

@ -16,6 +16,8 @@ pub enum ConfigError {
BadCWD,
NotFound,
IOError,
/// (path, reason)
BadFilePath(String, &'static str),
/// (environment_name)
BadEnv(String),
/// (environment_name, filename)
@ -35,6 +37,10 @@ impl ConfigError {
BadCWD => error!("couldn't get current working directory"),
NotFound => error!("config file was not found"),
IOError => error!("failed reading the config file: IO error"),
BadFilePath(ref path, ref reason) => {
error!("configuration file path '{}' is invalid", path);
info_!("{}", reason);
}
BadEntry(ref name, ref filename) => {
error!("[{}] is not a known configuration environment", name);
info_!("in {}", White.paint(filename));
@ -63,4 +69,12 @@ impl ConfigError {
}
}
}
pub fn is_not_found(&self) -> bool {
use self::ConfigError::*;
match *self {
NotFound => true,
_ => false
}
}
}

View File

@ -17,8 +17,12 @@ use self::config::Config;
use toml::{self, Table};
use logger::{self, LoggingLevel};
static mut CONFIG: Option<RocketConfig> = None;
const CONFIG_FILENAME: &'static str = "Rocket.toml";
pub type Result<T> = ::std::result::Result<T, ConfigError>;
#[derive(Debug, PartialEq, Clone)]
pub struct RocketConfig {
pub active_env: Environment,
@ -28,7 +32,7 @@ pub struct RocketConfig {
impl RocketConfig {
/// Iteratively search for `file` in `pwd` and its parents, returning the path
/// to the file or an Error::NoKey if the file couldn't be found.
fn find() -> Result<PathBuf, ConfigError> {
fn find() -> Result<PathBuf> {
let cwd = env::current_dir().map_err(|_| ConfigError::BadCWD)?;
let mut current = cwd.as_path();
@ -47,26 +51,24 @@ impl RocketConfig {
Err(ConfigError::NotFound)
}
fn set(&mut self, env: Environment, kvs: &Table, filename: &str)
-> Result<(), ConfigError> {
let config = self.config.entry(env).or_insert(Config::default_for(env));
fn set(&mut self, env: Environment, kvs: &Table)
-> Result<()> {
let config = match self.config.get_mut(&env) {
Some(config) => config,
None => panic!("set(): {} config is missing.", env),
};
for (key, value) in kvs {
if let Err(expected) = config.set(key, value) {
let name = format!("{}.{}", env, key);
return Err(ConfigError::BadType(
name, expected, value.type_str(), filename.to_string()
))
}
config.set(key, value)?;
}
Ok(())
}
pub fn get(&self, env: Environment) -> &Config {
if let Some(config) = self.config.get(&env) {
config
} else {
panic!("No value from environment: {:?}", env);
match self.config.get(&env) {
Some(config) => config,
None => panic!("get(): {} config is missing.", env),
}
}
@ -74,7 +76,7 @@ impl RocketConfig {
self.get(self.active_env)
}
fn parse(src: String, filename: &str) -> Result<RocketConfig, ConfigError> {
fn parse(src: String, filename: &str) -> Result<RocketConfig> {
// Parse the source as TOML, if possible.
let mut parser = toml::Parser::new(&src);
let toml = parser.parse().ok_or(ConfigError::ParseError(
@ -88,7 +90,7 @@ impl RocketConfig {
))?;
// Create a config with the defaults, but the set the env to the active
let mut config = RocketConfig::active_default()?;
let mut config = RocketConfig::active_default(filename)?;
// Parse the values from the TOML file.
for (entry, value) in toml {
@ -106,13 +108,13 @@ impl RocketConfig {
};
// Set the environment configuration from the kv pairs.
config.set(env, &kv_pairs, filename)?;
config.set(env, &kv_pairs)?;
}
Ok(config)
}
pub fn read() -> Result<RocketConfig, ConfigError> {
pub fn read() -> Result<RocketConfig> {
// Find the config file, starting from the `cwd` and working backwords.
let file = RocketConfig::find()?;
@ -127,34 +129,39 @@ impl RocketConfig {
RocketConfig::parse(contents, &file.to_string_lossy())
}
pub fn active_default() -> Result<RocketConfig, ConfigError> {
let mut default = RocketConfig::default();
default.active_env = Environment::active()?;
Ok(default)
pub fn active_default(filename: &str) -> Result<RocketConfig> {
let mut defaults = HashMap::new();
defaults.insert(Development, Config::default_for(Development, filename)?);
defaults.insert(Staging, Config::default_for(Staging, filename)?);
defaults.insert(Production, Config::default_for(Production, filename)?);
Ok(RocketConfig {
active_env: Environment::active()?,
config: defaults,
})
}
}
impl Default for RocketConfig {
fn default() -> RocketConfig {
RocketConfig {
active_env: Environment::Development,
config: {
let mut default_config = HashMap::new();
default_config.insert(Development, Config::default_for(Development));
default_config.insert(Staging, Config::default_for(Staging));
default_config.insert(Production, Config::default_for(Production));
default_config
},
}
/// Initializes the global RocketConfig by reading the Rocket config file from
/// the current directory or any of its parents. Returns the active
/// configuration, which is determined by the config env variable. If there as a
/// problem parsing the configuration, the error is printed and the progam is
/// aborted. If there an I/O issue reading the config file, a warning is printed
/// and the default configuration is used. If there is no config file, the
/// default configuration is used.
///
/// # Panics
///
/// If there is a problem, prints a nice error message and bails.
///
/// # Thread-Safety
///
/// This function is _not_ thread-safe. It should be called by a single thread.
pub fn init() -> &'static Config {
if let Some(config) = active() {
return config;
}
}
/// Read the Rocket config file from the current directory or any of its
/// parents. If there is no such file, return the default config. A returned
/// config will have its active environment set to whatever was passed in with
/// the rocket config env variable. If there is a problem doing any of this,
/// print a nice error message and bail.
pub fn read_or_default() -> RocketConfig {
let bail = |e: ConfigError| -> ! {
logger::init(LoggingLevel::Debug);
e.pretty_print();
@ -162,40 +169,54 @@ pub fn read_or_default() -> RocketConfig {
};
use self::ConfigError::*;
RocketConfig::read().unwrap_or_else(|e| {
let config = RocketConfig::read().unwrap_or_else(|e| {
match e {
ParseError(..) | BadEntry(..) | BadEnv(..) | BadType(..) => bail(e),
IOError | BadCWD => warn!("failed reading Rocket.toml. using defaults"),
ParseError(..) | BadEntry(..) | BadEnv(..) | BadType(..)
| BadFilePath(..) => bail(e),
IOError | BadCWD => warn!("Failed reading Rocket.toml. Using defaults."),
NotFound => { /* try using the default below */ }
}
RocketConfig::active_default().unwrap_or_else(|e| bail(e))
})
let default_path = match env::current_dir() {
Ok(path) => path.join(&format!(".{}.{}", "default", CONFIG_FILENAME)),
Err(_) => bail(ConfigError::BadCWD)
};
let filename = default_path.to_string_lossy();
RocketConfig::active_default(&filename).unwrap_or_else(|e| bail(e))
});
unsafe {
CONFIG = Some(config);
CONFIG.as_ref().unwrap().active()
}
}
pub fn active() -> Option<&'static Config> {
unsafe { CONFIG.as_ref().map(|c| c.active()) }
}
#[cfg(test)]
mod test {
use std::env;
use std::collections::HashMap;
use std::sync::Mutex;
use super::{RocketConfig, CONFIG_FILENAME, ConfigError};
use super::environment::CONFIG_ENV;
use super::{RocketConfig, ConfigError};
use super::environment::{Environment, CONFIG_ENV};
use super::Environment::*;
use super::config::Config;
use super::Result;
use ::toml::Value;
use ::logger::LoggingLevel;
const TEST_CONFIG_FILENAME: &'static str = "/tmp/testing/Rocket.toml";
lazy_static! {
static ref ENV_LOCK: Mutex<usize> = Mutex::new(0);
}
macro_rules! check_config {
($rconfig:expr => { $($param:tt)+ }) => (
check_config!($rconfig, Config { $($param)+ })
);
($rconfig:expr, $econfig:expr) => (
match $rconfig {
Ok(config) => assert_eq!(config.active(), &$econfig),
@ -211,6 +232,14 @@ mod test {
);
}
fn active_default() -> Result<RocketConfig> {
RocketConfig::active_default(TEST_CONFIG_FILENAME)
}
fn default_config(env: Environment) -> Config {
Config::default_for(env, TEST_CONFIG_FILENAME).expect("config")
}
#[test]
fn test_defaults() {
// Take the lock so changing the environment doesn't cause races.
@ -218,24 +247,24 @@ mod test {
// First, without an environment. Should get development defaults.
env::remove_var(CONFIG_ENV);
check_config!(RocketConfig::active_default(), Config::default_for(Development));
check_config!(active_default(), default_config(Development));
// Now with an explicit dev environment.
for env in &["development", "dev"] {
env::set_var(CONFIG_ENV, env);
check_config!(RocketConfig::active_default(), Config::default_for(Development));
check_config!(active_default(), default_config(Development));
}
// Now staging.
for env in &["stage", "staging"] {
env::set_var(CONFIG_ENV, env);
check_config!(RocketConfig::active_default(), Config::default_for(Staging));
check_config!(active_default(), default_config(Staging));
}
// Finally, production.
for env in &["prod", "production"] {
env::set_var(CONFIG_ENV, env);
check_config!(RocketConfig::active_default(), Config::default_for(Production));
check_config!(active_default(), default_config(Production));
}
}
@ -247,15 +276,16 @@ mod test {
for env in &["", "p", "pr", "pro", "prodo", " prod", "dev ", "!dev!", "🚀 "] {
env::set_var(CONFIG_ENV, env);
let err = ConfigError::BadEnv(env.to_string());
assert!(RocketConfig::active_default().err().map_or(false, |e| e == err));
assert!(active_default().err().map_or(false, |e| e == err));
}
// Test that a bunch of invalid environment names give the right error.
env::remove_var(CONFIG_ENV);
for env in &["p", "pr", "pro", "prodo", "bad", "meow", "this", "that"] {
let toml_table = format!("[{}]\n", env);
let err = ConfigError::BadEntry(env.to_string(), CONFIG_FILENAME.into());
assert!(RocketConfig::parse(toml_table, CONFIG_FILENAME)
let e_str = env.to_string();
let err = ConfigError::BadEntry(e_str, TEST_CONFIG_FILENAME.into());
assert!(RocketConfig::parse(toml_table, TEST_CONFIG_FILENAME)
.err().map_or(false, |e| e == err));
}
}
@ -276,36 +306,35 @@ mod test {
pi = 3.14
"#;
let mut extra: HashMap<String, Value> = HashMap::new();
extra.insert("template_dir".to_string(), Value::String("mine".into()));
extra.insert("json".to_string(), Value::Boolean(true));
extra.insert("pi".to_string(), Value::Float(3.14));
let expected = Config {
address: "1.2.3.4".to_string(),
port: 7810,
log_level: LoggingLevel::Critical,
session_key: Some("01234567890123456789012345678901".to_string()),
extra: extra
};
let mut expected = default_config(Development);
expected.address = "1.2.3.4".to_string();
expected.port = 7810;
expected.log_level = LoggingLevel::Critical;
expected.session_key = Some("01234567890123456789012345678901".to_string());
expected.set("template_dir", &Value::String("mine".into())).unwrap();
expected.set("json", &Value::Boolean(true)).unwrap();
expected.set("pi", &Value::Float(3.14)).unwrap();
expected.env = Development;
let dev_config = ["[dev]", config_str].join("\n");
let parsed = RocketConfig::parse(dev_config, CONFIG_FILENAME);
let parsed = RocketConfig::parse(dev_config, TEST_CONFIG_FILENAME);
check_config!(Development, parsed, expected);
check_config!(Staging, parsed, Config::default_for(Staging));
check_config!(Production, parsed, Config::default_for(Production));
check_config!(Staging, parsed, default_config(Staging));
check_config!(Production, parsed, default_config(Production));
expected.env = Staging;
let stage_config = ["[stage]", config_str].join("\n");
let parsed = RocketConfig::parse(stage_config, CONFIG_FILENAME);
let parsed = RocketConfig::parse(stage_config, TEST_CONFIG_FILENAME);
check_config!(Staging, parsed, expected);
check_config!(Development, parsed, Config::default_for(Development));
check_config!(Production, parsed, Config::default_for(Production));
check_config!(Development, parsed, default_config(Development));
check_config!(Production, parsed, default_config(Production));
expected.env = Production;
let prod_config = ["[prod]", config_str].join("\n");
let parsed = RocketConfig::parse(prod_config, CONFIG_FILENAME);
let parsed = RocketConfig::parse(prod_config, TEST_CONFIG_FILENAME);
check_config!(Production, parsed, expected);
check_config!(Development, parsed, Config::default_for(Development));
check_config!(Staging, parsed, Config::default_for(Staging));
check_config!(Development, parsed, default_config(Development));
check_config!(Staging, parsed, default_config(Staging));
}
#[test]
@ -317,29 +346,25 @@ mod test {
check_config!(RocketConfig::parse(r#"
[development]
address = "localhost"
"#.to_string(), CONFIG_FILENAME) => {
address: "localhost".to_string(),
..Config::default_for(Development)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Development).address("localhost".into())
});
check_config!(RocketConfig::parse(r#"
[dev]
address = "127.0.0.1"
"#.to_string(), CONFIG_FILENAME) => {
address: "127.0.0.1".to_string(),
..Config::default_for(Development)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Development).address("127.0.0.1".into())
});
check_config!(RocketConfig::parse(r#"
[dev]
address = "0.0.0.0"
"#.to_string(), CONFIG_FILENAME) => {
address: "0.0.0.0".to_string(),
..Config::default_for(Development)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Development).address("0.0.0.0".into())
});
}
#[test]
fn test_bad_address_values() {
// Take the lock so changing the environment doesn't cause races.
@ -349,27 +374,27 @@ mod test {
assert!(RocketConfig::parse(r#"
[development]
address = 0000
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[development]
address = true
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[development]
address = "_idk_"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[staging]
address = "1.2.3.4:100"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[production]
address = "1.2.3.4.5.6"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
}
#[test]
@ -381,17 +406,15 @@ mod test {
check_config!(RocketConfig::parse(r#"
[stage]
port = 100
"#.to_string(), CONFIG_FILENAME) => {
port: 100,
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).port(100)
});
check_config!(RocketConfig::parse(r#"
[stage]
port = 6000
"#.to_string(), CONFIG_FILENAME) => {
port: 6000,
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).port(6000)
});
}
@ -404,17 +427,17 @@ mod test {
assert!(RocketConfig::parse(r#"
[development]
port = true
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[production]
port = "hello"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[staging]
port = -1
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
}
#[test]
@ -426,26 +449,23 @@ mod test {
check_config!(RocketConfig::parse(r#"
[stage]
log = "normal"
"#.to_string(), CONFIG_FILENAME) => {
log_level: LoggingLevel::Normal,
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).log_level(LoggingLevel::Normal)
});
check_config!(RocketConfig::parse(r#"
[stage]
log = "debug"
"#.to_string(), CONFIG_FILENAME) => {
log_level: LoggingLevel::Debug,
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).log_level(LoggingLevel::Debug)
});
check_config!(RocketConfig::parse(r#"
[stage]
log = "critical"
"#.to_string(), CONFIG_FILENAME) => {
log_level: LoggingLevel::Critical,
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).log_level(LoggingLevel::Critical)
});
}
@ -458,17 +478,17 @@ mod test {
assert!(RocketConfig::parse(r#"
[dev]
log = false
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[development]
log = 0
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[prod]
log = "no"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
}
#[test]
@ -480,17 +500,19 @@ mod test {
check_config!(RocketConfig::parse(r#"
[stage]
session_key = "VheMwXIBygSmOlZAhuWl2B+zgvTN3WW5"
"#.to_string(), CONFIG_FILENAME) => {
session_key: Some("VheMwXIBygSmOlZAhuWl2B+zgvTN3WW5".into()),
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).session_key(
"VheMwXIBygSmOlZAhuWl2B+zgvTN3WW5".into()
)
});
check_config!(RocketConfig::parse(r#"
[stage]
session_key = "adL5fFIPmZBrlyHk2YT4NLV3YCk2gFXz"
"#.to_string(), CONFIG_FILENAME) => {
session_key: Some("adL5fFIPmZBrlyHk2YT4NLV3YCk2gFXz".into()),
..Config::default_for(Staging)
"#.to_string(), TEST_CONFIG_FILENAME), {
default_config(Staging).session_key(
"adL5fFIPmZBrlyHk2YT4NLV3YCk2gFXz".into()
)
});
}
@ -503,17 +525,17 @@ mod test {
assert!(RocketConfig::parse(r#"
[dev]
session_key = true
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[dev]
session_key = 1283724897238945234897
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[dev]
session_key = "abcv"
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
}
#[test]
@ -524,16 +546,16 @@ mod test {
assert!(RocketConfig::parse(r#"
[dev
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[dev]
1.2.3 = 2
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
assert!(RocketConfig::parse(r#"
[dev]
session_key = "abcv" = other
"#.to_string(), CONFIG_FILENAME).is_err());
"#.to_string(), TEST_CONFIG_FILENAME).is_err());
}
}

View File

@ -1,6 +1,7 @@
#![feature(question_mark)]
#![feature(specialization)]
#![feature(conservative_impl_trait)]
#![feature(drop_types_in_const)]
//! # Rocket - Core API Documentation
//!
@ -73,13 +74,13 @@ pub mod http;
pub mod request;
pub mod response;
pub mod outcome;
pub mod config;
mod error;
mod router;
mod rocket;
mod codegen;
mod catcher;
mod config;
/// Defines the types for request and error handlers.
#[doc(hidden)]

View File

@ -154,7 +154,7 @@ impl Rocket {
if !responder.respond(response).is_success() {
error_!("Catcher outcome was unsuccessul; aborting response.");
} else {
info_!("Responded with catcher.");
info_!("Responded with {} catcher.", White.paint(code));
}
} else {
error_!("Catcher returned an incomplete response.");
@ -226,21 +226,23 @@ impl Rocket {
}
pub fn ignite() -> Rocket {
// Note: read_or_default will exit the process under errors.
let config = config::read_or_default();
// Note: init() will exit the process under config errors.
let config = config::init();
logger::init(config.active().log_level);
info!("🔧 Configured for {}.", config.active_env);
logger::init(config.log_level);
info!("🔧 Configured for {}.", config.env);
info_!("listening: {}:{}",
White.paint(&config.active().address),
White.paint(&config.active().port));
info_!("logging: {:?}", White.paint(config.active().log_level));
info_!("session key: {}",
White.paint(config.active().session_key.is_some()));
White.paint(&config.address),
White.paint(&config.port));
info_!("logging: {:?}", White.paint(config.log_level));
info_!("session key: {}", White.paint(config.session_key.is_some()));
for (name, value) in config.extras() {
info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
}
Rocket {
address: config.active().address.clone(),
port: config.active().port,
address: config.address.clone(),
port: config.port,
router: Router::new(),
default_catchers: catcher::defaults::get(),
catchers: catcher::defaults::get(),