decrust_core/
syntax.rs

1/* src/syntax.rs */
2#![warn(missing_docs)]
3// ~=####====A===r===c===M===o===o===n====S===t===u===d===i===o===s====X|0|$>
4//! + [Syntax Generation Module]
5//!  - [AST-Aware Code Generation]
6//!  - [Template-Based Code Generation]
7//!  - [Syntax Tree Manipulation]
8// ~=####====A===r===c===M===o===o===n====S===t===u===d===i===o===s====X|0|$>
9
10//! This module provides utilities for generating and manipulating Rust code
11//! using the quote and syn crates. It enables AST-aware code generation for
12//! the autocorrection system.
13
14use super::types::ErrorCategory;
15use std::collections::HashMap;
16
17/// Template for generating code fixes
18#[derive(Debug, Clone)]
19pub struct FixTemplate {
20    /// Name of the template
21    pub name: String,
22    /// Description of what the template does
23    pub description: String,
24    /// The template code with placeholders
25    pub template: String,
26    /// The error categories this template applies to
27    pub applicable_categories: Vec<ErrorCategory>,
28    /// The error codes this template targets
29    pub target_error_codes: Vec<String>,
30}
31
32impl FixTemplate {
33    /// Creates a new FixTemplate
34    ///
35    /// # Parameters
36    /// * `name` - Name of the template
37    /// * `description` - Description of what the template does
38    /// * `template` - The template code with placeholders
39    pub fn new(
40        name: impl Into<String>,
41        description: impl Into<String>,
42        template: impl Into<String>,
43    ) -> Self {
44        Self {
45            name: name.into(),
46            description: description.into(),
47            template: template.into(),
48            applicable_categories: Vec::new(),
49            target_error_codes: Vec::new(),
50        }
51    }
52
53    /// Adds an applicable error category
54    ///
55    /// # Parameters
56    /// * `category` - Error category this template applies to
57    pub fn add_category(mut self, category: ErrorCategory) -> Self {
58        self.applicable_categories.push(category);
59        self
60    }
61
62    /// Adds a target error code
63    ///
64    /// # Parameters
65    /// * `code` - Error code this template targets
66    pub fn add_error_code(mut self, code: impl Into<String>) -> Self {
67        self.target_error_codes.push(code.into());
68        self
69    }
70
71    /// Applies the template with the given parameters
72    ///
73    /// # Parameters
74    /// * `params` - Map of parameter names to values
75    ///
76    /// # Returns
77    /// The template with placeholders replaced by parameter values
78    pub fn apply(&self, params: &HashMap<String, String>) -> String {
79        let mut result = self.template.clone();
80        for (key, value) in params {
81            result = result.replace(&format!("{{{}}}", key), value);
82        }
83        result
84    }
85}
86
87/// Utility for generating syntax-aware code using the quote crate
88#[derive(Debug, Default)]
89pub struct SyntaxGenerator;
90
91impl SyntaxGenerator {
92    /// Creates a new SyntaxGenerator
93    pub fn new() -> Self {
94        Self
95    }
96
97    /// Generates a trait implementation for a type
98    ///
99    /// # Parameters
100    /// * `trait_name` - Name of the trait to implement
101    /// * `type_name` - Name of the type to implement the trait for
102    /// * `methods` - Map of method names to method bodies
103    ///
104    /// # Returns
105    /// Generated trait implementation as a string
106    pub fn generate_trait_impl(
107        &self,
108        trait_name: &str,
109        type_name: &str,
110        methods: HashMap<String, String>,
111    ) -> String {
112        let mut impl_body = String::new();
113        for (method_name, method_body) in methods {
114            impl_body.push_str(&format!(
115                "    fn {}() {{\n        {}\n    }}\n\n",
116                method_name, method_body
117            ));
118        }
119
120        format!(
121            "impl {} for {} {{\n{}}}\n",
122            trait_name, type_name, impl_body
123        )
124    }
125
126    /// Generates an import statement
127    ///
128    /// # Parameters
129    /// * `path` - Path to import
130    /// * `items` - Items to import from the path
131    ///
132    /// # Returns
133    /// Generated import statement as a string
134    pub fn generate_import(&self, path: &str, items: &[&str]) -> String {
135        if items.is_empty() {
136            format!("use {};", path)
137        } else if items.len() == 1 {
138            format!("use {}::{};", path, items[0])
139        } else {
140            let items_str = items.join(", ");
141            format!("use {}::{{{}}};", path, items_str)
142        }
143    }
144
145    /// Generates a struct definition
146    ///
147    /// # Parameters
148    /// * `struct_name` - Name of the struct
149    /// * `fields` - Map of field names to field types
150    /// * `derive_traits` - Optional list of traits to derive
151    ///
152    /// # Returns
153    /// Generated struct definition as a string
154    pub fn generate_struct(
155        &self,
156        struct_name: &str,
157        fields: HashMap<String, String>,
158        derive_traits: Option<Vec<&str>>,
159    ) -> String {
160        let mut struct_def = String::new();
161
162        // Add derive attributes if provided
163        if let Some(traits) = derive_traits {
164            if !traits.is_empty() {
165                struct_def.push_str(&format!("#[derive({})]\n", traits.join(", ")));
166            }
167        }
168
169        struct_def.push_str(&format!("pub struct {} {{\n", struct_name));
170
171        // Add fields
172        for (field_name, field_type) in fields {
173            struct_def.push_str(&format!("    pub {}: {},\n", field_name, field_type));
174        }
175
176        struct_def.push_str("}\n");
177        struct_def
178    }
179
180    /// Generates an enum definition
181    ///
182    /// # Parameters
183    /// * `enum_name` - Name of the enum
184    /// * `variants` - Map of variant names to optional variant types
185    /// * `derive_traits` - Optional list of traits to derive
186    ///
187    /// # Returns
188    /// Generated enum definition as a string
189    pub fn generate_enum(
190        &self,
191        enum_name: &str,
192        variants: HashMap<String, Option<String>>,
193        derive_traits: Option<Vec<&str>>,
194    ) -> String {
195        let mut enum_def = String::new();
196
197        // Add derive attributes if provided
198        if let Some(traits) = derive_traits {
199            if !traits.is_empty() {
200                enum_def.push_str(&format!("#[derive({})]\n", traits.join(", ")));
201            }
202        }
203
204        enum_def.push_str(&format!("pub enum {} {{\n", enum_name));
205
206        // Add variants
207        for (variant_name, variant_type) in variants {
208            if let Some(type_str) = variant_type {
209                enum_def.push_str(&format!("    {}({}),\n", variant_name, type_str));
210            } else {
211                enum_def.push_str(&format!("    {},\n", variant_name));
212            }
213        }
214
215        enum_def.push_str("}\n");
216        enum_def
217    }
218
219    /// Generates a function definition
220    ///
221    /// # Parameters
222    /// * `fn_name` - Name of the function
223    /// * `params` - Map of parameter names to parameter types
224    /// * `return_type` - Optional return type
225    /// * `body` - Function body
226    ///
227    /// # Returns
228    /// Generated function definition as a string
229    pub fn generate_function(
230        &self,
231        fn_name: &str,
232        params: HashMap<String, String>,
233        return_type: Option<&str>,
234        body: &str,
235    ) -> String {
236        let mut param_str = String::new();
237        for (param_name, param_type) in params {
238            if !param_str.is_empty() {
239                param_str.push_str(", ");
240            }
241            param_str.push_str(&format!("{}: {}", param_name, param_type));
242        }
243
244        let return_type_str = if let Some(rt) = return_type {
245            format!(" -> {}", rt)
246        } else {
247            String::new()
248        };
249
250        format!(
251            "fn {}({}){} {{\n    {}\n}}\n",
252            fn_name, param_str, return_type_str, body
253        )
254    }
255
256    /// Generates a method call
257    ///
258    /// # Parameters
259    /// * `object` - Object to call the method on
260    /// * `method_name` - Name of the method to call
261    /// * `args` - Arguments to pass to the method
262    ///
263    /// # Returns
264    /// Generated method call as a string
265    pub fn generate_method_call(&self, object: &str, method_name: &str, args: &[&str]) -> String {
266        let args_str = args.join(", ");
267        format!("{}.{}({})", object, method_name, args_str)
268    }
269}
270
271/// Registry for fix templates
272#[derive(Debug, Default)]
273pub struct TemplateRegistry {
274    /// Map of template names to templates
275    templates: HashMap<String, FixTemplate>,
276}
277
278impl TemplateRegistry {
279    /// Creates a new TemplateRegistry
280    pub fn new() -> Self {
281        Self {
282            templates: HashMap::new(),
283        }
284    }
285
286    /// Registers a template
287    ///
288    /// # Parameters
289    /// * `template` - Template to register
290    pub fn register_template(&mut self, template: FixTemplate) {
291        self.templates.insert(template.name.clone(), template);
292    }
293
294    /// Gets a template by name
295    ///
296    /// # Parameters
297    /// * `name` - Name of the template to get
298    ///
299    /// # Returns
300    /// The template with the given name, or None if not found
301    pub fn get_template(&self, name: &str) -> Option<&FixTemplate> {
302        self.templates.get(name)
303    }
304
305    /// Gets all templates that apply to a given error category
306    ///
307    /// # Parameters
308    /// * `category` - Error category to filter by
309    ///
310    /// # Returns
311    /// Vector of templates that apply to the given category
312    pub fn get_templates_for_category(&self, category: ErrorCategory) -> Vec<&FixTemplate> {
313        self.templates
314            .values()
315            .filter(|t| t.applicable_categories.contains(&category))
316            .collect()
317    }
318
319    /// Gets all templates that target a given error code
320    ///
321    /// # Parameters
322    /// * `error_code` - Error code to filter by
323    ///
324    /// # Returns
325    /// Vector of templates that target the given error code
326    pub fn get_templates_for_error_code(&self, error_code: &str) -> Vec<&FixTemplate> {
327        self.templates
328            .values()
329            .filter(|t| t.target_error_codes.iter().any(|c| c == error_code))
330            .collect()
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use std::collections::HashMap;
338
339    #[test]
340    fn test_fix_template_creation_and_application() {
341        // Create a template with placeholders
342        let template = FixTemplate::new(
343            "test_template",
344            "A test template for fixing errors",
345            "fn {function_name}({param_name}: {param_type}) -> {return_type} {\n    // Implementation\n    {body}\n}"
346        )
347        .add_category(ErrorCategory::Validation)
348        .add_error_code("E0001");
349
350        // Verify template properties
351        assert_eq!(template.name, "test_template");
352        assert_eq!(template.description, "A test template for fixing errors");
353        assert_eq!(template.applicable_categories.len(), 1);
354        assert_eq!(template.applicable_categories[0], ErrorCategory::Validation);
355        assert_eq!(template.target_error_codes.len(), 1);
356        assert_eq!(template.target_error_codes[0], "E0001");
357
358        // Create parameters to apply to the template
359        let mut params = HashMap::new();
360        params.insert("function_name".to_string(), "process_data".to_string());
361        params.insert("param_name".to_string(), "data".to_string());
362        params.insert("param_type".to_string(), "String".to_string());
363        params.insert("return_type".to_string(), "Result<(), Error>".to_string());
364        params.insert("body".to_string(), "Ok(())".to_string());
365
366        // Apply the template with parameters
367        let result = template.apply(&params);
368
369        // Verify the result
370        let expected = "fn process_data(data: String) -> Result<(), Error> {\n    // Implementation\n    Ok(())\n}";
371        assert_eq!(result, expected);
372    }
373
374    #[test]
375    fn test_syntax_generator_code_generation() {
376        let generator = SyntaxGenerator::new();
377
378        // Test generating an import statement
379        let import_single = generator.generate_import("std::collections", &["HashMap"]);
380        assert_eq!(import_single, "use std::collections::HashMap;");
381
382        let import_multiple =
383            generator.generate_import("std::collections", &["HashMap", "HashSet", "BTreeMap"]);
384        assert_eq!(
385            import_multiple,
386            "use std::collections::{HashMap, HashSet, BTreeMap};"
387        );
388
389        let import_empty = generator.generate_import("std::collections", &[]);
390        assert_eq!(import_empty, "use std::collections;");
391
392        // Test generating a struct definition
393        let mut fields = HashMap::new();
394        fields.insert("name".to_string(), "String".to_string());
395        fields.insert("age".to_string(), "u32".to_string());
396        fields.insert("active".to_string(), "bool".to_string());
397
398        let struct_def = generator.generate_struct("User", fields, Some(vec!["Debug", "Clone"]));
399
400        // Verify struct definition contains expected elements
401        assert!(struct_def.contains("#[derive(Debug, Clone)]"));
402        assert!(struct_def.contains("pub struct User {"));
403        assert!(struct_def.contains("pub name: String,"));
404        assert!(struct_def.contains("pub age: u32,"));
405        assert!(struct_def.contains("pub active: bool,"));
406    }
407
408    #[test]
409    fn test_template_registry_operations() {
410        let mut registry = TemplateRegistry::new();
411
412        // Create templates
413        let template1 = FixTemplate::new(
414            "validation_fix",
415            "Fix for validation errors",
416            "// Validation fix for {field_name}",
417        )
418        .add_category(ErrorCategory::Validation)
419        .add_error_code("E0001");
420
421        let template2 = FixTemplate::new("io_fix", "Fix for IO errors", "// IO fix for {path}")
422            .add_category(ErrorCategory::Io)
423            .add_error_code("E0002");
424
425        // Register templates
426        registry.register_template(template1);
427        registry.register_template(template2);
428
429        // Test getting a template by name
430        let validation_template = registry.get_template("validation_fix");
431        assert!(validation_template.is_some());
432        assert_eq!(
433            validation_template.unwrap().description,
434            "Fix for validation errors"
435        );
436
437        // Test getting templates for a category
438        let io_templates = registry.get_templates_for_category(ErrorCategory::Io);
439        assert_eq!(io_templates.len(), 1);
440        assert_eq!(io_templates[0].name, "io_fix");
441
442        // Test getting templates for an error code
443        let e0001_templates = registry.get_templates_for_error_code("E0001");
444        assert_eq!(e0001_templates.len(), 1);
445        assert_eq!(e0001_templates[0].name, "validation_fix");
446    }
447
448    #[test]
449    fn test_syntax_integration() {
450        // Create a template registry
451        let mut registry = TemplateRegistry::new();
452
453        // Create a syntax generator
454        let generator = SyntaxGenerator::new();
455
456        // Generate a struct definition
457        let mut fields = HashMap::new();
458        fields.insert("id".to_string(), "u64".to_string());
459        fields.insert("name".to_string(), "String".to_string());
460
461        let struct_def = generator.generate_struct("User", fields, Some(vec!["Debug", "Clone"]));
462
463        // Create a template using the struct definition
464        let template = FixTemplate::new(
465            "user_struct_template",
466            "Template for User struct",
467            struct_def,
468        )
469        .add_category(ErrorCategory::Validation);
470
471        // Register the template
472        registry.register_template(template);
473
474        // Retrieve the template
475        let retrieved_template = registry.get_template("user_struct_template").unwrap();
476
477        // Verify the template contains the struct definition
478        assert!(retrieved_template.template.contains("pub struct User {"));
479        assert!(retrieved_template.template.contains("pub id: u64,"));
480        assert!(retrieved_template.template.contains("pub name: String,"));
481    }
482}