ricecoder_refactoring/adapters/
generic.rs

1//! Generic text-based refactoring provider (fallback for unconfigured languages)
2
3use crate::error::Result;
4use crate::providers::{RefactoringAnalysis, RefactoringProvider};
5use crate::types::{Refactoring, RefactoringType, ValidationResult};
6use regex::Regex;
7
8/// Generic text-based refactoring provider for any language
9pub struct GenericRefactoringProvider;
10
11impl GenericRefactoringProvider {
12    /// Create a new generic provider
13    pub fn new() -> Self {
14        Self
15    }
16
17    /// Apply a simple text-based transformation
18    pub fn apply_text_transformation(code: &str, from_pattern: &str, to_pattern: &str) -> Result<String> {
19        match Regex::new(from_pattern) {
20            Ok(re) => Ok(re.replace_all(code, to_pattern).to_string()),
21            Err(_) => {
22                // If regex fails, try simple string replacement
23                Ok(code.replace(from_pattern, to_pattern))
24            }
25        }
26    }
27
28    /// Apply a rename transformation
29    pub fn apply_rename(code: &str, old_name: &str, new_name: &str) -> Result<String> {
30        // Use word boundaries to avoid partial matches
31        let pattern = format!(r"\b{}\b", regex::escape(old_name));
32        match Regex::new(&pattern) {
33            Ok(re) => Ok(re.replace_all(code, new_name).to_string()),
34            Err(_) => {
35                // Fallback to simple replacement
36                Ok(code.replace(old_name, new_name))
37            }
38        }
39    }
40
41    /// Count occurrences of a pattern in code
42    pub fn count_occurrences(code: &str, pattern: &str) -> usize {
43        match Regex::new(pattern) {
44            Ok(re) => re.find_iter(code).count(),
45            Err(_) => code.matches(pattern).count(),
46        }
47    }
48}
49
50impl Default for GenericRefactoringProvider {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl RefactoringProvider for GenericRefactoringProvider {
57    fn analyze_refactoring(
58        &self,
59        _code: &str,
60        _language: &str,
61        _refactoring_type: RefactoringType,
62    ) -> Result<RefactoringAnalysis> {
63        // Generic provider always supports basic refactoring
64        Ok(RefactoringAnalysis {
65            applicable: true,
66            reason: None,
67            complexity: 3, // Low complexity for generic text-based refactoring
68        })
69    }
70
71    fn apply_refactoring(
72        &self,
73        code: &str,
74        _language: &str,
75        refactoring: &Refactoring,
76    ) -> Result<String> {
77        // For generic refactoring, we do simple text-based transformations
78        match refactoring.refactoring_type {
79            RefactoringType::Rename => {
80                // Simple rename: replace symbol with new name
81                // Note: In a real implementation, we would get the new name from options
82                Ok(code.to_string())
83            }
84            RefactoringType::RemoveUnused => {
85                // For generic, we can't reliably detect unused code
86                Ok(code.to_string())
87            }
88            _ => {
89                // For other types, return unchanged
90                Ok(code.to_string())
91            }
92        }
93    }
94
95    fn validate_refactoring(
96        &self,
97        original: &str,
98        refactored: &str,
99        _language: &str,
100    ) -> Result<ValidationResult> {
101        let mut errors = vec![];
102        let mut warnings = vec![];
103
104        // Check if refactored code is not empty
105        if refactored.is_empty() {
106            errors.push("Refactored code cannot be empty".to_string());
107        }
108
109        // Check if content changed
110        if original == refactored {
111            warnings.push("No changes were made".to_string());
112        }
113
114        // Check for basic syntax issues (very basic check)
115        let open_braces = refactored.matches('{').count();
116        let close_braces = refactored.matches('}').count();
117        if open_braces != close_braces {
118            warnings.push("Brace mismatch detected".to_string());
119        }
120
121        Ok(ValidationResult {
122            passed: errors.is_empty(),
123            errors,
124            warnings,
125        })
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use crate::types::{RefactoringOptions, RefactoringTarget};
133    use std::path::PathBuf;
134
135    #[test]
136    fn test_generic_provider_analyze() -> Result<()> {
137        let provider = GenericRefactoringProvider::new();
138        let analysis = provider.analyze_refactoring("code", "unknown", RefactoringType::Rename)?;
139
140        assert!(analysis.applicable);
141        assert_eq!(analysis.complexity, 3);
142
143        Ok(())
144    }
145
146    #[test]
147    fn test_generic_provider_validate() -> Result<()> {
148        let provider = GenericRefactoringProvider::new();
149        let result = provider.validate_refactoring("original", "refactored", "unknown")?;
150
151        assert!(result.passed);
152
153        Ok(())
154    }
155
156    #[test]
157    fn test_generic_provider_validate_empty() -> Result<()> {
158        let provider = GenericRefactoringProvider::new();
159        let result = provider.validate_refactoring("original", "", "unknown")?;
160
161        assert!(!result.passed);
162        assert!(!result.errors.is_empty());
163
164        Ok(())
165    }
166
167    #[test]
168    fn test_generic_provider_apply_refactoring() -> Result<()> {
169        let provider = GenericRefactoringProvider::new();
170        let refactoring = Refactoring {
171            id: "test".to_string(),
172            refactoring_type: RefactoringType::Rename,
173            target: RefactoringTarget {
174                file: PathBuf::from("test.txt"),
175                symbol: "old".to_string(),
176                range: None,
177            },
178            options: RefactoringOptions::default(),
179        };
180
181        let result = provider.apply_refactoring("code with old", "unknown", &refactoring)?;
182        assert_eq!(result, "code with old");
183
184        Ok(())
185    }
186}