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}