ricecoder_refactoring/providers/
mod.rs

1//! Provider traits and implementations for language-specific refactoring
2//!
3//! This module provides traits and implementations for language-specific refactoring providers,
4//! including LSP-based providers and configuration-driven providers.
5
6pub mod lsp;
7pub mod lsp_integration;
8pub mod lsp_watcher;
9
10pub use lsp::{LspProvider, LspProviderRegistry};
11pub use lsp_integration::{LspIntegration, LspServerInfo};
12pub use lsp_watcher::{ConfigurationWatcher, LspWatcher};
13
14use crate::error::Result;
15use crate::types::{Refactoring, RefactoringType, ValidationResult};
16use std::sync::Arc;
17
18/// Trait for language-specific refactoring providers
19pub trait RefactoringProvider: Send + Sync {
20    /// Analyze a refactoring operation
21    fn analyze_refactoring(
22        &self,
23        code: &str,
24        language: &str,
25        refactoring_type: RefactoringType,
26    ) -> Result<RefactoringAnalysis>;
27
28    /// Apply a refactoring to code
29    fn apply_refactoring(
30        &self,
31        code: &str,
32        language: &str,
33        refactoring: &Refactoring,
34    ) -> Result<String>;
35
36    /// Validate refactored code
37    fn validate_refactoring(
38        &self,
39        original: &str,
40        refactored: &str,
41        language: &str,
42    ) -> Result<ValidationResult>;
43}
44
45/// Analysis result for a refactoring
46#[derive(Debug, Clone)]
47pub struct RefactoringAnalysis {
48    /// Whether the refactoring is applicable
49    pub applicable: bool,
50    /// Reason if not applicable
51    pub reason: Option<String>,
52    /// Estimated complexity (1-10)
53    pub complexity: u8,
54}
55
56/// Registry for refactoring providers
57///
58/// Manages language-specific refactoring providers with support for:
59/// - LSP-based providers (highest priority)
60/// - Configured providers (from YAML/JSON configuration)
61/// - Built-in language-specific providers
62/// - Generic text-based fallback provider
63#[derive(Clone)]
64pub struct ProviderRegistry {
65    lsp_providers: Arc<LspProviderRegistry>,
66    providers: std::sync::Arc<std::sync::Mutex<std::collections::HashMap<String, Arc<dyn RefactoringProvider>>>>,
67    generic_provider: Arc<dyn RefactoringProvider>,
68}
69
70impl ProviderRegistry {
71    /// Create a new provider registry
72    pub fn new(generic_provider: Arc<dyn RefactoringProvider>) -> Self {
73        Self {
74            lsp_providers: Arc::new(LspProviderRegistry::new()),
75            providers: std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
76            generic_provider,
77        }
78    }
79
80    /// Create a new provider registry with LSP providers
81    pub fn with_lsp(
82        generic_provider: Arc<dyn RefactoringProvider>,
83        lsp_providers: Arc<LspProviderRegistry>,
84    ) -> Self {
85        Self {
86            lsp_providers,
87            providers: std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
88            generic_provider,
89        }
90    }
91
92    /// Get the LSP provider registry
93    pub fn lsp_providers(&self) -> Arc<LspProviderRegistry> {
94        self.lsp_providers.clone()
95    }
96
97    /// Register a provider for a language
98    pub fn register(&self, language: String, provider: Arc<dyn RefactoringProvider>) -> Result<()> {
99        let mut providers = self.providers.lock().map_err(|_| {
100            crate::error::RefactoringError::Other("Failed to acquire lock on provider registry".to_string())
101        })?;
102        providers.insert(language, provider);
103        Ok(())
104    }
105
106    /// Get a provider for a language using the priority chain:
107    /// 1. LSP provider (if available)
108    /// 2. Configured provider
109    /// 3. Generic fallback
110    pub fn get_provider(&self, language: &str) -> Arc<dyn RefactoringProvider> {
111        // Priority 1: Check for LSP provider
112        if self.lsp_providers.is_available(language) {
113            // In a real implementation, we'd wrap the LSP provider
114            // For now, we fall through to configured providers
115        }
116
117        // Priority 2: Check for configured provider
118        if let Ok(providers) = self.providers.lock() {
119            if let Some(provider) = providers.get(language) {
120                return provider.clone();
121            }
122        }
123
124        // Priority 3: Fall back to generic provider
125        self.generic_provider.clone()
126    }
127
128    /// Check if a language has a specific provider (configured or LSP)
129    pub fn has_provider(&self, language: &str) -> Result<bool> {
130        // Check LSP providers first
131        if self.lsp_providers.is_available(language) {
132            return Ok(true);
133        }
134
135        // Check configured providers
136        let providers = self.providers.lock().map_err(|_| {
137            crate::error::RefactoringError::Other("Failed to acquire lock on provider registry".to_string())
138        })?;
139        Ok(providers.contains_key(language))
140    }
141
142    /// Get all registered languages (configured + LSP)
143    pub fn get_languages(&self) -> Result<Vec<String>> {
144        let mut languages = Vec::new();
145
146        // Add LSP languages
147        if let Ok(lsp_langs) = self.lsp_providers.get_languages() {
148            languages.extend(lsp_langs);
149        }
150
151        // Add configured languages
152        let providers = self.providers.lock().map_err(|_| {
153            crate::error::RefactoringError::Other("Failed to acquire lock on provider registry".to_string())
154        })?;
155        languages.extend(providers.keys().cloned());
156
157        // Remove duplicates
158        languages.sort();
159        languages.dedup();
160
161        Ok(languages)
162    }
163
164    /// Register an LSP provider for a language
165    pub fn register_lsp_provider(
166        &self,
167        language: String,
168        provider: Arc<dyn LspProvider>,
169    ) -> Result<()> {
170        self.lsp_providers.register(language, provider)
171    }
172
173    /// Check if an LSP provider is available for a language
174    pub fn is_lsp_available(&self, language: &str) -> bool {
175        self.lsp_providers.is_available(language)
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    struct MockProvider;
184
185    impl RefactoringProvider for MockProvider {
186        fn analyze_refactoring(
187            &self,
188            _code: &str,
189            _language: &str,
190            _refactoring_type: RefactoringType,
191        ) -> Result<RefactoringAnalysis> {
192            Ok(RefactoringAnalysis {
193                applicable: true,
194                reason: None,
195                complexity: 5,
196            })
197        }
198
199        fn apply_refactoring(
200            &self,
201            code: &str,
202            _language: &str,
203            _refactoring: &Refactoring,
204        ) -> Result<String> {
205            Ok(code.to_string())
206        }
207
208        fn validate_refactoring(
209            &self,
210            _original: &str,
211            _refactored: &str,
212            _language: &str,
213        ) -> Result<ValidationResult> {
214            Ok(ValidationResult {
215                passed: true,
216                errors: vec![],
217                warnings: vec![],
218            })
219        }
220    }
221
222    struct MockLspProvider {
223        available: std::sync::Arc<std::sync::Mutex<bool>>,
224    }
225
226    impl LspProvider for MockLspProvider {
227        fn is_available(&self) -> bool {
228            self.available.lock().map(|a| *a).unwrap_or(false)
229        }
230
231        fn perform_refactoring(
232            &self,
233            code: &str,
234            _language: &str,
235            _refactoring: &Refactoring,
236        ) -> Result<String> {
237            Ok(code.to_string())
238        }
239
240        fn validate_refactoring(
241            &self,
242            _original: &str,
243            _refactored: &str,
244            _language: &str,
245        ) -> Result<ValidationResult> {
246            Ok(ValidationResult {
247                passed: true,
248                errors: vec![],
249                warnings: vec![],
250            })
251        }
252
253        fn on_availability_changed(&self, _callback: Box<dyn Fn(bool) + Send + Sync>) {
254            // Mock implementation
255        }
256    }
257
258    #[test]
259    fn test_provider_registry() -> Result<()> {
260        let generic: Arc<dyn RefactoringProvider> = Arc::new(MockProvider);
261        let registry = ProviderRegistry::new(generic.clone());
262
263        let rust_provider: Arc<dyn RefactoringProvider> = Arc::new(MockProvider);
264        registry.register("rust".to_string(), rust_provider.clone())?;
265
266        assert!(registry.has_provider("rust")?);
267        assert!(!registry.has_provider("python")?);
268
269        let _provider = registry.get_provider("rust");
270        // Verify that we got a provider (can't use ptr_eq with trait objects)
271        assert!(registry.has_provider("rust")?);
272
273        let _fallback = registry.get_provider("unknown");
274        // Verify that fallback returns the generic provider
275        assert!(!registry.has_provider("unknown")?);
276
277        Ok(())
278    }
279
280    #[test]
281    fn test_get_languages() -> Result<()> {
282        let generic = Arc::new(MockProvider);
283        let registry = ProviderRegistry::new(generic);
284
285        registry.register("rust".to_string(), Arc::new(MockProvider))?;
286        registry.register("typescript".to_string(), Arc::new(MockProvider))?;
287
288        let languages = registry.get_languages()?;
289        assert_eq!(languages.len(), 2);
290        assert!(languages.contains(&"rust".to_string()));
291        assert!(languages.contains(&"typescript".to_string()));
292
293        Ok(())
294    }
295
296    #[test]
297    fn test_lsp_provider_registration() -> Result<()> {
298        let generic = Arc::new(MockProvider);
299        let registry = ProviderRegistry::new(generic);
300
301        let available = std::sync::Arc::new(std::sync::Mutex::new(true));
302        let lsp_provider: Arc<dyn LspProvider> = Arc::new(MockLspProvider {
303            available: available.clone(),
304        });
305
306        registry.register_lsp_provider("rust".to_string(), lsp_provider)?;
307
308        assert!(registry.is_lsp_available("rust"));
309        assert!(!registry.is_lsp_available("python"));
310
311        Ok(())
312    }
313
314    #[test]
315    fn test_provider_priority_chain() -> Result<()> {
316        let generic = Arc::new(MockProvider);
317        let registry = ProviderRegistry::new(generic);
318
319        // Register a configured provider
320        registry.register("rust".to_string(), Arc::new(MockProvider))?;
321
322        // Both should be available
323        assert!(registry.has_provider("rust")?);
324
325        // Register an LSP provider
326        let available = std::sync::Arc::new(std::sync::Mutex::new(true));
327        let lsp_provider: Arc<dyn LspProvider> = Arc::new(MockLspProvider {
328            available: available.clone(),
329        });
330        registry.register_lsp_provider("rust".to_string(), lsp_provider)?;
331
332        // LSP should be available
333        assert!(registry.is_lsp_available("rust"));
334
335        Ok(())
336    }
337}