use anyhow::{Context, Error};
use config::Config;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::env;
use std::sync::OnceLock;
pub fn load_config_sources() -> Result<Config, Error> {
let env = env::var("CONFIG_ENV").unwrap_or_else(|_| {
if cfg!(debug_assertions) {
"dev".to_string()
} else {
"prod".to_string()
}
});
Ok(Config::builder()
.set_default("server.host", "0.0.0.0")?
.set_default("server.port", 8080)?
.add_source(config::File::with_name("./config").required(false))
.add_source(config::File::with_name(&format!("./config.{}", env)).required(false))
.add_source(config::Environment::with_prefix("MIKO").separator("__"))
.build()?)
}
static SETTINGS: OnceLock<Config> = OnceLock::new();
pub fn get_settings() -> &'static Config {
SETTINGS.get_or_init(|| {
load_config_sources().expect("Failed to initialize configuration. Check your config files.")
})
}
pub fn get_settings_value<T: DeserializeOwned>(path: &str) -> Result<T, Error> {
let (path, default_val) = match path.rsplit_once(":") {
Some((p, d)) => (p, Some(d)),
None => (path, None),
};
let settings = get_settings();
match settings.get::<T>(path) {
Ok(v) => Ok(v),
Err(config_err) => {
if let Some(def_str) = default_val {
try_parse_default_value(def_str).with_context(|| {
format!(
"Config key '{}' not found, and failed to parse default literal '{}'",
path, def_str
)
})
} else {
Err(config_err.into())
}
}
}
}
fn try_parse_default_value<T: DeserializeOwned>(val: &str) -> Result<T, serde_json::Error> {
let res = serde_json::from_str::<T>(val);
res.or_else(|_| serde_json::from_value(serde_json::Value::String(val.to_string())))
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ServerSettings {
pub host: String,
pub port: u16,
}
impl Default for ServerSettings {
fn default() -> Self {
ServerSettings {
host: "0.0.0.0".to_string(),
port: 8080,
}
}
}
impl ServerSettings {
pub fn from_global_settings() -> Self {
let settings = get_settings();
settings.get("server").unwrap_or_else(|_| {
tracing::warn!("Using default server settings.");
ServerSettings::default()
})
}
}