· 6 years ago · Mar 06, 2019, 05:08 PM
1use std::convert::From;
2use std::env;
3
4use num_cpus;
5use rand::{self, Rng};
6use rocket::{self, config::{Environment, LoggingLevel}};
7
8const DEFAULT_SECRETS_PATH: &str = "./secrets";
9const DEFAULT_SERVER_ADDRESS: &str = "0.0.0.0";
10const DEFAULT_SERVER_PORT: u16 = 8000;
11const DEFAULT_KEEP_ALIVE: u32 = 5;
12const DEFAULT_ENVIRONMENT: Environment = Environment::Development;
13const DEFAULT_LOGGING_LEVEL: LoggingLevel = LoggingLevel::Normal;
14
15macro_rules! env_name {
16 ($name:expr) => {
17 concat!("__", $name)
18 };
19 }
20
21#[derive(Debug)]
22pub struct Config {
23 pub secrets_path: String,
24 pub salt: String,
25
26 pub environment: Environment,
27 pub log_level: LoggingLevel,
28 pub address: String,
29 pub port: u16,
30 pub keep_alive: u32,
31 pub workers: u16,
32 pub secret_key: String,
33}
34
35impl Default for Config {
36 fn default() -> Self {
37 let secret_key: String = rand::thread_rng()
38 .sample_iter(&rand::distributions::Alphanumeric)
39 .take(32)
40 .collect();
41
42 let secret_key = base64::encode(&secret_key);
43
44 Self {
45 secrets_path: String::from(DEFAULT_SECRETS_PATH),
46 salt: String::new(),
47
48 environment: DEFAULT_ENVIRONMENT,
49 log_level: DEFAULT_LOGGING_LEVEL,
50 keep_alive: DEFAULT_KEEP_ALIVE,
51 address: String::from(DEFAULT_SERVER_ADDRESS),
52 port: DEFAULT_SERVER_PORT,
53 workers: (num_cpus::get() * 2) as u16,
54 secret_key,
55 }
56 }
57}
58
59impl Config {
60 pub fn new() -> Self {
61 Default::default()
62 }
63
64 fn parse_environment(env: &str) -> Option<Environment> {
65 match env {
66 "development" | "dev" => Some(Environment::Development),
67 "production" | "prod" => Some(Environment::Production),
68 "staging" | "stag" => Some(Environment::Staging),
69 _ => None,
70 }
71 }
72
73 fn parse_log_level(log_level: &str) -> Option<LoggingLevel> {
74 match log_level.to_lowercase().as_str() {
75 "off" | "0" | "none" => Some(LoggingLevel::Off),
76 "normal" | "1" => Some(LoggingLevel::Normal),
77 "critical" | "2" => Some(LoggingLevel::Critical),
78 "debug" | "3" => Some(LoggingLevel::Debug),
79 _ => None,
80 }
81 }
82
83 fn emit_warnings_maybe(&self) {
84 if self.salt.is_empty() {
85 println!("You didn't provide salt. It's not safe to do so.");
86 }
87 }
88}
89
90impl From<env::Vars> for Config {
91 fn from(vars: env::Vars) -> Self {
92 let mut config = Self::new();
93
94 for (key, value) in vars {
95 match key.as_str() {
96 env_name!("SECRETS_PATH") => config.secrets_path = value,
97 env_name!("SALT") => config.salt = value,
98 env_name!("KEEP_ALIVE") => {
99 if let Ok(n) = value.parse::<u32>() {
100 config.keep_alive = n;
101 }
102 }
103 env_name!("ENVIRONMENT") => {
104 if let Some(env) = Config::parse_environment(&value) {
105 config.environment = env;
106 }
107 }
108 env_name!("LOG_LEVEL") => {
109 if let Some(log_level) = Config::parse_log_level(&value) {
110 config.log_level = log_level;
111 }
112 }
113 _ => {}
114 }
115 }
116
117 config.emit_warnings_maybe();
118
119 config
120 }
121}
122
123impl From<Config> for rocket::Config {
124 fn from(c: Config) -> Self {
125 Self::build(c.environment)
126 .address(c.address)
127 .port(c.port)
128 .workers(c.workers)
129 .secret_key(c.secret_key)
130 .log_level(c.log_level)
131 .keep_alive(c.keep_alive)
132 .unwrap()
133 }
134}