mirror of https://github.com/rwf2/Rocket.git
Add `config::get()`, for global config access. Use it for `Template`.
This commit is contained in:
parent
722f613686
commit
1323e7a420
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
address = "localhost"
|
||||
port = 8000
|
||||
log = "normal"
|
||||
hi = "Hello!"
|
||||
is_extra = true
|
||||
|
||||
[staging]
|
||||
address = "0.0.0.0"
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue