use crate::error::{TranslationError, TranslationResult};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TranslationConfig {
pub api_url: String,
pub api_key: Option<String>,
#[serde(with = "serde_duration")]
pub timeout: Duration,
pub max_concurrent_requests: usize,
pub enable_cache: bool,
}
impl Default for TranslationConfig {
fn default() -> Self {
Self {
api_url: "https://api.example.com/translate".to_string(),
api_key: None,
timeout: Duration::from_secs(30),
max_concurrent_requests: 10,
enable_cache: true,
}
}
}
impl TranslationConfig {
pub fn new(api_url: String) -> Self {
Self {
api_url,
..Default::default()
}
}
pub fn with_api_key(mut self, api_key: String) -> Self {
self.api_key = Some(api_key);
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn with_max_concurrent(mut self, max_concurrent: usize) -> Self {
self.max_concurrent_requests = max_concurrent;
self
}
pub fn with_cache(mut self, enable_cache: bool) -> Self {
self.enable_cache = enable_cache;
self
}
pub fn from_env() -> TranslationResult<Self> {
let api_url = std::env::var("TRANSLATION_API_URL")
.unwrap_or_else(|_| "https://api.example.com/translate".to_string());
let api_key = std::env::var("TRANSLATION_API_KEY").ok();
let timeout_secs = std::env::var("TRANSLATION_TIMEOUT")
.unwrap_or_else(|_| "30".to_string())
.parse::<u64>()
.map_err(|_| TranslationError::Config("Invalid timeout value".to_string()))?;
let max_concurrent = std::env::var("TRANSLATION_MAX_CONCURRENT")
.unwrap_or_else(|_| "10".to_string())
.parse::<usize>()
.map_err(|_| TranslationError::Config("Invalid max_concurrent value".to_string()))?;
let enable_cache = std::env::var("TRANSLATION_ENABLE_CACHE")
.unwrap_or_else(|_| "true".to_string())
.parse::<bool>()
.map_err(|_| TranslationError::Config("Invalid enable_cache value".to_string()))?;
Ok(Self {
api_url,
api_key,
timeout: Duration::from_secs(timeout_secs),
max_concurrent_requests: max_concurrent,
enable_cache,
})
}
pub fn validate(&self) -> TranslationResult<()> {
if self.api_url.is_empty() {
return Err(TranslationError::Config(
"API URL cannot be empty".to_string(),
));
}
if !self.api_url.starts_with("http://") && !self.api_url.starts_with("https://") {
return Err(TranslationError::Config(
"API URL must start with http:// or https://".to_string(),
));
}
if self.max_concurrent_requests == 0 {
return Err(TranslationError::Config(
"max_concurrent_requests must be greater than 0".to_string(),
));
}
if self.timeout.as_secs() == 0 {
return Err(TranslationError::Config(
"timeout must be greater than 0".to_string(),
));
}
Ok(())
}
}
mod serde_duration {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::time::Duration;
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
duration.as_secs().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
let secs = u64::deserialize(deserializer)?;
Ok(Duration::from_secs(secs))
}
}