Skip to main content

project_examer/
simple_parser.rs

1use crate::file_discovery::FileInfo;
2use anyhow::Result;
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ParsedFile {
9    pub file_info: FileInfo,
10    pub imports: Vec<Import>,
11    pub exports: Vec<Export>,
12    pub functions: Vec<Function>,
13    pub classes: Vec<Class>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Import {
18    pub module: String,
19    pub items: Vec<String>,
20    pub is_default: bool,
21    pub line_number: usize,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Export {
26    pub name: String,
27    pub is_default: bool,
28    pub line_number: usize,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Function {
33    pub name: String,
34    pub parameters: Vec<String>,
35    pub return_type: Option<String>,
36    pub line_number: usize,
37    pub is_async: bool,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct Class {
42    pub name: String,
43    pub extends: Option<String>,
44    pub implements: Vec<String>,
45    pub methods: Vec<Function>,
46    pub line_number: usize,
47}
48
49pub struct SimpleParser {
50    language_patterns: HashMap<String, LanguagePatterns>,
51}
52
53struct LanguagePatterns {
54    import_patterns: Vec<Regex>,
55    export_patterns: Vec<Regex>,
56    function_patterns: Vec<Regex>,
57    class_patterns: Vec<Regex>,
58}
59
60impl SimpleParser {
61    pub fn new() -> Result<Self> {
62        let mut language_patterns = HashMap::new();
63        
64        // JavaScript/TypeScript patterns
65        language_patterns.insert("javascript".to_string(), LanguagePatterns {
66            import_patterns: vec![
67                Regex::new(r#"import\s+.*?\s+from\s+['"]([^'"]+)['"]"#)?,
68                Regex::new(r#"import\s+['"]([^'"]+)['"]"#)?,
69                Regex::new(r#"const\s+.*?\s*=\s*require\s*\(\s*['"]([^'"]+)['"]"#)?,
70            ],
71            export_patterns: vec![
72                Regex::new(r"export\s+(function|class|const|let|var)\s+(\w+)")?,
73                Regex::new(r"export\s+default\s+(\w+)")?,
74                Regex::new(r"export\s*\{\s*([^}]+)\s*\}")?,
75            ],
76            function_patterns: vec![
77                Regex::new(r"function\s+(\w+)\s*\(([^)]*)\)")?,
78                Regex::new(r"(\w+)\s*:\s*function\s*\(([^)]*)\)")?,
79                Regex::new(r"(\w+)\s*=>\s*")?,
80                Regex::new(r"(async\s+)?function\s+(\w+)")?,
81            ],
82            class_patterns: vec![
83                Regex::new(r"class\s+(\w+)(?:\s+extends\s+(\w+))?")?,
84            ],
85        });
86        
87        // TypeScript (same as JavaScript with some additions)
88        language_patterns.insert("typescript".to_string(), language_patterns["javascript"].clone());
89        
90        // Python patterns
91        language_patterns.insert("python".to_string(), LanguagePatterns {
92            import_patterns: vec![
93                Regex::new(r"from\s+([^\s]+)\s+import")?,
94                Regex::new(r"import\s+([^\s,]+)")?,
95            ],
96            export_patterns: vec![
97                Regex::new(r"__all__\s*=\s*\[([^\]]+)\]")?,
98            ],
99            function_patterns: vec![
100                Regex::new(r"def\s+(\w+)\s*\(([^)]*)\)")?,
101                Regex::new(r"async\s+def\s+(\w+)\s*\(([^)]*)\)")?,
102            ],
103            class_patterns: vec![
104                Regex::new(r"class\s+(\w+)(?:\(([^)]+)\))?")?,
105            ],
106        });
107        
108        // Rust patterns
109        language_patterns.insert("rust".to_string(), LanguagePatterns {
110            import_patterns: vec![
111                Regex::new(r"use\s+([^;]+);")?,
112                Regex::new(r"extern\s+crate\s+(\w+)")?,
113            ],
114            export_patterns: vec![
115                Regex::new(r"pub\s+(fn|struct|enum|trait|mod)\s+(\w+)")?,
116            ],
117            function_patterns: vec![
118                Regex::new(r"fn\s+(\w+)\s*\(([^)]*)\)")?,
119                Regex::new(r"pub\s+fn\s+(\w+)\s*\(([^)]*)\)")?,
120                Regex::new(r"async\s+fn\s+(\w+)")?,
121            ],
122            class_patterns: vec![
123                Regex::new(r"struct\s+(\w+)")?,
124                Regex::new(r"enum\s+(\w+)")?,
125                Regex::new(r"trait\s+(\w+)")?,
126            ],
127        });
128        
129        Ok(Self { language_patterns })
130    }
131
132    pub fn parse_file(&self, file_info: &FileInfo) -> Result<ParsedFile> {
133        let content = std::fs::read_to_string(&file_info.path)?;
134        
135        let default_language = "unknown".to_string();
136        let language = file_info.language.as_ref()
137            .unwrap_or(&default_language);
138
139        let patterns = self.language_patterns.get(language);
140        
141        let mut parsed_file = ParsedFile {
142            file_info: file_info.clone(),
143            imports: Vec::new(),
144            exports: Vec::new(),
145            functions: Vec::new(),
146            classes: Vec::new(),
147        };
148
149        if let Some(patterns) = patterns {
150            self.extract_imports(&content, patterns, &mut parsed_file)?;
151            self.extract_exports(&content, patterns, &mut parsed_file)?;
152            self.extract_functions(&content, patterns, &mut parsed_file)?;
153            self.extract_classes(&content, patterns, &mut parsed_file)?;
154        } else {
155            // Fallback: basic pattern matching for unknown languages
156            self.extract_basic_patterns(&content, &mut parsed_file)?;
157        }
158
159        Ok(parsed_file)
160    }
161
162    fn extract_imports(&self, content: &str, patterns: &LanguagePatterns, parsed_file: &mut ParsedFile) -> Result<()> {
163        for (line_num, line) in content.lines().enumerate() {
164            for pattern in &patterns.import_patterns {
165                if let Some(captures) = pattern.captures(line) {
166                    if let Some(module) = captures.get(1) {
167                        parsed_file.imports.push(Import {
168                            module: module.as_str().to_string(),
169                            items: Vec::new(),
170                            is_default: false,
171                            line_number: line_num + 1,
172                        });
173                    }
174                }
175            }
176        }
177        Ok(())
178    }
179
180    fn extract_exports(&self, content: &str, patterns: &LanguagePatterns, parsed_file: &mut ParsedFile) -> Result<()> {
181        for (line_num, line) in content.lines().enumerate() {
182            for pattern in &patterns.export_patterns {
183                if let Some(captures) = pattern.captures(line) {
184                    if let Some(name) = captures.get(captures.len() - 1) {
185                        parsed_file.exports.push(Export {
186                            name: name.as_str().to_string(),
187                            is_default: line.contains("default"),
188                            line_number: line_num + 1,
189                        });
190                    }
191                }
192            }
193        }
194        Ok(())
195    }
196
197    fn extract_functions(&self, content: &str, patterns: &LanguagePatterns, parsed_file: &mut ParsedFile) -> Result<()> {
198        for (line_num, line) in content.lines().enumerate() {
199            for pattern in &patterns.function_patterns {
200                if let Some(captures) = pattern.captures(line) {
201                    let is_async = line.contains("async");
202                    let name = if captures.len() > 2 {
203                        captures.get(2).map(|m| m.as_str()).unwrap_or("unknown")
204                    } else {
205                        captures.get(1).map(|m| m.as_str()).unwrap_or("unknown")
206                    };
207                    
208                    let params = if captures.len() > 2 {
209                        captures.get(captures.len() - 1)
210                    } else {
211                        captures.get(2)
212                    };
213                    
214                    let parameters = if let Some(params) = params {
215                        self.parse_parameters(params.as_str())
216                    } else {
217                        Vec::new()
218                    };
219
220                    parsed_file.functions.push(Function {
221                        name: name.to_string(),
222                        parameters,
223                        return_type: None,
224                        line_number: line_num + 1,
225                        is_async,
226                    });
227                }
228            }
229        }
230        Ok(())
231    }
232
233    fn extract_classes(&self, content: &str, patterns: &LanguagePatterns, parsed_file: &mut ParsedFile) -> Result<()> {
234        for (line_num, line) in content.lines().enumerate() {
235            for pattern in &patterns.class_patterns {
236                if let Some(captures) = pattern.captures(line) {
237                    if let Some(name) = captures.get(1) {
238                        let extends = captures.get(2).map(|m| m.as_str().to_string());
239                        
240                        parsed_file.classes.push(Class {
241                            name: name.as_str().to_string(),
242                            extends,
243                            implements: Vec::new(),
244                            methods: Vec::new(),
245                            line_number: line_num + 1,
246                        });
247                    }
248                }
249            }
250        }
251        Ok(())
252    }
253
254    fn extract_basic_patterns(&self, content: &str, parsed_file: &mut ParsedFile) -> Result<()> {
255        // Basic patterns that work across languages
256        let import_patterns = [
257            r#"import.*['"]([^'"]+)['"]"#,
258            r#"#include\s*[<"]([^>"]+)[>"]"#,
259            r#"require\s*\(['"]([^'"]+)['"]\)"#,
260        ];
261        
262        let function_patterns = [
263            r"(function|def|fn)\s+(\w+)",
264            r"(\w+)\s*\(",
265        ];
266
267        for (line_num, line) in content.lines().enumerate() {
268            // Try to find imports
269            for pattern_str in &import_patterns {
270                if let Ok(pattern) = Regex::new(pattern_str) {
271                    if let Some(captures) = pattern.captures(line) {
272                        if let Some(module) = captures.get(1) {
273                            parsed_file.imports.push(Import {
274                                module: module.as_str().to_string(),
275                                items: Vec::new(),
276                                is_default: false,
277                                line_number: line_num + 1,
278                            });
279                        }
280                    }
281                }
282            }
283            
284            // Try to find functions
285            for pattern_str in &function_patterns {
286                if let Ok(pattern) = Regex::new(pattern_str) {
287                    if let Some(captures) = pattern.captures(line) {
288                        if let Some(name) = captures.get(2).or(captures.get(1)) {
289                            parsed_file.functions.push(Function {
290                                name: name.as_str().to_string(),
291                                parameters: Vec::new(),
292                                return_type: None,
293                                line_number: line_num + 1,
294                                is_async: line.contains("async"),
295                            });
296                        }
297                    }
298                }
299            }
300        }
301        
302        Ok(())
303    }
304
305    fn parse_parameters(&self, params_str: &str) -> Vec<String> {
306        params_str
307            .split(',')
308            .map(|p| {
309                // Extract parameter name (before : or = if present)
310                p.trim()
311                    .split(':')
312                    .next()
313                    .unwrap_or(p.trim())
314                    .split('=')
315                    .next()
316                    .unwrap_or(p.trim())
317                    .trim()
318                    .to_string()
319            })
320            .filter(|p| !p.is_empty())
321            .collect()
322    }
323
324    pub fn get_dependencies(&self, parsed_file: &ParsedFile) -> Vec<String> {
325        parsed_file.imports.iter()
326            .map(|import| import.module.clone())
327            .collect()
328    }
329}
330
331impl Clone for LanguagePatterns {
332    fn clone(&self) -> Self {
333        Self {
334            import_patterns: self.import_patterns.iter().map(|r| Regex::new(r.as_str()).unwrap()).collect(),
335            export_patterns: self.export_patterns.iter().map(|r| Regex::new(r.as_str()).unwrap()).collect(),
336            function_patterns: self.function_patterns.iter().map(|r| Regex::new(r.as_str()).unwrap()).collect(),
337            class_patterns: self.class_patterns.iter().map(|r| Regex::new(r.as_str()).unwrap()).collect(),
338        }
339    }
340}