ricecoder_refactoring/safety/
checker.rs1use crate::error::Result;
4use crate::types::{Refactoring, ValidationResult};
5
6pub struct SafetyChecker;
8
9impl SafetyChecker {
10 pub fn new() -> Self {
12 Self
13 }
14
15 pub fn check(refactoring: &Refactoring) -> Result<ValidationResult> {
17 let mut errors = vec![];
18 let mut warnings = vec![];
19
20 if !refactoring.target.file.exists() {
22 errors.push(format!(
23 "Target file does not exist: {}",
24 refactoring.target.file.display()
25 ));
26 }
27
28 if refactoring.target.symbol.is_empty() {
30 errors.push("Symbol name cannot be empty".to_string());
31 }
32
33 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 pub fn validate_changes(original: &str, new: &str) -> Result<ValidationResult> {
47 let mut errors = vec![];
48 let mut warnings = vec![];
49
50 if new.is_empty() {
52 errors.push("Refactored code cannot be empty".to_string());
53 }
54
55 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}