use std::env;
use serde::{Deserialize, Serialize};
const DEFAULT_DB_PATH: &str = "data/db.redb";
const DEFAULT_SERVER_ADDRESS: &str = "0.0.0.0:3000";
#[cfg(feature = "tls")]
const DEFAULT_TLS_CERT: &str = "tls/cert.pem";
#[cfg(feature = "tls")]
const DEFAULT_TLS_PRIVKEY: &str = "tls/key.pem";
const DEFAULT_RATE_LIMIT_PER_SECOND: u64 = 2;
const DEFAULT_RATE_LIMIT_BURST_SIZE: u32 = 5;
#[derive(Serialize, Deserialize)]
pub struct Config {
pub db: Db,
pub server: Server,
pub rate_limit: RateLimit,
}
#[derive(Serialize, Deserialize)]
pub struct Db {
pub path: String,
}
#[cfg_attr(feature = "tls", doc = "including TLS settings")]
#[derive(Serialize, Deserialize)]
pub struct Server {
pub address: String,
#[cfg(feature = "tls")]
pub cert_path: String,
#[cfg(feature = "tls")]
pub key_path: String,
#[cfg(feature = "tls")]
pub use_tls: bool,
}
impl Config {
pub fn from_env() -> Self {
Self {
db: Db::from_env(),
server: Server::from_env(),
rate_limit: RateLimit::from_env(),
}
}
}
impl Db {
pub fn from_env() -> Self {
let path = env::var("KORROSYNC_DB_PATH").unwrap_or(DEFAULT_DB_PATH.to_string());
Self { path }
}
}
impl Server {
pub fn from_env() -> Self {
let address =
env::var("KORROSYNC_SERVER_ADDRESS").unwrap_or(DEFAULT_SERVER_ADDRESS.to_string());
#[cfg(feature = "tls")]
let cert_path = env::var("KORROSYNC_CERT_PATH").unwrap_or(DEFAULT_TLS_CERT.to_string());
#[cfg(feature = "tls")]
let key_path = env::var("KORROSYNC_KEY_PATH").unwrap_or(DEFAULT_TLS_PRIVKEY.to_string());
#[cfg(feature = "tls")]
let use_tls_str = env::var("KORROSYNC_USE_TLS").unwrap_or("false".to_string());
#[cfg(feature = "tls")]
let use_tls = match use_tls_str.to_lowercase().as_str() {
"true" | "1" | "yes" | "on" => true,
"false" | "0" | "no" | "off" => false,
_ => panic!(
"Invalid boolean value for KORROSYNC_USE_TLS: '{}'. Expected: true/1/yes/on or false/0/no/off",
use_tls_str
),
};
Self {
address,
#[cfg(feature = "tls")]
cert_path,
#[cfg(feature = "tls")]
key_path,
#[cfg(feature = "tls")]
use_tls,
}
}
}
#[derive(Serialize, Deserialize)]
pub struct RateLimit {
pub per_second: u64,
pub burst_size: u32,
}
impl RateLimit {
pub fn from_env() -> Self {
let per_second = env::var("KORROSYNC_RATE_LIMIT_PER_SECOND")
.map(|v| {
v.parse::<u64>().unwrap_or_else(|_| {
panic!(
"Invalid value for KORROSYNC_RATE_LIMIT_PER_SECOND: '{}'. Expected a positive integer",
v
)
})
})
.unwrap_or(DEFAULT_RATE_LIMIT_PER_SECOND);
assert!(
per_second > 0,
"KORROSYNC_RATE_LIMIT_PER_SECOND must be greater than 0"
);
let burst_size = env::var("KORROSYNC_RATE_LIMIT_BURST_SIZE")
.map(|v| {
v.parse::<u32>().unwrap_or_else(|_| {
panic!(
"Invalid value for KORROSYNC_RATE_LIMIT_BURST_SIZE: '{}'. Expected a positive integer",
v
)
})
})
.unwrap_or(DEFAULT_RATE_LIMIT_BURST_SIZE);
assert!(
burst_size > 0,
"KORROSYNC_RATE_LIMIT_BURST_SIZE must be greater than 0"
);
Self {
per_second,
burst_size,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rate_limit_defaults() {
temp_env::with_vars_unset(
vec![
"KORROSYNC_RATE_LIMIT_PER_SECOND",
"KORROSYNC_RATE_LIMIT_BURST_SIZE",
],
|| {
let rate_limit = RateLimit::from_env();
assert_eq!(rate_limit.per_second, 2);
assert_eq!(rate_limit.burst_size, 5);
},
);
}
#[test]
fn rate_limit_custom_values() {
temp_env::with_vars(
vec![
("KORROSYNC_RATE_LIMIT_PER_SECOND", Some("10")),
("KORROSYNC_RATE_LIMIT_BURST_SIZE", Some("20")),
],
|| {
let rate_limit = RateLimit::from_env();
assert_eq!(rate_limit.per_second, 10);
assert_eq!(rate_limit.burst_size, 20);
},
);
}
#[test]
#[should_panic(expected = "Invalid value for KORROSYNC_RATE_LIMIT_PER_SECOND")]
fn rate_limit_invalid_per_second() {
temp_env::with_vars(
vec![("KORROSYNC_RATE_LIMIT_PER_SECOND", Some("abc"))],
|| {
RateLimit::from_env();
},
);
}
#[test]
#[should_panic(expected = "Invalid value for KORROSYNC_RATE_LIMIT_BURST_SIZE")]
fn rate_limit_invalid_burst_size() {
temp_env::with_vars(
vec![("KORROSYNC_RATE_LIMIT_BURST_SIZE", Some("xyz"))],
|| {
RateLimit::from_env();
},
);
}
#[test]
#[should_panic(expected = "KORROSYNC_RATE_LIMIT_PER_SECOND must be greater than 0")]
fn rate_limit_zero_per_second() {
temp_env::with_vars(vec![("KORROSYNC_RATE_LIMIT_PER_SECOND", Some("0"))], || {
RateLimit::from_env();
});
}
#[test]
#[should_panic(expected = "KORROSYNC_RATE_LIMIT_BURST_SIZE must be greater than 0")]
fn rate_limit_zero_burst_size() {
temp_env::with_vars(vec![("KORROSYNC_RATE_LIMIT_BURST_SIZE", Some("0"))], || {
RateLimit::from_env();
});
}
}