df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use df_ls_core::Reference;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
use df_ls_lexical_analysis::TreeCursor;

/// Trait for converting Tokenized values to TokenArgument stored types
pub trait TokenDeserializeBasics: Sized {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()>;
}

/// `u8` is needed here as `TokenArgument::TVArgN` is of that type
impl TokenDeserializeBasics for u8 {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        let node = cursor.node();
        let mut text = cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found");

        // Remove `ARG` in front of `token_argument_arg_n`
        if let Some(left_over) = text.strip_prefix("ARG") {
            text = left_over;
        } else {
            unreachable!("Argument variable does not have `ARG` as its prefix.");
        }

        match text.parse::<u8>() {
            Ok(value) => Ok(value),
            Err(err) => {
                diagnostics.add_message(
                    DMExtraInfo {
                        range: node.get_range(),
                        message_template_data: hash_map! {
                            "text_found" => format!("`{}`", text),
                            "error_message" => err.to_string(),
                        },
                    },
                    "expected_integer", // TODO can be changed to new error for
                                        // `ARG` number being to high/low.
                );
                Err(())
            }
        }
    }
}

/// `u16` is needed here as `TokenArgument::TVBangArgN` is of that type
impl TokenDeserializeBasics for u16 {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        let node = cursor.node();
        let mut text = cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found");

        // Remove `!ARG` in front of `token_argument_bang_arg_n`
        if let Some(left_over) = text.strip_prefix("!ARG") {
            text = left_over;
        } else {
            unreachable!("Argument variable does not have `!ARG` as its prefix.");
        }

        match text.parse::<u16>() {
            Ok(value) => Ok(value),
            Err(err) => {
                diagnostics.add_message(
                    DMExtraInfo {
                        range: node.get_range(),
                        message_template_data: hash_map! {
                            "text_found" => format!("`{}`", text),
                            "error_message" => err.to_string(),
                        },
                    },
                    "expected_integer", // TODO can be changed to new error for
                                        // `!ARG` number being to high/low.
                );
                Err(())
            }
        }
    }
}

/// `i64` is needed here as `TokenArgument::TVInteger` is of that type
impl TokenDeserializeBasics for i64 {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        let node = cursor.node();
        let text = cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found");

        match text.parse::<i64>() {
            Ok(value) => Ok(value),
            Err(err) => {
                diagnostics.add_message(
                    DMExtraInfo {
                        range: node.get_range(),
                        message_template_data: hash_map! {
                            "text_found" => format!("`{}`", text),
                            "error_message" => err.to_string(),
                        },
                    },
                    "expected_integer",
                );
                Err(())
            }
        }
    }
}

impl TokenDeserializeBasics for String {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        _diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        Ok(cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found")
            .to_owned())
    }
}

impl TokenDeserializeBasics for Reference {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        _diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        Ok(Reference(
            cursor
                .node()
                .utf8_text(source.as_bytes())
                .expect("Non UTF-8 Characters found")
                .to_owned(),
        ))
    }
}

impl TokenDeserializeBasics for bool {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        _diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        Ok(cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found")
            == "true")
    }
}

impl TokenDeserializeBasics for char {
    fn deserialize_tokens(
        cursor: &mut TreeCursor,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
    ) -> Result<Self, ()> {
        let node = cursor.node();
        // check if it is a int
        let value = cursor
            .node()
            .utf8_text(source.as_bytes())
            .expect("Non UTF-8 Characters found");
        let mut chars = value.chars();
        let first_quote = chars.next();
        if Some('\'') != first_quote {
            let first_quote = first_quote.unwrap_or('_');
            diagnostics.add_message(
                DMExtraInfo {
                    range: node.get_range(),
                    message_template_data: hash_map! {
                        "found_token" => format!("`{}`",first_quote),
                    },
                },
                "char_wrong_quote",
            );
        }
        let character = chars.next();
        let result_char = match character {
            Some(value) => value,
            None => {
                diagnostics.add_message(
                    DMExtraInfo::new(node.get_range()),
                    "char_expected_more_chars",
                );
                return Err(());
            }
        };
        let last_quote = chars.next();
        if Some('\'') != last_quote {
            let last_quote = last_quote.unwrap_or('_');
            diagnostics.add_message(
                DMExtraInfo {
                    range: node.get_range(),
                    message_template_data: hash_map! {
                        "found_token" => format!("`{}`",last_quote),
                    },
                },
                "char_wrong_quote",
            );
        }
        Ok(result_char)
    }
}