use super::{kinds::*, types::*};
use crate::workers::WorkerConfig;
type ConfigBuilder<S = config::builder::DefaultState> = config::builder::ConfigBuilder<S>;
type ConfigResult<T> = core::result::Result<T, config::ConfigError>;
fn fmt_src(dir: impl core::fmt::Display, fname: impl core::fmt::Display) -> String {
format!("{dir}/{fname}")
}
fn set_default(builder: ConfigBuilder) -> ConfigResult<ConfigBuilder> {
let builder = builder
.set_default("mode", "debug")?
.set_default("name", crate::APP_NAME)?
.set_default("version", env!("CARGO_PKG_VERSION"))?
.set_default("scope.context", ".")?
.set_default("scope.workdir", crate::DEFAULT_WORKDIR)?
.set_default("network.address.host", crate::DEFAULT_HOST)?
.set_default("network.address.port", crate::DEFAULT_PORT)?
.set_default("services.tracing.level", "info")?;
Ok(builder)
}
fn add_sources(builder: ConfigBuilder) -> ConfigBuilder {
let workdir =
std::env::var("PZZLD_CONFIG").unwrap_or_else(|_| crate::DEFAULT_CONFIG_DIR.to_string());
let fname = std::env::var("PZZLD_SETTINGS").unwrap_or_else(|_| "Puzzled.toml".to_string());
builder
.add_source(config::Environment::with_prefix("PZZLD").separator("_"))
.add_source(config::File::with_name(&fmt_src(&workdir, "default.config")).required(false))
.add_source(config::File::with_name(&fmt_src(&workdir, "server.config")).required(false))
.add_source(config::File::with_name(&fmt_src(&workdir, "docker.config")).required(false))
.add_source(config::File::with_name(&fmt_src(&workdir, &fname)).required(false))
.add_source(config::File::with_name(&fname).required(false))
.add_source(config::File::with_name(&fmt_src(&workdir, "prod.config")).required(false))
}
fn set_overrides(builder: ConfigBuilder) -> ConfigResult<ConfigBuilder> {
Ok(builder
.set_override_option("mode", std::env::var("RUNMODE").ok())?
.set_override_option("name", std::env::var("APP_NAME").ok())?
.set_override_option("network.address.host", std::env::var("HOSTNAME").ok())?
.set_override_option("network.address.port", std::env::var("HOSTPORT").ok())?
.set_override_option("scope.context", std::env::var("PZZLD_CONTEXT").ok())?
.set_override_option("scope.workdir", std::env::var("PZZLD_WORKDIR").ok())?)
}
#[derive(
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(default)]
pub struct Settings {
pub mode: Mode,
pub name: String,
pub network: NetworkConfig,
pub scope: Scope,
pub services: ServicesConfig,
pub version: String,
pub worker: Vec<WorkerConfig>,
}
impl Settings {
pub fn new(mode: Mode) -> Self {
Self {
mode,
name: crate::APP_NAME.to_string(),
network: NetworkConfig::default(),
scope: Scope::default(),
services: ServicesConfig::default(),
version: env!("CARGO_PKG_VERSION").to_string(),
worker: Vec::new(),
}
}
pub fn build() -> Result<Self, config::ConfigError> {
Self::builder_base()?.build()?.try_deserialize()
}
pub async fn bind(&self) -> std::io::Result<tokio::net::TcpListener> {
self.network().bind().await
}
pub fn init_tracing(&self) {
self.services().tracing().init_tracing(crate::APP_NAME);
}
pub fn debug(self) -> Self {
Self {
mode: Mode::debug(),
..self
}
}
pub fn release(self) -> Self {
Self {
mode: Mode::release(),
..self
}
}
getter! {
mode: Mode,
name: String,
network: NetworkConfig,
scope: Scope,
services: ServicesConfig,
version: String,
}
setwith! {
mode: Mode,
name: String,
scope: Scope,
network: NetworkConfig,
services: ServicesConfig,
version: String,
}
pub fn workers(&self) -> &[WorkerConfig] {
&self.worker
}
pub fn workers_mut(&mut self) -> &mut Vec<WorkerConfig> {
&mut self.worker
}
pub fn push_worker(&mut self, worker: WorkerConfig) {
self.worker.push(worker)
}
pub fn set_workdir(&mut self, workdir: impl ToString) {
self.scope_mut().set_workdir(workdir);
}
pub fn set_some_workdir(&mut self, workdir: Option<impl ToString>) {
self.scope_mut().set_some_workdir(workdir);
}
pub fn set_scope_context(&mut self, context: impl ToString) {
self.scope_mut().set_context(context);
}
pub fn set_port(&mut self, port: u16) {
self.network_mut().set_port(port);
}
pub fn set_log_level(&mut self, level: LogLevel) {
self.services_mut().tracing_mut().set_level(level);
}
fn builder_base() -> ConfigResult<ConfigBuilder> {
let mut builder = config::Config::builder();
builder = set_default(builder)?;
builder = add_sources(builder);
builder = set_overrides(builder)?;
Ok(builder)
}
}
impl Default for Settings {
fn default() -> Self {
Self::new(Mode::Debug)
}
}