pzzld_server/config/
settings.rs

1/*
2    Appellation: settings <module>
3    Contrib: FL03 <jo3mccain@icloud.com>
4*/
5use super::{kinds::*, types::*};
6use crate::workers::WorkerConfig;
7
8type ConfigBuilder<S = config::builder::DefaultState> = config::builder::ConfigBuilder<S>;
9
10type ConfigResult<T> = core::result::Result<T, config::ConfigError>;
11
12fn fmt_src(dir: impl core::fmt::Display, fname: impl core::fmt::Display) -> String {
13    format!("{dir}/{fname}")
14}
15
16fn set_default(builder: ConfigBuilder) -> ConfigResult<ConfigBuilder> {
17    let builder = builder
18        .set_default("mode", "debug")?
19        .set_default("name", crate::APP_NAME)?
20        .set_default("version", env!("CARGO_PKG_VERSION"))?
21        .set_default("scope.context", ".")?
22        .set_default("scope.workdir", crate::DEFAULT_WORKDIR)?
23        .set_default("network.address.host", crate::DEFAULT_HOST)?
24        .set_default("network.address.port", crate::DEFAULT_PORT)?
25        .set_default("services.tracing.level", "info")?;
26    Ok(builder)
27}
28
29fn add_sources(builder: ConfigBuilder) -> ConfigBuilder {
30    let workdir =
31        std::env::var("PZZLD_CONFIG").unwrap_or_else(|_| crate::DEFAULT_CONFIG_DIR.to_string());
32    let fname = std::env::var("PZZLD_SETTINGS").unwrap_or_else(|_| "Puzzled.toml".to_string());
33
34    builder
35        .add_source(config::Environment::with_prefix("PZZLD").separator("_"))
36        .add_source(config::File::with_name(&fmt_src(&workdir, "default.config")).required(false))
37        .add_source(config::File::with_name(&fmt_src(&workdir, "server.config")).required(false))
38        .add_source(config::File::with_name(&fmt_src(&workdir, "docker.config")).required(false))
39        .add_source(config::File::with_name(&fmt_src(&workdir, &fname)).required(false))
40        .add_source(config::File::with_name(&fname).required(false))
41        .add_source(config::File::with_name(&fmt_src(&workdir, "prod.config")).required(false))
42}
43
44fn set_overrides(builder: ConfigBuilder) -> ConfigResult<ConfigBuilder> {
45    Ok(builder
46        .set_override_option("mode", std::env::var("RUNMODE").ok())?
47        .set_override_option("name", std::env::var("APP_NAME").ok())?
48        .set_override_option("network.address.host", std::env::var("HOSTNAME").ok())?
49        .set_override_option("network.address.port", std::env::var("HOSTPORT").ok())?
50        .set_override_option("scope.context", std::env::var("PZZLD_CONTEXT").ok())?
51        .set_override_option("scope.workdir", std::env::var("PZZLD_WORKDIR").ok())?)
52}
53
54#[derive(
55    Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
56)]
57#[serde(default)]
58pub struct Settings {
59    pub mode: Mode,
60    pub name: String,
61    pub network: NetworkConfig,
62    pub scope: Scope,
63    pub services: ServicesConfig,
64    pub version: String,
65    pub worker: Vec<WorkerConfig>,
66}
67
68impl Settings {
69    pub fn new(mode: Mode) -> Self {
70        Self {
71            mode,
72            name: crate::APP_NAME.to_string(),
73            network: NetworkConfig::default(),
74            scope: Scope::default(),
75            services: ServicesConfig::default(),
76            version: env!("CARGO_PKG_VERSION").to_string(),
77            worker: Vec::new(),
78        }
79    }
80
81    pub fn build() -> Result<Self, config::ConfigError> {
82        Self::builder_base()?.build()?.try_deserialize()
83    }
84
85    pub async fn bind(&self) -> std::io::Result<tokio::net::TcpListener> {
86        self.network().bind().await
87    }
88    /// Initialize tracing modules
89    pub fn init_tracing(&self) {
90        self.services().tracing().init_tracing(crate::APP_NAME);
91    }
92    /// Returns a new [Settings] instance with the [Mode] set to [Mode::Debug].
93    pub fn debug(self) -> Self {
94        Self {
95            mode: Mode::debug(),
96            ..self
97        }
98    }
99    /// Returns a new [Settings] instance with the [Mode] set to [Mode::Release].
100    pub fn release(self) -> Self {
101        Self {
102            mode: Mode::release(),
103            ..self
104        }
105    }
106
107    getter! {
108        mode: Mode,
109        name: String,
110        network: NetworkConfig,
111        scope: Scope,
112        services: ServicesConfig,
113        version: String,
114    }
115
116    setwith! {
117        mode: Mode,
118        name: String,
119        scope: Scope,
120        network: NetworkConfig,
121        services: ServicesConfig,
122        version: String,
123    }
124    /// Returns an immutable reference to the worker configuration.
125    pub fn workers(&self) -> &[WorkerConfig] {
126        &self.worker
127    }
128    /// Returns a mutable reference to the worker configuration.
129    pub fn workers_mut(&mut self) -> &mut Vec<WorkerConfig> {
130        &mut self.worker
131    }
132    /// Push a new worker configuration to the worker configuration list.
133    pub fn push_worker(&mut self, worker: WorkerConfig) {
134        self.worker.push(worker)
135    }
136    /// set the working directory of the scope
137    pub fn set_workdir(&mut self, workdir: impl ToString) {
138        self.scope_mut().set_workdir(workdir);
139    }
140    /// if the workdir is set, set it to the given workdir
141    pub fn set_some_workdir(&mut self, workdir: Option<impl ToString>) {
142        self.scope_mut().set_some_workdir(workdir);
143    }
144    /// Set the context of the scope
145    pub fn set_scope_context(&mut self, context: impl ToString) {
146        self.scope_mut().set_context(context);
147    }
148
149    pub fn set_port(&mut self, port: u16) {
150        self.network_mut().set_port(port);
151    }
152
153    pub fn set_log_level(&mut self, level: LogLevel) {
154        self.services_mut().tracing_mut().set_level(level);
155    }
156
157    fn builder_base() -> ConfigResult<ConfigBuilder> {
158        // initialize the builder
159        let mut builder = config::Config::builder();
160        // set defaults
161        builder = set_default(builder)?;
162        // add sources
163        builder = add_sources(builder);
164        // set overrides
165        builder = set_overrides(builder)?;
166        // return the builder
167        Ok(builder)
168    }
169}
170
171impl Default for Settings {
172    fn default() -> Self {
173        Self::new(Mode::Debug)
174    }
175}