pub mod app;
pub mod auth;
pub mod cloud;
pub mod observability;
pub mod rate_limit;
pub use app::AppConfig;
pub use auth::AuthConfig;
pub use cloud::CloudConfig;
pub use observability::ObservabilityConfig;
pub use rate_limit::{RateLimitConfig, RateLimitTier};
use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Failed to load configuration: {0}")]
LoadError(String),
#[error("Invalid configuration: {0}")]
ValidationError(String),
#[error("Missing required field: {0}")]
MissingField(String),
}
pub type Result<T> = std::result::Result<T, ConfigError>;
pub fn load_config(config_path: Option<&Path>) -> Result<AppConfig> {
let mut builder = config::Config::builder();
if let Some(path) = config_path {
builder = builder.add_source(config::File::from(path));
}
builder = builder.add_source(
config::Environment::with_prefix("LLM_SHIELD_API")
.separator("__")
.try_parsing(true),
);
let config: AppConfig = builder
.build()
.map_err(|e| ConfigError::LoadError(e.to_string()))?
.try_deserialize()
.map_err(|e| ConfigError::LoadError(e.to_string()))?;
config.validate()?;
Ok(config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::app::ServerConfig;
use std::env;
#[test]
fn test_load_config_with_defaults() {
let result = load_config(None);
match result {
Ok(config) => {
assert_eq!(config.server.port, 3000);
assert_eq!(config.server.host, "127.0.0.1");
}
Err(e) => {
println!("Expected error (no config sources): {}", e);
}
}
}
#[test]
fn test_config_from_environment() {
let server_config = ServerConfig {
host: "0.0.0.0".to_string(),
port: 8080,
timeout_secs: 30,
max_body_size: 10 * 1024 * 1024,
workers: 4,
};
assert!(server_config.validate().is_ok());
assert_eq!(server_config.host, "0.0.0.0");
assert_eq!(server_config.port, 8080);
assert_eq!(server_config.bind_address(), "0.0.0.0:8080");
let app_config = AppConfig::default();
assert!(app_config.validate().is_ok());
assert_eq!(app_config.server.port, 3000);
}
#[test]
fn test_config_validation() {
env::set_var("LLM_SHIELD_API__SERVER__PORT", "99999");
let result = load_config(None);
env::remove_var("LLM_SHIELD_API__SERVER__PORT");
assert!(result.is_err(), "Should reject invalid port");
}
}