df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use super::super::{Argument, Token, TokenArgument, TryFromArgument, TryFromArgumentGroup};
use super::TokenDeserialize;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};

// Deserialize a token with following pattern: `[REF:bool]` or `[REF:1]`
crate::token_deserialize_unary_token!(bool);

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

impl TryFromArgumentGroup for bool {
    fn try_from_argument_group(
        token: &mut Token,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        token.check_as_required_argument(source, diagnostics, add_diagnostics_on_err)?;
        let arg = token.get_current_arg_opt();
        let result = Self::try_from_argument(arg, source, diagnostics, add_diagnostics_on_err);
        token.consume_argument();
        result
    }
}

// -------------------------Convert one argument to Self -----------------------

impl TryFromArgument for bool {
    fn try_from_argument(
        arg_opt: Option<&Argument>,
        _source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        if let Some(arg) = arg_opt {
            match arg.value {
                TokenArgument::TVInteger(v) => match v {
                    0 => Ok(false),
                    1 => Ok(true),
                    x if x > 1 => {
                        if add_diagnostics_on_err {
                            diagnostics.add_message(
                                DMExtraInfo {
                                    range: arg.node.get_range(),
                                    message_template_data: hash_map! {
                                        "max_value" => "1".to_owned(),
                                    },
                                },
                                "too_large_int",
                            );
                        }
                        Err(())
                    }
                    _ => {
                        if add_diagnostics_on_err {
                            diagnostics.add_message(
                                DMExtraInfo {
                                    range: arg.node.get_range(),
                                    message_template_data: hash_map! {
                                        "min_value" => "0".to_owned(),
                                    },
                                },
                                "too_small_int",
                            );
                        }
                        Err(())
                    }
                },
                _ => {
                    if add_diagnostics_on_err {
                        diagnostics.add_message(
                            DMExtraInfo {
                                range: arg.node.get_range(),
                                message_template_data: hash_map! {
                                    "expected_parameters" => Self::expected_argument_types(),
                                    "found_parameters" => arg.value.argument_to_token_type_name(),
                                },
                            },
                            "wrong_arg_type",
                        );
                    }
                    Err(())
                }
            }
        } else {
            Err(())
        }
    }

    fn expected_argument_types() -> String {
        "bool".to_owned()
    }
}

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

impl From<bool> for TokenArgument {
    fn from(item: bool) -> TokenArgument {
        TokenArgument::TVInteger(match item {
            false => 0,
            true => 1,
        })
    }
}

impl From<Option<bool>> for TokenArgument {
    fn from(item: Option<bool>) -> TokenArgument {
        match item {
            Some(v) => TokenArgument::TVInteger(match v {
                false => 0,
                true => 1,
            }),
            None => TokenArgument::TVEmpty,
        }
    }
}

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

    #[test]
    fn test_bool_correct() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:1]
                [REF:0]
                [REF:1]",
            )
            .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,
            [
                bool => true,
                bool => false,
                (bool,) => (true,),
            ]
        );
    }

    #[test]
    fn test_bool_integer_overflow() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:5]
                [REF:-1]",
            )
            .add_test_lexer_diagnostics_codes(vec![])
            .add_test_lexer_diagnostics_ranges(vec![]),
        )
        .add_test_syntax_diagnostics_codes(vec!["too_large_int", "too_small_int"])
        .add_test_syntax_diagnostics_ranges(vec![
            Range {
                start: Position {
                    line: 1,
                    character: 21,
                },
                end: Position {
                    line: 1,
                    character: 22,
                },
            },
            Range {
                start: Position {
                    line: 2,
                    character: 21,
                },
                end: Position {
                    line: 2,
                    character: 23,
                },
            },
        ]);

        test_tree_structure!(
            test_builder,
            [
                bool =!,
                bool =!,
            ]
        );
    }
}