ricecoder_refactoring/patterns/
validator.rs

1//! Pattern validation
2
3use crate::error::{RefactoringError, Result};
4use super::RefactoringPattern;
5
6/// Validates refactoring patterns
7pub struct PatternValidator;
8
9impl PatternValidator {
10    /// Validate a pattern
11    pub fn validate(pattern: &RefactoringPattern) -> Result<()> {
12        // Check pattern name
13        if pattern.name.is_empty() {
14            return Err(RefactoringError::InvalidConfiguration(
15                "Pattern name cannot be empty".to_string(),
16            ));
17        }
18
19        // Check pattern template
20        if pattern.template.is_empty() {
21            return Err(RefactoringError::InvalidConfiguration(
22                "Pattern template cannot be empty".to_string(),
23            ));
24        }
25
26        // Check that all placeholders in template have corresponding parameters
27        let template_placeholders = Self::extract_placeholders(&pattern.template);
28        let parameter_placeholders: std::collections::HashSet<_> =
29            pattern.parameters.iter().map(|p| p.placeholder.clone()).collect();
30
31        for placeholder in template_placeholders {
32            if !parameter_placeholders.contains(&placeholder) {
33                return Err(RefactoringError::InvalidConfiguration(format!(
34                    "Template uses placeholder {} but no parameter defined",
35                    placeholder
36                )));
37            }
38        }
39
40        // Check that all parameters are used in template
41        for param in &pattern.parameters {
42            if !pattern.template.contains(&param.placeholder) {
43                return Err(RefactoringError::InvalidConfiguration(format!(
44                    "Parameter {} is defined but not used in template",
45                    param.name
46                )));
47            }
48        }
49
50        Ok(())
51    }
52
53    /// Extract placeholders from a template
54    fn extract_placeholders(template: &str) -> Vec<String> {
55        let mut placeholders = vec![];
56        let mut chars = template.chars().peekable();
57
58        while let Some(ch) = chars.next() {
59            if ch == '{' && chars.peek() == Some(&'{') {
60                chars.next(); // consume second {
61                let mut placeholder = String::from("{{");
62
63                while let Some(ch) = chars.next() {
64                    placeholder.push(ch);
65                    if ch == '}' && chars.peek() == Some(&'}') {
66                        chars.next(); // consume second }
67                        placeholder.push('}');
68                        placeholders.push(placeholder);
69                        break;
70                    }
71                }
72            }
73        }
74
75        placeholders
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::patterns::{RefactoringPattern, PatternParameter, PatternScope};
83
84    #[test]
85    fn test_validate_valid_pattern() -> Result<()> {
86        let pattern = RefactoringPattern {
87            name: "test".to_string(),
88            description: "Test pattern".to_string(),
89            template: "fn {{old_name}}() -> fn {{new_name}}()".to_string(),
90            parameters: vec![
91                PatternParameter {
92                    name: "old_name".to_string(),
93                    placeholder: "{{old_name}}".to_string(),
94                    description: "Old name".to_string(),
95                },
96                PatternParameter {
97                    name: "new_name".to_string(),
98                    placeholder: "{{new_name}}".to_string(),
99                    description: "New name".to_string(),
100                },
101            ],
102            scope: PatternScope::Global,
103        };
104
105        PatternValidator::validate(&pattern)?;
106        Ok(())
107    }
108
109    #[test]
110    fn test_validate_empty_name() {
111        let pattern = RefactoringPattern {
112            name: "".to_string(),
113            description: "Test pattern".to_string(),
114            template: "template".to_string(),
115            parameters: vec![],
116            scope: PatternScope::Global,
117        };
118
119        assert!(PatternValidator::validate(&pattern).is_err());
120    }
121
122    #[test]
123    fn test_validate_empty_template() {
124        let pattern = RefactoringPattern {
125            name: "test".to_string(),
126            description: "Test pattern".to_string(),
127            template: "".to_string(),
128            parameters: vec![],
129            scope: PatternScope::Global,
130        };
131
132        assert!(PatternValidator::validate(&pattern).is_err());
133    }
134
135    #[test]
136    fn test_validate_unused_placeholder() {
137        let pattern = RefactoringPattern {
138            name: "test".to_string(),
139            description: "Test pattern".to_string(),
140            template: "fn {{old_name}}()".to_string(),
141            parameters: vec![
142                PatternParameter {
143                    name: "old_name".to_string(),
144                    placeholder: "{{old_name}}".to_string(),
145                    description: "Old name".to_string(),
146                },
147                PatternParameter {
148                    name: "new_name".to_string(),
149                    placeholder: "{{new_name}}".to_string(),
150                    description: "New name".to_string(),
151                },
152            ],
153            scope: PatternScope::Global,
154        };
155
156        assert!(PatternValidator::validate(&pattern).is_err());
157    }
158
159    #[test]
160    fn test_extract_placeholders() {
161        let template = "fn {{old_name}}() -> fn {{new_name}}()";
162        let placeholders = PatternValidator::extract_placeholders(template);
163
164        assert_eq!(placeholders.len(), 2);
165        assert!(placeholders.contains(&"{{old_name}}".to_string()));
166        assert!(placeholders.contains(&"{{new_name}}".to_string()));
167    }
168}