subx_cli/config/
builder.rs

1//! Configuration builder for fluent test configuration creation.
2//!
3//! This module provides a fluent API for building test configurations,
4//! making it easy to create specific configuration scenarios for testing.
5
6use crate::config::test_service::TestConfigService;
7use crate::config::{Config, OverflowStrategy};
8
9/// Fluent builder for creating test configurations.
10///
11/// This builder provides a convenient way to create configurations
12/// for testing with specific settings, using method chaining for clarity.
13///
14/// # Examples
15///
16/// ```rust
17/// use subx_cli::config::TestConfigBuilder;
18///
19/// let config = TestConfigBuilder::new()
20///     .with_ai_provider("openai")
21///     .with_ai_model("gpt-4.1")
22///     .with_vad_enabled(true)
23///     .build_config();
24/// ```
25pub struct TestConfigBuilder {
26    config: Config,
27}
28
29impl TestConfigBuilder {
30    /// Create a new configuration builder with default values.
31    pub fn new() -> Self {
32        Self {
33            config: Config::default(),
34        }
35    }
36
37    // AI Configuration Methods
38
39    /// Set the AI provider.
40    ///
41    /// # Arguments
42    ///
43    /// * `provider` - The AI provider name (e.g., "openai", "anthropic")
44    pub fn with_ai_provider(mut self, provider: &str) -> Self {
45        self.config.ai.provider = provider.to_string();
46        self
47    }
48
49    /// Set the AI model.
50    ///
51    /// # Arguments
52    ///
53    /// * `model` - The AI model name (e.g., "gpt-4.1", "claude-3")
54    pub fn with_ai_model(mut self, model: &str) -> Self {
55        self.config.ai.model = model.to_string();
56        self
57    }
58
59    /// Set the AI API key.
60    ///
61    /// # Arguments
62    ///
63    /// * `api_key` - The API key for authentication
64    pub fn with_ai_api_key(mut self, api_key: &str) -> Self {
65        self.config.ai.api_key = Some(api_key.to_string());
66        self
67    }
68
69    /// Set the AI base URL.
70    ///
71    /// # Arguments
72    ///
73    /// * `base_url` - The base URL for the AI service
74    pub fn with_ai_base_url(mut self, base_url: &str) -> Self {
75        self.config.ai.base_url = base_url.to_string();
76        self
77    }
78
79    /// Set the maximum sample length for AI requests.
80    ///
81    /// # Arguments
82    ///
83    /// * `length` - Maximum sample length in characters
84    pub fn with_max_sample_length(mut self, length: usize) -> Self {
85        self.config.ai.max_sample_length = length;
86        self
87    }
88
89    /// Set the AI temperature parameter.
90    ///
91    /// # Arguments
92    ///
93    /// * `temperature` - Temperature value (0.0-1.0)
94    pub fn with_ai_temperature(mut self, temperature: f32) -> Self {
95        self.config.ai.temperature = temperature;
96        self
97    }
98
99    /// Set the AI max tokens parameter.
100    ///
101    /// # Arguments
102    ///
103    /// * `max_tokens` - Maximum tokens in response (1-100000)
104    pub fn with_ai_max_tokens(mut self, max_tokens: u32) -> Self {
105        self.config.ai.max_tokens = max_tokens;
106        self
107    }
108
109    /// Set the AI retry parameters.
110    ///
111    /// # Arguments
112    ///
113    /// * `attempts` - Number of retry attempts
114    /// * `delay_ms` - Delay between retries in milliseconds
115    pub fn with_ai_retry(mut self, attempts: u32, delay_ms: u64) -> Self {
116        self.config.ai.retry_attempts = attempts;
117        self.config.ai.retry_delay_ms = delay_ms;
118        self
119    }
120
121    /// Set the AI request timeout.
122    ///
123    /// # Arguments
124    ///
125    /// * `timeout_seconds` - Request timeout in seconds
126    pub fn with_ai_request_timeout(mut self, timeout_seconds: u64) -> Self {
127        self.config.ai.request_timeout_seconds = timeout_seconds;
128        self
129    }
130
131    // Sync Configuration Methods
132
133    /// Set the synchronization method.
134    ///
135    /// # Arguments
136    ///
137    /// * `method` - The sync method to use ("vad", "auto", "manual")
138    pub fn with_sync_method(mut self, method: &str) -> Self {
139        self.config.sync.default_method = method.to_string();
140        self
141    }
142
143    /// Enable or disable local VAD.
144    ///
145    /// # Arguments
146    ///
147    /// * `enabled` - Whether to enable local VAD processing
148    pub fn with_vad_enabled(mut self, enabled: bool) -> Self {
149        self.config.sync.vad.enabled = enabled;
150        self
151    }
152
153    /// Set VAD sensitivity.
154    ///
155    /// # Arguments
156    ///
157    /// * `sensitivity` - VAD sensitivity value (0.0-1.0)
158    pub fn with_vad_sensitivity(mut self, sensitivity: f32) -> Self {
159        self.config.sync.vad.sensitivity = sensitivity;
160        self
161    }
162
163    // Formats Configuration Methods
164
165    /// Set the default output format.
166    ///
167    /// # Arguments
168    ///
169    /// * `format` - The output format (e.g., "srt", "ass", "vtt")
170    pub fn with_default_output_format(mut self, format: &str) -> Self {
171        self.config.formats.default_output = format.to_string();
172        self
173    }
174
175    /// Enable or disable style preservation.
176    ///
177    /// # Arguments
178    ///
179    /// * `preserve` - Whether to preserve styling
180    pub fn with_preserve_styling(mut self, preserve: bool) -> Self {
181        self.config.formats.preserve_styling = preserve;
182        self
183    }
184
185    /// Set the default encoding.
186    ///
187    /// # Arguments
188    ///
189    /// * `encoding` - The default encoding (e.g., "utf-8", "gbk")
190    pub fn with_default_encoding(mut self, encoding: &str) -> Self {
191        self.config.formats.default_encoding = encoding.to_string();
192        self
193    }
194
195    /// Set the encoding detection confidence threshold.
196    ///
197    /// # Arguments
198    ///
199    /// * `confidence` - Confidence threshold (0.0-1.0)
200    pub fn with_encoding_detection_confidence(mut self, confidence: f32) -> Self {
201        self.config.formats.encoding_detection_confidence = confidence;
202        self
203    }
204
205    // General Configuration Methods
206
207    /// Enable or disable backup.
208    ///
209    /// # Arguments
210    ///
211    /// * `enabled` - Whether to enable backup
212    pub fn with_backup_enabled(mut self, enabled: bool) -> Self {
213        self.config.general.backup_enabled = enabled;
214        self
215    }
216
217    /// Set the maximum number of concurrent jobs.
218    ///
219    /// # Arguments
220    ///
221    /// * `jobs` - Maximum concurrent jobs
222    pub fn with_max_concurrent_jobs(mut self, jobs: usize) -> Self {
223        self.config.general.max_concurrent_jobs = jobs;
224        self
225    }
226
227    /// Set the task timeout.
228    ///
229    /// # Arguments
230    ///
231    /// * `timeout_seconds` - Timeout in seconds
232    pub fn with_task_timeout(mut self, timeout_seconds: u64) -> Self {
233        self.config.general.task_timeout_seconds = timeout_seconds;
234        self
235    }
236
237    /// Enable or disable progress bar.
238    ///
239    /// # Arguments
240    ///
241    /// * `enabled` - Whether to enable progress bar
242    pub fn with_progress_bar(mut self, enabled: bool) -> Self {
243        self.config.general.enable_progress_bar = enabled;
244        self
245    }
246
247    /// Set the worker idle timeout.
248    ///
249    /// # Arguments
250    ///
251    /// * `timeout_seconds` - Idle timeout in seconds
252    pub fn with_worker_idle_timeout(mut self, timeout_seconds: u64) -> Self {
253        self.config.general.worker_idle_timeout_seconds = timeout_seconds;
254        self
255    }
256
257    // Parallel Configuration Methods
258
259    /// Set the task queue size.
260    ///
261    /// # Arguments
262    ///
263    /// * `size` - Queue size
264    pub fn with_task_queue_size(mut self, size: usize) -> Self {
265        self.config.parallel.task_queue_size = size;
266        self
267    }
268
269    /// Enable or disable task priorities.
270    ///
271    /// # Arguments
272    ///
273    /// * `enabled` - Whether to enable task priorities
274    pub fn with_task_priorities(mut self, enabled: bool) -> Self {
275        self.config.parallel.enable_task_priorities = enabled;
276        self
277    }
278
279    /// Enable or disable auto-balancing of workers.
280    ///
281    /// # Arguments
282    ///
283    /// * `enabled` - Whether to enable auto-balancing
284    pub fn with_auto_balance_workers(mut self, enabled: bool) -> Self {
285        self.config.parallel.auto_balance_workers = enabled;
286        self
287    }
288
289    /// Set the queue overflow strategy.
290    ///
291    /// # Arguments
292    ///
293    /// * `strategy` - Overflow strategy
294    pub fn with_queue_overflow_strategy(mut self, strategy: OverflowStrategy) -> Self {
295        self.config.parallel.overflow_strategy = strategy;
296        self
297    }
298
299    /// Set the number of parallel workers and queue size, used for integration testing.
300    pub fn with_parallel_settings(mut self, max_workers: usize, queue_size: usize) -> Self {
301        self.config.general.max_concurrent_jobs = max_workers;
302        self.config.parallel.task_queue_size = queue_size;
303        self
304    }
305
306    // Builder Methods
307
308    /// Build a test configuration service with the configured settings.
309    pub fn build_service(self) -> TestConfigService {
310        TestConfigService::new(self.config)
311    }
312
313    /// Build a configuration object with the configured settings.
314    pub fn build_config(self) -> Config {
315        self.config
316    }
317
318    /// Get a reference to the current configuration being built.
319    pub fn config(&self) -> &Config {
320        &self.config
321    }
322
323    /// Get a mutable reference to the current configuration being built.
324    pub fn config_mut(&mut self) -> &mut Config {
325        &mut self.config
326    }
327    /// Configure AI base URL for mock server integration testing.
328    ///
329    /// Sets up the configuration to use a mock AI server for testing purposes.
330    /// This is primarily used in integration tests to avoid making real API calls.
331    ///
332    /// # Arguments
333    ///
334    /// - `mock_url`: The URL of the mock server to use for AI API calls
335    ///
336    /// # Examples
337    ///
338    /// ```rust
339    /// use subx_cli::config::TestConfigBuilder;
340    ///
341    /// let config = TestConfigBuilder::new()
342    ///     .with_mock_ai_server("http://localhost:3000")
343    ///     .build_config();
344    /// ```
345    pub fn with_mock_ai_server(mut self, mock_url: &str) -> Self {
346        self.config.ai.base_url = mock_url.to_string();
347        self.config.ai.api_key = Some("mock-api-key".to_string());
348        self
349    }
350}
351
352impl Default for TestConfigBuilder {
353    fn default() -> Self {
354        Self::new()
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361    use crate::config::service::ConfigService;
362
363    #[test]
364    fn test_builder_default() {
365        let config = TestConfigBuilder::new().build_config();
366        let default_config = Config::default();
367
368        assert_eq!(config.ai.provider, default_config.ai.provider);
369        assert_eq!(config.ai.model, default_config.ai.model);
370    }
371
372    #[test]
373    fn test_builder_ai_configuration() {
374        let config = TestConfigBuilder::new()
375            .with_ai_provider("anthropic")
376            .with_ai_model("claude-3")
377            .with_ai_api_key("test-key")
378            .with_max_sample_length(5000)
379            .with_ai_temperature(0.7)
380            .build_config();
381
382        assert_eq!(config.ai.provider, "anthropic");
383        assert_eq!(config.ai.model, "claude-3");
384        assert_eq!(config.ai.api_key, Some("test-key".to_string()));
385        assert_eq!(config.ai.max_sample_length, 5000);
386        assert_eq!(config.ai.temperature, 0.7);
387    }
388
389    #[test]
390    fn test_builder_sync_configuration() {
391        let config = TestConfigBuilder::new()
392            .with_sync_method("vad")
393            .with_vad_enabled(true)
394            .with_vad_sensitivity(0.8)
395            .build_config();
396
397        assert_eq!(config.sync.default_method, "vad");
398        assert!(config.sync.vad.enabled);
399        assert_eq!(config.sync.vad.sensitivity, 0.8);
400    }
401
402    #[test]
403    fn test_builder_service_creation() {
404        let service = TestConfigBuilder::new()
405            .with_ai_provider("test-provider")
406            .build_service();
407
408        let config = service.get_config().unwrap();
409        assert_eq!(config.ai.provider, "test-provider");
410    }
411
412    #[test]
413    fn test_builder_chaining() {
414        let config = TestConfigBuilder::new()
415            .with_ai_provider("openai")
416            .with_ai_model("gpt-4.1")
417            .with_sync_method("vad")
418            .with_vad_sensitivity(0.5)
419            .with_max_concurrent_jobs(8)
420            .with_task_queue_size(200)
421            .build_config();
422
423        assert_eq!(config.ai.provider, "openai");
424        assert_eq!(config.ai.model, "gpt-4.1");
425        assert_eq!(config.sync.default_method, "vad");
426        assert_eq!(config.sync.vad.sensitivity, 0.5);
427        assert_eq!(config.general.max_concurrent_jobs, 8);
428        assert_eq!(config.parallel.task_queue_size, 200);
429    }
430
431    #[test]
432    fn test_builder_ai_configuration_openrouter() {
433        let config = TestConfigBuilder::new()
434            .with_ai_provider("openrouter")
435            .with_ai_model("deepseek/deepseek-r1-0528:free")
436            .with_ai_api_key("test-openrouter-key")
437            .build_config();
438        assert_eq!(config.ai.provider, "openrouter");
439        assert_eq!(config.ai.model, "deepseek/deepseek-r1-0528:free");
440        assert_eq!(config.ai.api_key, Some("test-openrouter-key".to_string()));
441    }
442}