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    /// Set VAD sample rate.
164    ///
165    /// # Arguments
166    ///
167    /// * `sample_rate` - Audio sample rate in Hz
168    pub fn with_vad_sample_rate(mut self, sample_rate: u32) -> Self {
169        self.config.sync.vad.sample_rate = sample_rate;
170        self
171    }
172
173    // Formats Configuration Methods
174
175    /// Set the default output format.
176    ///
177    /// # Arguments
178    ///
179    /// * `format` - The output format (e.g., "srt", "ass", "vtt")
180    pub fn with_default_output_format(mut self, format: &str) -> Self {
181        self.config.formats.default_output = format.to_string();
182        self
183    }
184
185    /// Enable or disable style preservation.
186    ///
187    /// # Arguments
188    ///
189    /// * `preserve` - Whether to preserve styling
190    pub fn with_preserve_styling(mut self, preserve: bool) -> Self {
191        self.config.formats.preserve_styling = preserve;
192        self
193    }
194
195    /// Set the default encoding.
196    ///
197    /// # Arguments
198    ///
199    /// * `encoding` - The default encoding (e.g., "utf-8", "gbk")
200    pub fn with_default_encoding(mut self, encoding: &str) -> Self {
201        self.config.formats.default_encoding = encoding.to_string();
202        self
203    }
204
205    /// Set the encoding detection confidence threshold.
206    ///
207    /// # Arguments
208    ///
209    /// * `confidence` - Confidence threshold (0.0-1.0)
210    pub fn with_encoding_detection_confidence(mut self, confidence: f32) -> Self {
211        self.config.formats.encoding_detection_confidence = confidence;
212        self
213    }
214
215    // General Configuration Methods
216
217    /// Enable or disable backup.
218    ///
219    /// # Arguments
220    ///
221    /// * `enabled` - Whether to enable backup
222    pub fn with_backup_enabled(mut self, enabled: bool) -> Self {
223        self.config.general.backup_enabled = enabled;
224        self
225    }
226
227    /// Set the maximum number of concurrent jobs.
228    ///
229    /// # Arguments
230    ///
231    /// * `jobs` - Maximum concurrent jobs
232    pub fn with_max_concurrent_jobs(mut self, jobs: usize) -> Self {
233        self.config.general.max_concurrent_jobs = jobs;
234        self
235    }
236
237    /// Set the task timeout.
238    ///
239    /// # Arguments
240    ///
241    /// * `timeout_seconds` - Timeout in seconds
242    pub fn with_task_timeout(mut self, timeout_seconds: u64) -> Self {
243        self.config.general.task_timeout_seconds = timeout_seconds;
244        self
245    }
246
247    /// Enable or disable progress bar.
248    ///
249    /// # Arguments
250    ///
251    /// * `enabled` - Whether to enable progress bar
252    pub fn with_progress_bar(mut self, enabled: bool) -> Self {
253        self.config.general.enable_progress_bar = enabled;
254        self
255    }
256
257    /// Set the worker idle timeout.
258    ///
259    /// # Arguments
260    ///
261    /// * `timeout_seconds` - Idle timeout in seconds
262    pub fn with_worker_idle_timeout(mut self, timeout_seconds: u64) -> Self {
263        self.config.general.worker_idle_timeout_seconds = timeout_seconds;
264        self
265    }
266
267    // Parallel Configuration Methods
268
269    /// Set the task queue size.
270    ///
271    /// # Arguments
272    ///
273    /// * `size` - Queue size
274    pub fn with_task_queue_size(mut self, size: usize) -> Self {
275        self.config.parallel.task_queue_size = size;
276        self
277    }
278
279    /// Enable or disable task priorities.
280    ///
281    /// # Arguments
282    ///
283    /// * `enabled` - Whether to enable task priorities
284    pub fn with_task_priorities(mut self, enabled: bool) -> Self {
285        self.config.parallel.enable_task_priorities = enabled;
286        self
287    }
288
289    /// Enable or disable auto-balancing of workers.
290    ///
291    /// # Arguments
292    ///
293    /// * `enabled` - Whether to enable auto-balancing
294    pub fn with_auto_balance_workers(mut self, enabled: bool) -> Self {
295        self.config.parallel.auto_balance_workers = enabled;
296        self
297    }
298
299    /// Set the queue overflow strategy.
300    ///
301    /// # Arguments
302    ///
303    /// * `strategy` - Overflow strategy
304    pub fn with_queue_overflow_strategy(mut self, strategy: OverflowStrategy) -> Self {
305        self.config.parallel.overflow_strategy = strategy;
306        self
307    }
308
309    /// Set the number of parallel workers and queue size, used for integration testing.
310    pub fn with_parallel_settings(mut self, max_workers: usize, queue_size: usize) -> Self {
311        self.config.general.max_concurrent_jobs = max_workers;
312        self.config.parallel.task_queue_size = queue_size;
313        self
314    }
315
316    // Builder Methods
317
318    /// Build a test configuration service with the configured settings.
319    pub fn build_service(self) -> TestConfigService {
320        TestConfigService::new(self.config)
321    }
322
323    /// Build a configuration object with the configured settings.
324    pub fn build_config(self) -> Config {
325        self.config
326    }
327
328    /// Get a reference to the current configuration being built.
329    pub fn config(&self) -> &Config {
330        &self.config
331    }
332
333    /// Get a mutable reference to the current configuration being built.
334    pub fn config_mut(&mut self) -> &mut Config {
335        &mut self.config
336    }
337    /// Configure AI base URL for mock server integration testing.
338    ///
339    /// Sets up the configuration to use a mock AI server for testing purposes.
340    /// This is primarily used in integration tests to avoid making real API calls.
341    ///
342    /// # Arguments
343    ///
344    /// - `mock_url`: The URL of the mock server to use for AI API calls
345    ///
346    /// # Examples
347    ///
348    /// ```rust
349    /// use subx_cli::config::TestConfigBuilder;
350    ///
351    /// let config = TestConfigBuilder::new()
352    ///     .with_mock_ai_server("http://localhost:3000")
353    ///     .build_config();
354    /// ```
355    pub fn with_mock_ai_server(mut self, mock_url: &str) -> Self {
356        self.config.ai.base_url = mock_url.to_string();
357        self.config.ai.api_key = Some("mock-api-key".to_string());
358        self
359    }
360}
361
362impl Default for TestConfigBuilder {
363    fn default() -> Self {
364        Self::new()
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371    use crate::config::service::ConfigService;
372
373    #[test]
374    fn test_builder_default() {
375        let config = TestConfigBuilder::new().build_config();
376        let default_config = Config::default();
377
378        assert_eq!(config.ai.provider, default_config.ai.provider);
379        assert_eq!(config.ai.model, default_config.ai.model);
380    }
381
382    #[test]
383    fn test_builder_ai_configuration() {
384        let config = TestConfigBuilder::new()
385            .with_ai_provider("anthropic")
386            .with_ai_model("claude-3")
387            .with_ai_api_key("test-key")
388            .with_max_sample_length(5000)
389            .with_ai_temperature(0.7)
390            .build_config();
391
392        assert_eq!(config.ai.provider, "anthropic");
393        assert_eq!(config.ai.model, "claude-3");
394        assert_eq!(config.ai.api_key, Some("test-key".to_string()));
395        assert_eq!(config.ai.max_sample_length, 5000);
396        assert_eq!(config.ai.temperature, 0.7);
397    }
398
399    #[test]
400    fn test_builder_sync_configuration() {
401        let config = TestConfigBuilder::new()
402            .with_sync_method("vad")
403            .with_vad_enabled(true)
404            .with_vad_sensitivity(0.8)
405            .with_vad_sample_rate(32000)
406            .build_config();
407
408        assert_eq!(config.sync.default_method, "vad");
409        assert!(config.sync.vad.enabled);
410        assert_eq!(config.sync.vad.sensitivity, 0.8);
411        assert_eq!(config.sync.vad.sample_rate, 32000);
412    }
413
414    #[test]
415    fn test_builder_service_creation() {
416        let service = TestConfigBuilder::new()
417            .with_ai_provider("test-provider")
418            .build_service();
419
420        let config = service.get_config().unwrap();
421        assert_eq!(config.ai.provider, "test-provider");
422    }
423
424    #[test]
425    fn test_builder_chaining() {
426        let config = TestConfigBuilder::new()
427            .with_ai_provider("openai")
428            .with_ai_model("gpt-4.1")
429            .with_sync_method("vad")
430            .with_vad_sensitivity(0.5)
431            .with_max_concurrent_jobs(8)
432            .with_task_queue_size(200)
433            .build_config();
434
435        assert_eq!(config.ai.provider, "openai");
436        assert_eq!(config.ai.model, "gpt-4.1");
437        assert_eq!(config.sync.default_method, "vad");
438        assert_eq!(config.sync.vad.sensitivity, 0.5);
439        assert_eq!(config.general.max_concurrent_jobs, 8);
440        assert_eq!(config.parallel.task_queue_size, 200);
441    }
442}