use figment::Figment;
use figment::providers::Env;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ConfigError {
#[error("failed to load configuration: {0}")]
Figment(Box<figment::Error>),
}
impl From<figment::Error> for ConfigError {
fn from(err: figment::Error) -> Self {
ConfigError::Figment(Box::new(err))
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ServiceConfig {
pub name: String,
pub environment: String,
pub host: String,
pub port: u16,
pub debug: bool,
pub log_level: String,
}
impl Default for ServiceConfig {
fn default() -> Self {
Self {
name: "sunbeam-g2v".to_string(),
environment: "development".to_string(),
host: "0.0.0.0".to_string(),
port: 8080,
debug: false,
log_level: "info".to_string(),
}
}
}
impl ServiceConfig {
pub fn load() -> Result<Self, ConfigError> {
let config: Self = Figment::new().merge(Env::raw()).extract()?;
Ok(config)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub connect_timeout: u64,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
url: "postgres://localhost:5432/sunbeam".to_string(),
max_connections: 10,
connect_timeout: 30,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NatsConfig {
pub url: String,
pub jetstream: bool,
pub lease_duration: u64,
pub auth_token: Option<String>,
}
impl Default for NatsConfig {
fn default() -> Self {
Self {
url: "nats://localhost:4222".to_string(),
jetstream: true,
lease_duration: 30,
auth_token: None,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuthConfig {
pub kratos_admin_url: String,
pub kratos_public_url: String,
pub authorization_read_url: String,
pub authorization_write_url: String,
pub system_tenant_ulid: String,
}
impl Default for AuthConfig {
fn default() -> Self {
Self {
kratos_admin_url: "http://localhost:4434".to_string(),
kratos_public_url: "http://localhost:4433".to_string(),
authorization_read_url: "http://localhost:4466".to_string(),
authorization_write_url: "http://localhost:4467".to_string(),
system_tenant_ulid: String::new(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ObservabilityConfig {
pub metrics: bool,
pub tracing: bool,
pub prometheus_endpoint: String,
}
impl Default for ObservabilityConfig {
fn default() -> Self {
Self {
metrics: true,
tracing: true,
prometheus_endpoint: "/metrics".to_string(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LimitsConfig {
pub max_requests_per_sec: u64,
pub burst_size: u64,
}
impl Default for LimitsConfig {
fn default() -> Self {
Self {
max_requests_per_sec: 100,
burst_size: 50,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ElectionConfig {
pub enabled: bool,
pub strategy: String,
pub lease_duration: u64,
}
impl Default for ElectionConfig {
fn default() -> Self {
Self {
enabled: false,
strategy: "memory".to_string(),
lease_duration: 30,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct VaultConfig {
pub url: String,
pub token: String,
pub kv_path: String,
pub lease_duration: u64,
}
impl Default for VaultConfig {
fn default() -> Self {
Self {
url: "http://localhost:8200".to_string(),
token: "root".to_string(),
kv_path: "secret/sunbeam".to_string(),
lease_duration: 30,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_service_config_default() {
let config = ServiceConfig::default();
assert_eq!(config.name, "sunbeam-g2v");
assert_eq!(config.environment, "development");
assert_eq!(config.host, "0.0.0.0");
assert_eq!(config.port, 8080);
assert!(!config.debug);
assert_eq!(config.log_level, "info");
}
#[test]
fn test_database_config_default() {
let config = DatabaseConfig::default();
assert_eq!(config.url, "postgres://localhost:5432/sunbeam");
assert_eq!(config.max_connections, 10);
assert_eq!(config.connect_timeout, 30);
}
#[test]
fn test_nats_config_default() {
let config = NatsConfig::default();
assert_eq!(config.url, "nats://localhost:4222");
assert!(config.jetstream);
assert!(config.auth_token.is_none());
}
#[test]
fn test_auth_config_default() {
let config = AuthConfig::default();
assert_eq!(config.kratos_admin_url, "http://localhost:4434");
assert_eq!(config.kratos_public_url, "http://localhost:4433");
assert_eq!(config.authorization_read_url, "http://localhost:4466");
assert_eq!(config.authorization_write_url, "http://localhost:4467");
assert!(config.system_tenant_ulid.is_empty());
}
#[test]
fn test_observability_config_default() {
let config = ObservabilityConfig::default();
assert!(config.metrics);
assert!(config.tracing);
assert_eq!(config.prometheus_endpoint, "/metrics");
}
#[test]
fn test_limits_config_default() {
let config = LimitsConfig::default();
assert_eq!(config.max_requests_per_sec, 100);
assert_eq!(config.burst_size, 50);
}
#[test]
fn test_election_config_default() {
let config = ElectionConfig::default();
assert!(!config.enabled);
assert_eq!(config.strategy, "memory");
assert_eq!(config.lease_duration, 30);
}
#[test]
fn test_vault_config_default() {
let config = VaultConfig::default();
assert_eq!(config.url, "http://localhost:8200");
assert_eq!(config.token, "root");
assert_eq!(config.kv_path, "secret/sunbeam");
}
}