use crate::errors::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
pub trait ServerConfig {
fn validate(&self) -> Result<()> {
Ok(())
}
fn config_name(&self) -> &'static str;
fn is_enabled(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeoutConfig {
pub connect_timeout: Duration,
pub read_timeout: Duration,
pub write_timeout: Duration,
}
impl Default for TimeoutConfig {
fn default() -> Self {
Self {
connect_timeout: Duration::from_secs(30),
read_timeout: Duration::from_secs(30),
write_timeout: Duration::from_secs(30),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
pub enable_tls: bool,
pub min_tls_version: String,
pub cipher_suites: Vec<String>,
pub cert_validation: CertificateValidation,
pub verify_certificates: bool,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
enable_tls: true,
min_tls_version: "1.2".to_string(),
cipher_suites: vec![
"TLS_AES_256_GCM_SHA384".to_string(),
"TLS_CHACHA20_POLY1305_SHA256".to_string(),
"TLS_AES_128_GCM_SHA256".to_string(),
],
cert_validation: CertificateValidation::Full,
verify_certificates: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CertificateValidation {
Full,
SkipHostname,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointConfig {
pub base_url: String,
pub api_version: Option<String>,
pub headers: HashMap<String, String>,
pub timeout: TimeoutConfig,
pub security: SecurityConfig,
}
impl EndpointConfig {
pub fn new(base_url: impl Into<String>) -> Self {
Self {
base_url: base_url.into(),
api_version: None,
headers: HashMap::new(),
timeout: TimeoutConfig::default(),
security: SecurityConfig::default(),
}
}
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.insert(key.into(), value.into());
self
}
pub fn with_api_version(mut self, version: impl Into<String>) -> Self {
self.api_version = Some(version.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
pub max_attempts: u32,
pub initial_delay: Duration,
pub max_delay: Duration,
pub backoff_multiplier: f64,
pub jitter_factor: f64,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_attempts: 3,
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
backoff_multiplier: 2.0,
jitter_factor: 0.1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
pub debug: bool,
pub log_bodies: bool,
pub log_sensitive: bool,
pub max_log_size: usize,
}
impl Default for LoggingConfig {
fn default() -> Self {
Self {
debug: false,
log_bodies: false,
log_sensitive: false,
max_log_size: 4096,
}
}
}
pub mod validation {
use super::*;
pub fn validate_url(url: &str) -> Result<()> {
if url.is_empty() {
return Err(crate::errors::AuthError::ConfigurationError(
"URL cannot be empty".to_string(),
));
}
if !url.starts_with("http://") && !url.starts_with("https://") {
return Err(crate::errors::AuthError::ConfigurationError(format!(
"Invalid URL format: {}",
url
)));
}
Ok(())
}
pub fn validate_positive_duration(duration: &Duration, field_name: &str) -> Result<()> {
if duration.is_zero() {
return Err(crate::errors::AuthError::ConfigurationError(format!(
"{} must be greater than zero",
field_name
)));
}
Ok(())
}
pub fn validate_port(port: u16) -> Result<()> {
if port == 0 {
return Err(crate::errors::AuthError::ConfigurationError(
"Port cannot be zero".to_string(),
));
}
Ok(())
}
pub fn validate_required_field(value: &str, field_name: &str) -> Result<()> {
if value.trim().is_empty() {
return Err(crate::errors::AuthError::ConfigurationError(format!(
"{} is required and cannot be empty",
field_name
)));
}
Ok(())
}
}