ricecoder_refactoring/safety/
checker.rs

1//! Safety validation for refactoring operations
2
3use crate::error::Result;
4use crate::types::{Refactoring, ValidationResult};
5
6/// Validates safety of refactoring operations
7pub struct SafetyChecker;
8
9impl SafetyChecker {
10    /// Create a new safety checker
11    pub fn new() -> Self {
12        Self
13    }
14
15    /// Check if a refactoring is safe to apply
16    pub fn check(refactoring: &Refactoring) -> Result<ValidationResult> {
17        let mut errors = vec![];
18        let mut warnings = vec![];
19
20        // Check if target file exists
21        if !refactoring.target.file.exists() {
22            errors.push(format!(
23                "Target file does not exist: {}",
24                refactoring.target.file.display()
25            ));
26        }
27
28        // Check if symbol is not empty
29        if refactoring.target.symbol.is_empty() {
30            errors.push("Symbol name cannot be empty".to_string());
31        }
32
33        // Check for potential issues
34        if refactoring.target.symbol.len() > 100 {
35            warnings.push("Symbol name is very long".to_string());
36        }
37
38        Ok(ValidationResult {
39            passed: errors.is_empty(),
40            errors,
41            warnings,
42        })
43    }
44
45    /// Validate that changes don't break the code
46    pub fn validate_changes(original: &str, new: &str) -> Result<ValidationResult> {
47        let mut errors = vec![];
48        let mut warnings = vec![];
49
50        // Check if content is not empty
51        if new.is_empty() {
52            errors.push("Refactored code cannot be empty".to_string());
53        }
54
55        // Check if content changed
56        if original == new {
57            warnings.push("No changes were made".to_string());
58        }
59
60        Ok(ValidationResult {
61            passed: errors.is_empty(),
62            errors,
63            warnings,
64        })
65    }
66}
67
68impl Default for SafetyChecker {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::types::{RefactoringOptions, RefactoringTarget, RefactoringType};
78    use std::path::PathBuf;
79    use tempfile::NamedTempFile;
80
81    #[test]
82    fn test_check_refactoring_invalid_file() -> Result<()> {
83        let refactoring = Refactoring {
84            id: "test".to_string(),
85            refactoring_type: RefactoringType::Rename,
86            target: RefactoringTarget {
87                file: PathBuf::from("/nonexistent/file.rs"),
88                symbol: "test".to_string(),
89                range: None,
90            },
91            options: RefactoringOptions::default(),
92        };
93
94        let result = SafetyChecker::check(&refactoring)?;
95        assert!(!result.passed);
96        assert!(!result.errors.is_empty());
97
98        Ok(())
99    }
100
101    #[test]
102    fn test_check_refactoring_empty_symbol() -> Result<()> {
103        let file = NamedTempFile::new()?;
104        let refactoring = Refactoring {
105            id: "test".to_string(),
106            refactoring_type: RefactoringType::Rename,
107            target: RefactoringTarget {
108                file: file.path().to_path_buf(),
109                symbol: "".to_string(),
110                range: None,
111            },
112            options: RefactoringOptions::default(),
113        };
114
115        let result = SafetyChecker::check(&refactoring)?;
116        assert!(!result.passed);
117
118        Ok(())
119    }
120
121    #[test]
122    fn test_validate_changes_empty_new() -> Result<()> {
123        let result = SafetyChecker::validate_changes("original", "")?;
124        assert!(!result.passed);
125
126        Ok(())
127    }
128
129    #[test]
130    fn test_validate_changes_no_change() -> Result<()> {
131        let result = SafetyChecker::validate_changes("same", "same")?;
132        assert!(result.passed);
133        assert!(!result.warnings.is_empty());
134
135        Ok(())
136    }
137}