html_translation_lib/
config.rs

1//! 翻译配置管理模块
2//!
3//! 提供翻译库的配置管理功能,支持多种配置方式和动态配置更新
4
5use crate::error::{TranslationError, TranslationResult};
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8use std::time::Duration;
9
10/// 翻译配置结构体
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct TranslationConfig {
13    /// 源语言代码 (ISO 639-1)
14    pub source_language: String,
15    
16    /// 目标语言代码 (ISO 639-1)
17    pub target_language: String,
18    
19    /// 翻译API的URL地址
20    pub api_url: String,
21    
22    /// 是否启用缓存
23    pub enable_cache: bool,
24    
25    /// 缓存TTL (生存时间)
26    pub cache_ttl: Duration,
27    
28    /// 缓存最大条目数
29    pub max_cache_entries: usize,
30    
31    /// 批处理大小
32    pub batch_size: usize,
33    
34    /// 最大重试次数
35    pub max_retries: usize,
36    
37    /// 重试延迟 (毫秒)
38    pub retry_delay_ms: u64,
39    
40    /// 请求超时时间 (秒)
41    pub timeout_seconds: u64,
42    
43    /// 是否启用索引标记
44    pub use_indexing: bool,
45    
46    /// 最小翻译文本长度
47    pub min_text_length: usize,
48    
49    /// 是否跳过链接文本
50    pub skip_links: bool,
51    
52    /// 是否跳过代码块
53    pub skip_code_blocks: bool,
54    
55    /// 自定义过滤规则 (正则表达式)
56    pub custom_filters: Vec<String>,
57    
58    /// API认证密钥
59    pub api_key: Option<String>,
60    
61    /// 用户代理字符串
62    pub user_agent: String,
63}
64
65impl Default for TranslationConfig {
66    fn default() -> Self {
67        Self {
68            source_language: "auto".to_string(),
69            target_language: "zh".to_string(),
70            api_url: "http://localhost:1188/translate".to_string(),
71            enable_cache: true,
72            cache_ttl: Duration::from_secs(3600), // 1小时
73            max_cache_entries: 10000,
74            batch_size: 20,
75            max_retries: 3,
76            retry_delay_ms: 1000,
77            timeout_seconds: 30,
78            use_indexing: true,
79            min_text_length: 2,
80            skip_links: false,
81            skip_code_blocks: true,
82            custom_filters: Vec::new(),
83            api_key: None,
84            user_agent: format!("html-translation-lib/{}", env!("CARGO_PKG_VERSION")),
85        }
86    }
87}
88
89impl TranslationConfig {
90    /// 创建新的配置实例
91    pub fn new() -> Self {
92        Self::default()
93    }
94    
95    /// 设置目标语言
96    pub fn target_language(mut self, lang: &str) -> Self {
97        self.target_language = lang.to_string();
98        self
99    }
100    
101    /// 设置源语言
102    pub fn source_language(mut self, lang: &str) -> Self {
103        self.source_language = lang.to_string();
104        self
105    }
106    
107    /// 设置API URL
108    pub fn api_url(mut self, url: &str) -> Self {
109        self.api_url = url.to_string();
110        self
111    }
112    
113    /// 启用或禁用缓存
114    pub fn enable_cache(mut self, enabled: bool) -> Self {
115        self.enable_cache = enabled;
116        self
117    }
118    
119    /// 设置缓存TTL
120    pub fn cache_ttl(mut self, ttl: Duration) -> Self {
121        self.cache_ttl = ttl;
122        self
123    }
124    
125    /// 设置批处理大小
126    pub fn batch_size(mut self, size: usize) -> Self {
127        self.batch_size = size;
128        self
129    }
130    
131    /// 设置最大重试次数
132    pub fn max_retries(mut self, retries: usize) -> Self {
133        self.max_retries = retries;
134        self
135    }
136    
137    /// 设置请求超时时间
138    pub fn timeout(mut self, seconds: u64) -> Self {
139        self.timeout_seconds = seconds;
140        self
141    }
142    
143    /// 设置API密钥
144    pub fn api_key(mut self, key: Option<String>) -> Self {
145        self.api_key = key;
146        self
147    }
148    
149    /// 启用或禁用索引标记
150    pub fn use_indexing(mut self, enabled: bool) -> Self {
151        self.use_indexing = enabled;
152        self
153    }
154    
155    /// 设置最小翻译文本长度
156    pub fn min_text_length(mut self, length: usize) -> Self {
157        self.min_text_length = length;
158        self
159    }
160    
161    /// 添加自定义过滤规则
162    pub fn add_filter(mut self, pattern: &str) -> Self {
163        self.custom_filters.push(pattern.to_string());
164        self
165    }
166    
167    /// 从文件加载配置
168    pub fn from_file<P: AsRef<Path>>(path: P) -> TranslationResult<Self> {
169        let content = std::fs::read_to_string(path)?;
170        let config: TranslationConfig = toml::from_str(&content)?;
171        Ok(config)
172    }
173    
174    /// 保存配置到文件
175    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> TranslationResult<()> {
176        let content = toml::to_string_pretty(self)
177            .map_err(|e| TranslationError::ConfigError(format!("配置序列化失败: {e}")))?;
178        std::fs::write(path, content)?;
179        Ok(())
180    }
181    
182    /// 从环境变量加载配置
183    pub fn from_env() -> Self {
184        let mut config = Self::default();
185        
186        if let Ok(target_lang) = std::env::var("TRANSLATION_TARGET_LANG") {
187            config.target_language = target_lang;
188        }
189        
190        if let Ok(api_url) = std::env::var("TRANSLATION_API_URL") {
191            config.api_url = api_url;
192        }
193        
194        if let Ok(api_key) = std::env::var("TRANSLATION_API_KEY") {
195            config.api_key = Some(api_key);
196        }
197        
198        if let Ok(cache_enabled) = std::env::var("TRANSLATION_ENABLE_CACHE") {
199            config.enable_cache = cache_enabled.to_lowercase() == "true";
200        }
201        
202        if let Ok(batch_size) = std::env::var("TRANSLATION_BATCH_SIZE") {
203            if let Ok(size) = batch_size.parse() {
204                config.batch_size = size;
205            }
206        }
207        
208        config
209    }
210    
211    /// 验证配置的有效性
212    pub fn validate(&self) -> TranslationResult<()> {
213        if self.target_language.is_empty() {
214            return Err(TranslationError::ConfigError("目标语言不能为空".to_string()));
215        }
216        
217        if self.api_url.is_empty() {
218            return Err(TranslationError::ConfigError("API URL不能为空".to_string()));
219        }
220        
221        if self.batch_size == 0 {
222            return Err(TranslationError::ConfigError("批处理大小必须大于0".to_string()));
223        }
224        
225        if self.timeout_seconds == 0 {
226            return Err(TranslationError::ConfigError("超时时间必须大于0".to_string()));
227        }
228        
229        // 验证自定义过滤规则
230        for filter in &self.custom_filters {
231            if let Err(e) = regex::Regex::new(filter) {
232                return Err(TranslationError::ConfigError(format!(
233                    "无效的正则表达式过滤规则 '{filter}': {e}"
234                )));
235            }
236        }
237        
238        Ok(())
239    }
240    
241    /// 获取HTTP客户端超时时间
242    pub fn http_timeout(&self) -> Duration {
243        Duration::from_secs(self.timeout_seconds)
244    }
245    
246    /// 获取重试延迟时间
247    pub fn retry_delay(&self) -> Duration {
248        Duration::from_millis(self.retry_delay_ms)
249    }
250}
251
252/// 配置构建器
253pub struct TranslationConfigBuilder {
254    config: TranslationConfig,
255}
256
257impl TranslationConfigBuilder {
258    /// 创建新的配置构建器
259    pub fn new() -> Self {
260        Self {
261            config: TranslationConfig::default(),
262        }
263    }
264    
265    /// 设置目标语言
266    pub fn target_language(mut self, lang: &str) -> Self {
267        self.config.target_language = lang.to_string();
268        self
269    }
270    
271    /// 设置API URL
272    pub fn api_url(mut self, url: &str) -> Self {
273        self.config.api_url = url.to_string();
274        self
275    }
276    
277    /// 启用缓存
278    pub fn with_cache(mut self, ttl: Duration, max_entries: usize) -> Self {
279        self.config.enable_cache = true;
280        self.config.cache_ttl = ttl;
281        self.config.max_cache_entries = max_entries;
282        self
283    }
284    
285    /// 设置批处理配置
286    pub fn with_batching(mut self, size: usize, use_indexing: bool) -> Self {
287        self.config.batch_size = size;
288        self.config.use_indexing = use_indexing;
289        self
290    }
291    
292    /// 设置重试配置
293    pub fn with_retry(mut self, max_retries: usize, delay_ms: u64) -> Self {
294        self.config.max_retries = max_retries;
295        self.config.retry_delay_ms = delay_ms;
296        self
297    }
298    
299    /// 构建配置
300    pub fn build(self) -> TranslationResult<TranslationConfig> {
301        self.config.validate()?;
302        Ok(self.config)
303    }
304}
305
306impl Default for TranslationConfigBuilder {
307    fn default() -> Self {
308        Self::new()
309    }
310}
311
312/// 生成默认配置文件示例
313pub fn generate_example_config<P: AsRef<Path>>(path: P) -> TranslationResult<()> {
314    let config = TranslationConfig::default();
315    config.save_to_file(path)?;
316    Ok(())
317}
318
319/// 检查配置文件是否存在
320pub fn config_file_exists<P: AsRef<Path>>(path: P) -> bool {
321    path.as_ref().exists()
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327    use std::time::Duration;
328    use tempfile::NamedTempFile;
329
330    #[test]
331    fn test_default_config() {
332        let config = TranslationConfig::default();
333        
334        assert_eq!(config.source_language, "auto");
335        assert_eq!(config.target_language, "zh");
336        assert_eq!(config.api_url, "http://localhost:1188/translate");
337        assert!(config.enable_cache);
338        assert_eq!(config.cache_ttl, Duration::from_secs(3600));
339        assert_eq!(config.max_cache_entries, 10000);
340        assert_eq!(config.batch_size, 20);
341        assert_eq!(config.max_retries, 3);
342        assert_eq!(config.retry_delay_ms, 1000);
343        assert_eq!(config.timeout_seconds, 30);
344        assert!(config.use_indexing);
345        assert_eq!(config.min_text_length, 2);
346        assert!(!config.skip_links);
347        assert!(config.skip_code_blocks);
348        assert!(config.custom_filters.is_empty());
349        assert!(config.api_key.is_none());
350    }
351}