daipendency_extractor/tree_sitter_helpers.rs
1use crate::ExtractionError;
2use std::ops::Range;
3use tree_sitter::{Node, Parser, Query, QueryCursor, QueryMatches, Tree};
4
5/// A parsed source file with its tree-sitter parse tree and original source code.
6pub struct ParsedFile<'a> {
7 root_tree: Tree,
8 source_code: &'a str,
9}
10
11impl<'a> ParsedFile<'a> {
12 /// Parse source code into a tree-sitter parse tree.
13 ///
14 /// # Parameters
15 /// * `source_code` - The source code to parse
16 /// * `parser` - A mutable reference to a configured tree-sitter parser
17 ///
18 /// # Returns
19 /// A new `ParsedFile` instance or an `ExtractionError` if parsing fails
20 pub fn parse(source_code: &'a str, parser: &mut Parser) -> Result<Self, ExtractionError> {
21 let root_tree = parser
22 .parse(source_code, None)
23 .ok_or_else(|| ExtractionError::Malformed("Failed to parse source file".to_string()))?;
24
25 if root_tree.root_node().has_error() {
26 return Err(ExtractionError::Malformed(
27 "Failed to parse source file".to_string(),
28 ));
29 }
30
31 Ok(Self {
32 root_tree,
33 source_code,
34 })
35 }
36
37 /// Return the root node of the parse tree.
38 ///
39 /// # Returns
40 /// The root node of the parsed source file
41 pub fn root_node(&'a self) -> Node<'a> {
42 self.root_tree.root_node()
43 }
44
45 /// Return a tree-sitter node's text content.
46 ///
47 /// # Parameters
48 /// * `node` - The tree-sitter node to render
49 ///
50 /// # Returns
51 /// The text content of the node or an `ExtractionError` if rendering fails
52 pub fn render_node(&self, node: Node) -> Result<String, ExtractionError> {
53 node.utf8_text(self.source_code.as_bytes())
54 .map(|s| s.to_string())
55 .map_err(|_| ExtractionError::Malformed("Failed to render node".to_string()))
56 }
57
58 /// Return text content from a byte range in the source code.
59 ///
60 /// # Parameters
61 /// * `range` - The byte range to extract from the source code
62 ///
63 /// # Returns
64 /// The text content within the specified range
65 pub fn render(&self, range: Range<usize>) -> String {
66 self.source_code[range].to_string()
67 }
68
69 /// Create a new tree-sitter query from a query string.
70 ///
71 /// # Parameters
72 /// * `query` - The query string in tree-sitter query language
73 ///
74 /// # Returns
75 /// A compiled query or an `ExtractionError` if query creation fails
76 pub fn make_query(&self, query: &str) -> Result<Query, ExtractionError> {
77 Query::new(&self.root_tree.language(), query)
78 .map_err(|_| ExtractionError::Malformed("Failed to create query".to_string()))
79 }
80
81 /// Execute a tree-sitter query on a specific node.
82 ///
83 /// # Parameters
84 /// * `query` - The compiled tree-sitter query to execute
85 /// * `node` - The node to search within
86 /// * `cursor` - A mutable reference to a query cursor for tracking query state
87 ///
88 /// # Returns
89 /// An iterator over the query matches
90 pub fn exec_query<'b>(
91 &'b self,
92 query: &'b Query,
93 node: Node<'b>,
94 cursor: &'b mut QueryCursor,
95 ) -> QueryMatches<'b, 'b, &'b [u8], &'b [u8]> {
96 cursor.matches(query, node, self.source_code.as_bytes())
97 }
98}