fluidattacks-blends 0.32.0

Blends imperative shell: parsing, AST-graph construction, serialization
Documentation
//! Source content → tree-sitter parse tree.

use tree_sitter::{Parser, Tree};

use crate::content::Content;
use crate::language::LanguageExt;

/// The grammar for the content's language could not be loaded, or the parsed
/// tree contains a syntax error.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub struct ParseError;

/// Parses `content` with the tree-sitter grammar of its language.
///
/// # Errors
///
/// Returns [`ParseError`] when the grammar cannot be loaded or the tree
/// contains a syntax error.
pub fn parse(content: &Content) -> Result<Tree, ParseError> {
    let language = content.language.tree_sitter().ok_or(ParseError)?;
    let mut parser = Parser::new();
    parser.set_language(&language).map_err(|_| ParseError)?;

    let tree = parser.parse(&content.bytes, None).ok_or(ParseError)?;
    if tree.root_node().has_error() {
        return Err(ParseError);
    }

    Ok(tree)
}

#[cfg(test)]
mod tests {
    use super::parse;
    use crate::content::Content;
    use std::fs;

    fn file_content(source: &[u8]) -> Content {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("snippet.java");
        fs::write(&path, source).unwrap();
        Content::from_path(&path, None).unwrap()
    }

    #[test]
    fn parses_valid_file() {
        assert!(parse(&file_content(b"class A {}")).is_ok());
    }

    #[test]
    fn rejects_malformed_file() {
        assert!(parse(&file_content(b"class A {")).is_err());
    }
}