codemod_core/pattern/
validator.rs1use super::Pattern;
8
9#[derive(Debug, Clone)]
15pub struct ValidationResult {
16 pub is_valid: bool,
18 pub warnings: Vec<String>,
20 pub errors: Vec<String>,
22}
23
24impl ValidationResult {
25 fn ok() -> Self {
27 Self {
28 is_valid: true,
29 warnings: Vec::new(),
30 errors: Vec::new(),
31 }
32 }
33}
34
35pub struct PatternValidator;
42
43impl PatternValidator {
44 pub fn validate(pattern: &Pattern) -> crate::Result<ValidationResult> {
59 let mut result = ValidationResult::ok();
60
61 if pattern.before_template.trim().is_empty() {
64 result.errors.push("before_template is empty".into());
65 }
66 if pattern.after_template.trim().is_empty() {
67 result.errors.push("after_template is empty".into());
68 }
69 if pattern.language.trim().is_empty() {
70 result.errors.push("language is not specified".into());
71 }
72
73 for var in &pattern.variables {
75 if !pattern.before_template.contains(&var.name) {
76 result.errors.push(format!(
77 "Variable '{}' does not appear in before_template",
78 var.name
79 ));
80 }
81 }
82
83 if pattern.confidence < 0.1 {
85 result.errors.push(format!(
86 "Confidence score ({:.2}) is below the minimum threshold (0.1)",
87 pattern.confidence
88 ));
89 }
90
91 for var in &pattern.variables {
96 if !pattern.after_template.contains(&var.name) {
97 result.warnings.push(format!(
98 "Variable '{}' does not appear in after_template — captured value will be dropped",
99 var.name
100 ));
101 }
102 }
103
104 if pattern.confidence < 0.3 {
105 result.warnings.push(format!(
106 "Low confidence score ({:.2}); consider providing more examples",
107 pattern.confidence
108 ));
109 }
110
111 if pattern.before_template == pattern.after_template {
112 result.warnings.push(
113 "before_template and after_template are identical — no transformation will occur"
114 .into(),
115 );
116 }
117
118 let mut seen = std::collections::HashSet::new();
120 for var in &pattern.variables {
121 if !seen.insert(&var.name) {
122 result
123 .warnings
124 .push(format!("Duplicate variable name '{}'", var.name));
125 }
126 }
127
128 result.is_valid = result.errors.is_empty();
129 Ok(result)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::pattern::{Pattern, PatternVar};
137
138 #[test]
139 fn test_valid_pattern() {
140 let p = Pattern::new(
141 "foo($var1)".into(),
142 "bar($var1)".into(),
143 vec![PatternVar {
144 name: "$var1".into(),
145 node_type: None,
146 }],
147 "rust".into(),
148 0.9,
149 );
150 let res = PatternValidator::validate(&p).unwrap();
151 assert!(res.is_valid);
152 assert!(res.errors.is_empty());
153 }
154
155 #[test]
156 fn test_empty_before_template() {
157 let p = Pattern::new(
158 "".into(),
159 "bar($var1)".into(),
160 vec![PatternVar {
161 name: "$var1".into(),
162 node_type: None,
163 }],
164 "rust".into(),
165 0.9,
166 );
167 let res = PatternValidator::validate(&p).unwrap();
168 assert!(!res.is_valid);
169 assert!(res.errors.iter().any(|e| e.contains("before_template")));
170 }
171
172 #[test]
173 fn test_missing_variable_in_before() {
174 let p = Pattern::new(
175 "foo(x)".into(),
176 "bar($var1)".into(),
177 vec![PatternVar {
178 name: "$var1".into(),
179 node_type: None,
180 }],
181 "rust".into(),
182 0.9,
183 );
184 let res = PatternValidator::validate(&p).unwrap();
185 assert!(!res.is_valid);
186 assert!(res.errors.iter().any(|e| e.contains("$var1")));
187 }
188
189 #[test]
190 fn test_low_confidence_warning() {
191 let p = Pattern::new(
192 "foo($var1)".into(),
193 "bar($var1)".into(),
194 vec![PatternVar {
195 name: "$var1".into(),
196 node_type: None,
197 }],
198 "rust".into(),
199 0.2,
200 );
201 let res = PatternValidator::validate(&p).unwrap();
202 assert!(res.is_valid);
203 assert!(res.warnings.iter().any(|w| w.contains("confidence")));
204 }
205
206 #[test]
207 fn test_identical_templates_warning() {
208 let p = Pattern::new(
209 "foo($var1)".into(),
210 "foo($var1)".into(),
211 vec![PatternVar {
212 name: "$var1".into(),
213 node_type: None,
214 }],
215 "rust".into(),
216 0.9,
217 );
218 let res = PatternValidator::validate(&p).unwrap();
219 assert!(res.is_valid);
220 assert!(res.warnings.iter().any(|w| w.contains("identical")));
221 }
222}