1use crate::error::{ParseError, Result};
8use crate::languages::{get_parser, LanguageParser};
9use crate::node::CodeNode;
10use std::fs;
11use std::path::Path;
12
13pub fn parse_file(path: &Path) -> Result<Vec<CodeNode>> {
31 let source = fs::read_to_string(path).map_err(|e| ParseError::io(path, e))?;
33
34 if source.is_empty() {
35 if path
37 .file_name()
38 .map(|n| n == "__init__.py")
39 .unwrap_or(false)
40 {
41 return Ok(vec![]); }
43 return Err(ParseError::EmptyFile(path.to_path_buf()));
44 }
45
46 let parser =
48 detect_language(path).ok_or_else(|| ParseError::UnsupportedLanguage(path.to_path_buf()))?;
49
50 let file_path = path.to_string_lossy().to_string();
52
53 parse_source(&source, &file_path, parser.as_ref())
54}
55
56pub fn parse_source(
61 source: &str,
62 file_path: &str,
63 lang_parser: &dyn LanguageParser,
64) -> Result<Vec<CodeNode>> {
65 let mut parser = tree_sitter::Parser::new();
67 parser
68 .set_language(&lang_parser.language())
69 .map_err(|e| ParseError::ParserError(format!("Failed to set language: {}", e)))?;
70
71 let tree = parser
73 .parse(source, None)
74 .ok_or_else(|| ParseError::ParserError("Tree-sitter returned no tree".into()))?;
75
76 let nodes = lang_parser.extract_nodes(&tree, source, file_path);
78
79 Ok(nodes)
80}
81
82pub fn detect_language(path: &Path) -> Option<Box<dyn LanguageParser>> {
86 let extension = path.extension()?.to_str()?;
87 get_parser(extension)
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::node::NodeKind;
94
95 #[test]
96 fn test_detect_language() {
97 assert!(detect_language(Path::new("foo.rs")).is_some());
98 assert!(detect_language(Path::new("bar.ts")).is_some());
99 assert!(detect_language(Path::new("baz.py")).is_some());
100 assert!(detect_language(Path::new("unknown.xyz")).is_none());
101 }
102
103 #[test]
104 fn test_parse_rust_source() {
105 let source = r#"
106 fn hello_world() {
107 println!("Hello!");
108 }
109
110 pub struct User {
111 name: String,
112 }
113 "#;
114
115 let parser = get_parser("rs").unwrap();
116 let nodes = parse_source(source, "test.rs", parser.as_ref()).unwrap();
117
118 assert!(nodes
120 .iter()
121 .any(|n| n.name == "hello_world" && n.kind == NodeKind::Function));
122 assert!(nodes
123 .iter()
124 .any(|n| n.name == "User" && n.kind == NodeKind::Struct));
125 }
126
127 #[test]
128 fn test_parse_typescript_source() {
129 let source = r#"
130 export function greet(name: string): string {
131 return `Hello, ${name}!`;
132 }
133
134 export class UserService {
135 validate() {}
136 }
137 "#;
138
139 let parser = get_parser("ts").unwrap();
140 let nodes = parse_source(source, "test.ts", parser.as_ref()).unwrap();
141
142 assert!(nodes
143 .iter()
144 .any(|n| n.name == "greet" && n.kind == NodeKind::Function));
145 assert!(nodes
146 .iter()
147 .any(|n| n.name == "UserService" && n.kind == NodeKind::Class));
148 }
149}