sunbeam-g2v 0.2.0

Sunbeam Service Framework - A ConnectRPC-based framework for building microservices
Documentation
//! Service configuration management.
//!
//! This module provides configuration types and loading utilities for Sunbeam services.

use figment::Figment;
use figment::providers::Env;
use serde::{Deserialize, Serialize};

/// Main service configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ServiceConfig {
    /// Service name.
    pub name: String,
    /// Environment (development, staging, production).
    pub environment: String,
    /// Host to bind to.
    pub host: String,
    /// Port to bind to.
    pub port: u16,
    /// Debug mode.
    pub debug: bool,
    /// Log level.
    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 {
    /// Load configuration from environment.
    pub fn load() -> Result<Self, figment::Error> {
        let config: Self = Figment::new()
            .merge(Env::raw())
            .extract()?;
        Ok(config)
    }
}

/// Database configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DatabaseConfig {
    /// Database URL.
    pub url: String,
    /// Maximum connections.
    pub max_connections: u32,
    /// Connection timeout in seconds.
    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,
        }
    }
}

/// NATS configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NatsConfig {
    /// NATS server URL.
    pub url: String,
    /// JetStream enabled.
    pub jetstream: bool,
    /// Lease duration for leader election locks (seconds).
    pub lease_duration: u64,
}

impl Default for NatsConfig {
    fn default() -> Self {
        Self {
            url: "nats://localhost:4222".to_string(),
            jetstream: true,
            lease_duration: 30,
        }
    }
}

/// Authentication configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuthConfig {
    /// JWT secret key.
    pub jwt_secret: String,
    /// Token expiration in seconds.
    pub token_expiry: u64,
}

impl Default for AuthConfig {
    fn default() -> Self {
        Self {
            jwt_secret: "change-me".to_string(),
            token_expiry: 3600,
        }
    }
}

/// Observability configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ObservabilityConfig {
    /// Metrics enabled.
    pub metrics: bool,
    /// Tracing enabled.
    pub tracing: bool,
    /// Prometheus endpoint.
    pub prometheus_endpoint: String,
}

impl Default for ObservabilityConfig {
    fn default() -> Self {
        Self {
            metrics: true,
            tracing: true,
            prometheus_endpoint: "/metrics".to_string(),
        }
    }
}

/// Rate limiting configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LimitsConfig {
    /// Max requests per second.
    pub max_requests_per_sec: u64,
    /// Burst size.
    pub burst_size: u64,
}

impl Default for LimitsConfig {
    fn default() -> Self {
        Self {
            max_requests_per_sec: 100,
            burst_size: 50,
        }
    }
}

/// Leader election configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ElectionConfig {
    /// Election enabled.
    pub enabled: bool,
    /// Election strategy (vault, nats, memory).
    pub strategy: String,
    /// Lease duration in seconds.
    pub lease_duration: u64,
}

impl Default for ElectionConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            strategy: "memory".to_string(),
            lease_duration: 30,
        }
    }
}

/// Vault configuration.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct VaultConfig {
    /// Vault server URL.
    pub url: String,
    /// Vault token.
    pub token: String,
    /// KV secret path.
    pub kv_path: String,
    /// Lease duration in seconds.
    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);
    }

    #[test]
    fn test_auth_config_default() {
        let config = AuthConfig::default();
        assert_eq!(config.jwt_secret, "change-me");
        assert_eq!(config.token_expiry, 3600);
    }

    #[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");
    }
}