use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
pub max_memory_mb: usize,
pub max_operations_per_batch: usize,
pub cache_size_mb: usize,
pub enable_streaming: bool,
pub max_concurrent_ops: usize,
pub operation_timeout_ms: u64,
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
max_memory_mb: 512,
max_operations_per_batch: 1000,
cache_size_mb: 64,
enable_streaming: true,
max_concurrent_ops: 4,
operation_timeout_ms: 30000,
}
}
}
impl PerformanceConfig {
pub fn minimal() -> Self {
Self {
max_memory_mb: 64,
max_operations_per_batch: 100,
cache_size_mb: 8,
enable_streaming: false,
max_concurrent_ops: 1,
operation_timeout_ms: 10000,
}
}
pub fn high_performance() -> Self {
Self {
max_memory_mb: 2048,
max_operations_per_batch: 10000,
cache_size_mb: 256,
enable_streaming: true,
max_concurrent_ops: 16,
operation_timeout_ms: 60000,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
pub allowed_schemes: Vec<String>,
pub max_url_length: usize,
pub block_external_resources: bool,
pub sanitize_content: bool,
pub max_block_size: usize,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
allowed_schemes: vec!["http".to_string(), "https".to_string(), "data".to_string()],
max_url_length: 2048,
block_external_resources: false,
sanitize_content: true,
max_block_size: 1024 * 1024, }
}
}
impl SecurityConfig {
pub fn strict() -> Self {
Self {
allowed_schemes: vec!["https".to_string()],
max_url_length: 1024,
block_external_resources: true,
sanitize_content: true,
max_block_size: 256 * 1024, }
}
pub fn validate_url(&self, url: &str) -> Result<(), String> {
if url.len() > self.max_url_length {
return Err(format!(
"URL exceeds maximum length of {} characters",
self.max_url_length
));
}
let scheme = url.split(':').next().unwrap_or("");
if !self.allowed_schemes.contains(&scheme.to_lowercase()) {
return Err(format!(
"URL scheme '{}' is not allowed. Allowed schemes: {:?}",
scheme, self.allowed_schemes
));
}
if url.contains("..") {
return Err("Path traversal attempts are not allowed".to_string());
}
Ok(())
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EngineConfig {
pub performance: PerformanceConfig,
pub security: SecurityConfig,
}
impl EngineConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_performance(mut self, config: PerformanceConfig) -> Self {
self.performance = config;
self
}
pub fn with_security(mut self, config: SecurityConfig) -> Self {
self.security = config;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = EngineConfig::default();
assert_eq!(config.performance.max_memory_mb, 512);
assert!(config
.security
.allowed_schemes
.contains(&"https".to_string()));
}
#[test]
fn test_minimal_config() {
let config = PerformanceConfig::minimal();
assert!(config.max_memory_mb < 100);
assert!(!config.enable_streaming);
}
#[test]
fn test_high_performance_config() {
let config = PerformanceConfig::high_performance();
assert!(config.max_memory_mb > 1000);
assert!(config.max_concurrent_ops > 8);
}
#[test]
fn test_strict_security() {
let config = SecurityConfig::strict();
assert!(config.block_external_resources);
assert_eq!(config.allowed_schemes.len(), 1);
}
#[test]
fn test_url_validation() {
let config = SecurityConfig::default();
assert!(config.validate_url("https://example.com/image.jpg").is_ok());
assert!(config.validate_url("http://example.com/file").is_ok());
assert!(config.validate_url("data:image/png;base64,abc123").is_ok());
assert!(config.validate_url("ftp://example.com/file").is_err());
assert!(config
.validate_url("https://example.com/../etc/passwd")
.is_err());
}
#[test]
fn test_url_length_limit() {
let config = SecurityConfig {
max_url_length: 50,
..Default::default()
};
let short_url = "https://example.com/a";
let long_url = "https://example.com/".to_string() + &"a".repeat(100);
assert!(config.validate_url(short_url).is_ok());
assert!(config.validate_url(&long_url).is_err());
}
}