1
use std::{env, fmt};
2

            
3
use config::{Config, ConfigError, Environment, File};
4
use lazy_static::lazy_static;
5
use serde::Deserialize;
6

            
7
lazy_static! {
8
    pub static ref SETTINGS: Settings = Settings::new().expect("Failed to setup settings");
9
}
10

            
11
#[derive(Debug, Clone, Deserialize)]
12
pub struct Server {
13
    pub port: u16,
14
}
15

            
16
#[derive(Debug, Clone, Deserialize)]
17
pub struct Logger {
18
    pub level: String,
19
}
20

            
21
#[derive(Debug, Clone, Deserialize)]
22
pub struct Auth {
23
    pub secret: String,
24
}
25

            
26
#[derive(Debug, Clone, Deserialize)]
27
pub struct Settings {
28
    pub environment: String,
29
    pub server: Server,
30
    pub logger: Logger,
31
    pub auth: Auth,
32
}
33

            
34
impl Settings {
35
    pub fn new() -> Result<Self, ConfigError> {
36
        let run_mode = env::var("RUN_MODE").unwrap_or_else(|_| "development".into());
37

            
38
        let mut builder = Config::builder()
39
            .add_source(File::with_name("config/default"))
40
            .add_source(File::with_name(&format!("config/{run_mode}")).required(false))
41
            .add_source(File::with_name("config/local").required(false))
42
            .add_source(Environment::default().separator("__"));
43

            
44
        // Some cloud services like Heroku exposes a randomly assigned port in
45
        // the PORT env var and there is no way to change the env var name.
46
        if let Ok(port) = env::var("PORT") {
47
            builder = builder.set_override("server.port", port)?;
48
        }
49

            
50
        builder
51
            .build()?
52
            // Deserialize (and thus freeze) the entire configuration.
53
            .try_deserialize()
54
    }
55
}
56

            
57
impl fmt::Display for Server {
58
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59
        write!(f, "http://localhost:{}", &self.port)
60
    }
61
}