1use std::path::Path;
7
8use tree_sitter::{Language, Parser, Query, Tree};
9
10#[derive(Debug)]
12pub struct ParseError(pub String);
13
14impl std::fmt::Display for ParseError {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 write!(f, "{}", self.0)
17 }
18}
19
20impl std::error::Error for ParseError {}
21
22pub struct RustParser {
24 parser: Parser,
25 language: Language,
26}
27
28impl RustParser {
29 pub fn new() -> Result<Self, ParseError> {
35 let language: Language = tree_sitter_rust::LANGUAGE.into();
36
37 let mut parser = Parser::new();
38 parser
39 .set_language(&language)
40 .map_err(|e| ParseError(format!("Failed to set language: {e}")))?;
41
42 Ok(Self { parser, language })
43 }
44
45 #[must_use]
47 pub fn parse(&mut self, source: &str) -> Option<Tree> {
48 self.parser.parse(source, None)
49 }
50
51 pub fn parse_file(&mut self, path: &Path) -> Result<(String, Tree), ParseError> {
57 let source = std::fs::read_to_string(path)
58 .map_err(|e| ParseError(format!("Failed to read file {}: {e}", path.display())))?;
59
60 let tree = self
61 .parse(&source)
62 .ok_or_else(|| ParseError("Failed to parse source".to_string()))?;
63
64 Ok((source, tree))
65 }
66
67 pub fn create_query(&self, query_str: &str) -> Result<Query, ParseError> {
73 Query::new(&self.language, query_str).map_err(|e| ParseError(format!("Invalid query: {e}")))
74 }
75}
76
77impl Default for RustParser {
78 fn default() -> Self {
79 Self::new().expect("Failed to create Rust parser")
80 }
81}
82
83pub const METRICS_QUERY: &str = r"
90(macro_invocation
91 macro: (identifier) @macro_name
92 (token_tree) @args)
93";
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_parse_simple_function() {
101 let mut parser = RustParser::new().expect("Failed to create parser");
102 let source = r#"
103fn main() {
104 println!("Hello, world!");
105}
106"#;
107 let tree = parser.parse(source).expect("Failed to parse");
108 assert_eq!(tree.root_node().kind(), "source_file");
109 }
110
111 #[test]
112 fn test_parse_with_metrics_macro() {
113 let mut parser = RustParser::new().expect("Failed to create parser");
114 let source = r#"
115fn handle_request() {
116 counter!("http.requests", "method" => "GET").increment(1);
117}
118"#;
119 let tree = parser.parse(source).expect("Failed to parse");
120 assert_eq!(tree.root_node().kind(), "source_file");
121 }
122
123 #[test]
124 fn test_metrics_query() {
125 use streaming_iterator::StreamingIterator;
126 use tree_sitter::QueryCursor;
127
128 let mut parser = RustParser::new().expect("Failed to create parser");
129 let source = r#"
130fn handle_request() {
131 counter!("http.requests", "method" => "GET").increment(1);
132 gauge!("connections.active").set(42.0);
133 histogram!("request.latency_ms").record(150.0);
134}
135"#;
136 let tree = parser.parse(source).expect("Failed to parse");
137 let query = parser
138 .create_query(METRICS_QUERY)
139 .expect("Failed to create query");
140 let mut cursor = QueryCursor::new();
141
142 let mut count = 0;
143 let mut matches = cursor.matches(&query, tree.root_node(), source.as_bytes());
144 while matches.next().is_some() {
145 count += 1;
146 }
147
148 assert_eq!(count, 3);
150 }
151}