use serde::Deserialize;
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(default)]
pub struct Config {
pub server: ServerSection,
pub tls: Option<TlsSection>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct ServerSection {
pub host: String,
pub port: u16,
pub max_body_bytes: usize,
pub request_timeout_ms: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TlsSection {
pub cert: String,
pub key: String,
}
impl Default for ServerSection {
fn default() -> Self {
Self {
host: "127.0.0.1".into(),
port: 8080,
max_body_bytes: 1 << 20,
request_timeout_ms: 30_000,
}
}
}
impl Config {
pub fn load(path: &str) -> Self {
let mut cfg = match std::fs::read_to_string(path) {
Ok(text) => toml::from_str::<Config>(&text).unwrap_or_default(),
Err(_) => Config::default(),
};
cfg.apply_env(|k| std::env::var(k).ok());
cfg
}
pub fn load_default() -> Self {
Self::load("churust.toml")
}
pub fn apply_env(&mut self, get: impl Fn(&str) -> Option<String>) {
if let Some(v) = get("CHURUST_SERVER_HOST") {
self.server.host = v;
}
if let Some(v) = get("CHURUST_SERVER_PORT").and_then(|s| s.parse().ok()) {
self.server.port = v;
}
if let Some(v) = get("CHURUST_SERVER_MAX_BODY_BYTES").and_then(|s| s.parse().ok()) {
self.server.max_body_bytes = v;
}
if let Some(v) = get("CHURUST_SERVER_REQUEST_TIMEOUT_MS").and_then(|s| s.parse().ok()) {
self.server.request_timeout_ms = v;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn defaults_are_sane() {
let c = Config::default();
assert_eq!(c.server.port, 8080);
assert_eq!(c.server.max_body_bytes, 1 << 20);
assert!(c.tls.is_none());
}
#[test]
fn parses_toml() {
let text = r#"
[server]
host = "0.0.0.0"
port = 9090
"#;
let c: Config = toml::from_str(text).unwrap();
assert_eq!(c.server.host, "0.0.0.0");
assert_eq!(c.server.port, 9090);
assert_eq!(c.server.max_body_bytes, 1 << 20);
}
#[test]
fn env_overrides_file() {
let mut c = Config::default();
let env: HashMap<&str, &str> = [("CHURUST_SERVER_PORT", "7000")].into_iter().collect();
c.apply_env(|k| env.get(k).map(|s| s.to_string()));
assert_eq!(c.server.port, 7000);
}
}