df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
#![cfg(debug_assertions)]

mod test_structure;
mod test_tree_structure;

use crate::TokenDeserialize;
use df_ls_diagnostics::{lsp_types::Diagnostic, DiagnosticsInfo, Range};
use df_ls_lexical_analysis::{
    test_utils::{
        test_diagnostics, test_diagnostics_codes, test_diagnostics_ranges, LexerTestBuilder,
    },
    Tree,
};
use std::{fmt::Debug, rc::Rc};
pub use test_tree_structure::skip_comment;

pub struct SyntaxTestBuilder<S: TokenDeserialize + Debug + PartialEq> {
    lexer_test_builder: Option<LexerTestBuilder>,
    structure: Option<S>,
    syntax_diagnostics: Option<Vec<Diagnostic>>,
    syntax_diagnostics_codes: Option<Vec<String>>,
    syntax_diagnostics_ranges: Option<Vec<Range>>,
}

impl<S: TokenDeserialize + Debug + PartialEq> SyntaxTestBuilder<S> {
    /// Create a new test case
    #[must_use = "Don't forget to call `.run_test()`"]
    pub fn from_lexer_test_builder(lexer_test_builder: LexerTestBuilder) -> Self {
        Self {
            lexer_test_builder: Some(lexer_test_builder),
            structure: None,
            syntax_diagnostics: None,
            syntax_diagnostics_codes: None,
            syntax_diagnostics_ranges: None,
        }
    }
    /// Start running all the tests.
    /// This function will panic if the test failed.
    pub fn run_test(self) {
        self.run_test_with_result();
    }

    /// Start running all the tests.
    /// This function will panic if the test failed.
    /// It will return the output of the lexer so it can be used in the Semantic Analysis.
    pub fn run_test_with_result(mut self) -> (Rc<Tree>, S, String) {
        // Move lexer test builder out of struct so we can barrow struct.
        let lexer_test_builder = self
            .lexer_test_builder
            .expect("LexerTestBuilder was already used.");
        self.lexer_test_builder = None;
        // Run LexerTestBuilder first
        let (tree, source) = lexer_test_builder.run_test_with_result();

        // Start Syntax Analysis on AST
        let (given_structure, diagnostic_info_syntax): (S, _) =
            crate::do_syntax_analysis(&tree, &source);

        self.run_test_with_custom_data(&given_structure, diagnostic_info_syntax);

        (tree, given_structure, source)
    }

    /// Start running only syntax tests on the custom data.
    /// This function is used by the `test_tree_structure!` macro
    /// and should usually not be used elsewhere.
    ///
    /// This function will panic if the test failed.
    pub fn run_test_with_custom_data(
        &self,
        given_structure: &S,
        diagnostic_info_syntax: DiagnosticsInfo,
    ) {
        // Test structure
        if let Some(structure) = &self.structure {
            test_structure::test_structure(structure, given_structure);
        }

        // Test full syntax diagnostics message
        if let Some(syntax_diagnostics) = &self.syntax_diagnostics {
            test_diagnostics::test_diagnostics(syntax_diagnostics, &diagnostic_info_syntax);
        }

        // Test syntax diagnostic codes
        if let Some(syntax_diagnostics_codes) = &self.syntax_diagnostics_codes {
            test_diagnostics_codes::test_diagnostics_codes(
                syntax_diagnostics_codes,
                &diagnostic_info_syntax,
            );
        }

        // Test syntax diagnostic ranges
        if let Some(syntax_diagnostics_ranges) = &self.syntax_diagnostics_ranges {
            test_diagnostics_ranges::test_diagnostics_ranges(
                syntax_diagnostics_ranges,
                &diagnostic_info_syntax,
            );
        }
    }

    /// This function should never be used outside of macros.
    /// Only used inside `test_tree_structure!` macro.
    ///
    /// The `LexerTestBuilder` will be used and can no longer be used after this.
    #[must_use = "Don't forget to call `.run_test_with_custom_data()`"]
    pub fn run_lexer_test_builder(mut self) -> (Self, Rc<Tree>, String) {
        // Move lexer test builder out of struct so we can barrow struct.
        let lexer_test_builder = self
            .lexer_test_builder
            .expect("LexerTestBuilder was already used.");
        self.lexer_test_builder = None;
        // Run LexerTestBuilder
        let (tree, source) = lexer_test_builder.run_test_with_result();
        (self, tree, source)
    }

    /// Add test to validate generated structure.
    #[must_use = "Don't forget to call `.run_test()`"]
    pub fn add_test_structure(mut self, structure: S) -> Self {
        self.structure = Some(structure);
        self
    }

    /// Add test to validate a full diagnostic message.
    #[must_use = "Don't forget to call `.run_test()`"]
    pub fn add_test_syntax_diagnostics(mut self, diagnostics: Vec<Diagnostic>) -> Self {
        self.syntax_diagnostics = Some(diagnostics);
        self
    }

    /// Add test to validate the diagnostic codes.
    ///
    /// Example:
    /// ```rust,ignore
    /// vec!["missing_newline"]
    /// ```
    #[must_use = "Don't forget to call `.run_test()`"]
    pub fn add_test_syntax_diagnostics_codes(mut self, diagnostics_codes: Vec<&str>) -> Self {
        self.syntax_diagnostics_codes = Some(
            diagnostics_codes
                .into_iter()
                .map(|s| s.to_owned())
                .collect(),
        );
        self
    }

    /// Add test to validate the diagnostic ranges.
    ///
    /// Example:
    /// ```rust,ignore
    /// vec![Range {
    ///     start: Position {
    ///         line: 0,
    ///         character: 0,
    ///     },
    ///     end: Position {
    ///         line: 0,
    ///         character: 17,
    ///     },
    /// }]
    /// ```
    #[must_use = "Don't forget to call `.run_test()`"]
    pub fn add_test_syntax_diagnostics_ranges(mut self, diagnostics_ranges: Vec<Range>) -> Self {
        self.syntax_diagnostics_ranges = Some(diagnostics_ranges);
        self
    }
}