Skip to main content

fluidattacks_blends/
parse.rs

1//! Source content → tree-sitter parse tree.
2
3use tree_sitter::{Parser, Tree};
4
5use crate::content::Content;
6use crate::language::LanguageExt;
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
9#[non_exhaustive]
10pub struct ParseError;
11
12/// Parses `content` with the tree-sitter grammar of its language.
13///
14/// # Errors
15///
16/// Returns [`ParseError`] when the grammar cannot be loaded or the tree
17/// contains a syntax error.
18pub fn parse(content: &Content) -> Result<Tree, ParseError> {
19    let mut parser = Parser::new();
20    parser
21        .set_language(&content.language.tree_sitter())
22        .map_err(|_| ParseError)?;
23
24    let tree = parser.parse(&content.bytes, None).ok_or(ParseError)?;
25    if tree.root_node().has_error() {
26        return Err(ParseError);
27    }
28
29    Ok(tree)
30}
31
32#[cfg(test)]
33mod tests {
34    use super::parse;
35    use crate::content::Content;
36    use std::fs;
37
38    fn file_content(source: &[u8]) -> Content {
39        let dir = tempfile::tempdir().unwrap();
40        let path = dir.path().join("snippet.java");
41        fs::write(&path, source).unwrap();
42        Content::from_path(&path, None).unwrap()
43    }
44
45    #[test]
46    fn parses_valid_file() {
47        assert!(parse(&file_content(b"class A {}")).is_ok());
48    }
49
50    #[test]
51    fn rejects_malformed_file() {
52        assert!(parse(&file_content(b"class A {")).is_err());
53    }
54}