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