df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use super::super::{Token, TokenArgument, TryFromArgumentGroup};
use super::{LoopControl, TokenDeserialize};
use df_ls_core::Choose;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
use df_ls_lexical_analysis::TreeCursor;

/// Deserialize a token with following pattern: `[REF:Choose<T1,T2>]`
impl<T1, T2> TokenDeserialize for Choose<T1, T2>
where
    T1: Default + TryFromArgumentGroup,
    T2: Default + TryFromArgumentGroup,
{
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Box<Self>, ()> {
        // Get arguments from token
        let mut token = match Token::deserialize_tokens(cursor, source, diagnostics) {
            Ok(token) => token,
            Err(err) => {
                // When token could not be parsed correctly.
                // Token could not be parsed, so we can consume it.
                // Because this will always fail.
                Token::consume_token(cursor)?;
                return Err(err);
            }
        };
        Token::consume_token(cursor)?;
        // Token Name
        token.check_token_name(source, diagnostics, true)?;
        // Arg 0
        let result = Self::try_from_argument_group(&mut token, source, diagnostics, true);
        // Just check if all tokens are consumed if no other errors are present
        if result.is_ok() {
            token.check_all_arg_consumed(source, diagnostics, true)?;
        } else {
            // In case of an error, mark other arguments as unchecked.
            crate::mark_rest_of_token_as_unchecked(diagnostics, &token);
        }
        Ok(Box::new(result?))
    }

    fn deserialize_general_token(
        _cursor: &mut TreeCursor,
        _source: &str,
        _diagnostics: &mut DiagnosticsInfo,
        new_self: Box<Self>,
    ) -> (LoopControl, Box<Self>) {
        (LoopControl::DoNothing, new_self)
    }

    fn get_vec_loopcontrol() -> LoopControl {
        LoopControl::DoNothing
    }

    fn get_allowed_tokens() -> Option<Vec<String>> {
        None
    }
}

// ------------------------- Convert a group of arguments to Self -----------------------

impl<T1, T2> TryFromArgumentGroup for Choose<T1, T2>
where
    T1: TryFromArgumentGroup,
    T2: TryFromArgumentGroup,
{
    fn try_from_argument_group(
        token: &mut Token,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        let mut token_clone1 = token.clone();
        let mut token_clone2 = token.clone();
        // Check which option finished without errors
        let option1 = T1::try_from_argument_group(&mut token_clone1, source, diagnostics, false);
        let option2 = T2::try_from_argument_group(&mut token_clone2, source, diagnostics, false);

        if option1.is_ok() {
            // Use option 1, even if option2 is valid
            // Add potential error messages
            let result =
                T1::try_from_argument_group(token, source, diagnostics, add_diagnostics_on_err)?;
            Ok(Choose::Choice1(result))
        } else if option2.is_ok() {
            // Add potential error messages
            let result =
                T2::try_from_argument_group(token, source, diagnostics, add_diagnostics_on_err)?;
            Ok(Choose::Choice2(result))
        } else {
            if add_diagnostics_on_err {
                let current_arg = token.get_current_arg()?;
                diagnostics.add_message(
                    DMExtraInfo {
                        range: current_arg.node.get_range(),
                        message_template_data: hash_map! {
                            "expected_parameters" => "One or multiple arguments".to_owned(),
                            "found_parameters" => current_arg.value.argument_to_token_type_name(),
                        },
                    },
                    "wrong_arg_type",
                );
            }
            token.consume_argument();
            Err(())
        }
    }
}

// -------------------------Convert one argument to Self -----------------------
// TryFromArgument is not used, use TryFromArgumentGroup instead

// -------------------------Convert from TokenArgument -----------------------

impl<T1, T2> From<Choose<T1, T2>> for TokenArgument
where
    TokenArgument: From<T1>,
    TokenArgument: From<T2>,
{
    fn from(item: Choose<T1, T2>) -> TokenArgument {
        match item {
            Choose::Choice1(v) => TokenArgument::from(v),
            Choose::Choice2(v) => TokenArgument::from(v),
        }
    }
}

impl<T1, T2> From<Option<Choose<T1, T2>>> for TokenArgument
where
    TokenArgument: From<T1>,
    TokenArgument: From<T2>,
{
    fn from(item: Option<Choose<T1, T2>>) -> TokenArgument {
        match item {
            Some(item) => match item {
                Choose::Choice1(v) => TokenArgument::from(v),
                Choose::Choice2(v) => TokenArgument::from(v),
            },
            None => TokenArgument::TVEmpty,
        }
    }
}

// ---------------------------- TESTS --------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_tree_structure;
    use crate::test_utils::SyntaxTestBuilder;
    use df_ls_lexical_analysis::test_utils::LexerTestBuilder;

    #[test]
    fn test_choose_correct() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:string]
                [REF:83]",
            )
            .add_test_lexer_diagnostics_codes(vec![])
            .add_test_lexer_diagnostics_ranges(vec![]),
        )
        .add_test_syntax_diagnostics_codes(vec![])
        .add_test_syntax_diagnostics_ranges(vec![]);

        test_tree_structure!(
            test_builder,
            [
                Choose<u8, String> => Choose::Choice2("string".to_owned()),
                Choose<u8, String> => Choose::Choice1(83u8),
            ]
        );
    }

    #[test]
    fn test_choose_enum_correct() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:string]
                [REF:83]",
            )
            .add_test_lexer_diagnostics_codes(vec![])
            .add_test_lexer_diagnostics_ranges(vec![]),
        )
        .add_test_syntax_diagnostics_codes(vec![])
        .add_test_syntax_diagnostics_ranges(vec![]);

        test_tree_structure!(
            test_builder,
            [
                (Choose<u8, String>,) => (Choose::Choice2("string".to_owned()),),
                (Choose<u8, String>,) => (Choose::Choice1(83u8),),
            ]
        );
    }
}