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 language_patterns.insert("javascript".to_string(), LanguagePatterns {
66 import_patterns: vec['"]"#)?,
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 language_patterns.insert("typescript".to_string(), language_patterns["javascript"].clone());
89
90 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 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 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 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 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 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 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}