cadi_scraper/
transformer.rs

1use crate::error::Result;
2use crate::parser::CodeAst;
3
4/// Transformer for language-specific AST extraction and transformation
5pub struct Transformer;
6
7#[derive(Debug, Clone)]
8pub struct TransformResult {
9    pub original_language: String,
10    pub transformed_language: Option<String>,
11    pub transformations_applied: Vec<String>,
12    pub optimizations: Vec<String>,
13    pub warnings: Vec<String>,
14}
15
16impl Transformer {
17    /// Transform AST to extract and enhance information
18    pub fn transform(ast: &CodeAst, target_language: Option<&str>) -> Result<TransformResult> {
19        let mut result = TransformResult {
20            original_language: ast.language.clone(),
21            transformed_language: target_language.map(String::from),
22            transformations_applied: Vec::new(),
23            optimizations: Vec::new(),
24            warnings: Vec::new(),
25        };
26
27        // Language-specific transformations
28        match ast.language.as_str() {
29            "rust" => {
30                Self::transform_rust(ast, &mut result)?;
31            }
32            "typescript" => {
33                Self::transform_typescript(ast, &mut result)?;
34            }
35            "python" => {
36                Self::transform_python(ast, &mut result)?;
37            }
38            _ => {
39                result.transformations_applied
40                    .push(format!("Basic transformation for {}", ast.language));
41            }
42        }
43
44        Ok(result)
45    }
46
47    fn transform_rust(ast: &CodeAst, result: &mut TransformResult) -> Result<()> {
48        // Check for common patterns and suggest optimizations
49        if !ast.functions.is_empty()
50            && ast.functions.iter().any(|f| f.contains("_async")) {
51                result.optimizations.push(
52                    "Consider using tokio runtime for async code optimization".to_string(),
53                );
54            }
55
56        if ast.traits.len() > 5 {
57            result.optimizations.push(
58                "High trait count detected - consider trait composition".to_string(),
59            );
60        }
61
62        result
63            .transformations_applied
64            .push("Applied Rust-specific AST transformation".to_string());
65
66        Ok(())
67    }
68
69    fn transform_typescript(ast: &CodeAst, result: &mut TransformResult) -> Result<()> {
70        // Check for TypeScript-specific patterns
71        if ast.interfaces.is_empty() && !ast.classes.is_empty() {
72            result
73                .warnings
74                .push("No interfaces defined - consider using interfaces for better type safety"
75                    .to_string());
76        }
77
78        if ast.functions.len() > ast.classes.len() {
79            result.optimizations.push(
80                "Consider using more OOP patterns with classes".to_string(),
81            );
82        }
83
84        result
85            .transformations_applied
86            .push("Applied TypeScript-specific AST transformation".to_string());
87
88        Ok(())
89    }
90
91    fn transform_python(ast: &CodeAst, result: &mut TransformResult) -> Result<()> {
92        // Check for Python-specific patterns
93        if ast.classes.is_empty() && !ast.functions.is_empty() {
94            result.warnings.push(
95                "Functional style detected - consider using classes for better organization"
96                    .to_string(),
97            );
98        }
99
100        result
101            .transformations_applied
102            .push("Applied Python-specific AST transformation".to_string());
103
104        Ok(())
105    }
106
107    /// Extract semantic features from code
108    pub fn extract_features(ast: &CodeAst) -> Vec<String> {
109        let mut features = Vec::new();
110
111        // Count metrics
112        if !ast.functions.is_empty() {
113            features.push(format!("defines_{}_{}_functions", ast.language, ast.functions.len()));
114        }
115
116        if !ast.classes.is_empty() {
117            features.push(format!("defines_{}_{}_classes", ast.language, ast.classes.len()));
118        }
119
120        if !ast.traits.is_empty() {
121            features.push(format!("uses_{}_{}_traits", ast.language, ast.traits.len()));
122        }
123
124        if !ast.imports.is_empty() {
125            features.push(format!("has_{}_{}_dependencies", ast.language, ast.imports.len()));
126        }
127
128        features
129    }
130
131    /// Check code quality metrics
132    pub fn compute_quality_metrics(ast: &CodeAst) -> CodeQualityMetrics {
133        CodeQualityMetrics {
134            cyclomatic_complexity_estimate: estimate_complexity(ast),
135            api_surface_size: ast.functions.len() + ast.classes.len() + ast.traits.len(),
136            dependency_count: ast.imports.len(),
137            modularity_score: compute_modularity(ast),
138        }
139    }
140}
141
142#[derive(Debug, Clone)]
143pub struct CodeQualityMetrics {
144    pub cyclomatic_complexity_estimate: f32,
145    pub api_surface_size: usize,
146    pub dependency_count: usize,
147    pub modularity_score: f32,
148}
149
150fn estimate_complexity(ast: &CodeAst) -> f32 {
151    // Simple estimation based on AST structure
152    let func_count = ast.functions.len() as f32;
153    let class_count = ast.classes.len() as f32;
154    let trait_count = ast.traits.len() as f32;
155
156    (func_count + (class_count * 2.0) + (trait_count * 1.5)).min(100.0)
157}
158
159fn compute_modularity(ast: &CodeAst) -> f32 {
160    // Score based on how well the code is modularized
161    let total_elements = ast.functions.len() + ast.classes.len() + ast.traits.len();
162
163    if total_elements == 0 {
164        return 0.0;
165    }
166
167    // Higher score for more classes/traits relative to functions
168    let class_trait_ratio = (ast.classes.len() + ast.traits.len()) as f32 / total_elements as f32;
169    (class_trait_ratio * 100.0).min(100.0)
170}