df_ls_derive 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use super::token_de_info::*;
use crate::common;
use quote::quote;

pub fn impl_token_deserialize_macro_enum_value(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
    let name = &ast.ident;
    let fields = common::get_struct_enum_fields(ast);
    let mut expected_values = vec![];
    let mut errors = quote! {};

    let convert_match_items = {
        let mut parse_gen = quote! {};
        for (_i, field) in fields.iter().enumerate() {
            let ident = field.ident.as_ref().unwrap();
            let (token_info, token_err) = get_token_de_info(field);
            errors = quote! {
                #errors
                #token_err
            };
            let token_ref = token_info.token;
            expected_values.push(token_ref.clone().unwrap());
            parse_gen = quote! {
                #parse_gen
                #token_ref => Ok(Self::#ident),
            };
            // Alias
            if let Some(alias) = token_info.alias {
                expected_values.push(alias.clone());
                parse_gen = quote! {
                    #parse_gen
                    #alias => {
                        diagnostics.add_message(
                            DMExtraInfo {
                                range: arg.node.get_range(),
                                message_template_data: hash_map! {
                                    "alias_name" => format!("`{}`", #alias),
                                    "suggested_name" => format!("`{}`", #token_ref),
                                },
                            },
                            "alias",
                        );
                        Ok(Self::#ident)
                    },
                };
            }
        }
        parse_gen
    };

    let expected_values_string = expected_values
        .iter()
        .map(|item| format!("`{}`", item))
        .collect::<Vec<String>>()
        .join(", ");

    // This is the same as `td_reference.rs`
    quote! {
        #errors
        /// Deserialize a token with following pattern: `[REF:ENUM]`
        impl TokenDeserialize for #name {
            #[allow(unused_variables)]
            fn deserialize_tokens(
                mut cursor: &mut df_ls_syntax_analysis::TreeCursor,
                source: &str,
                mut diagnostics: &mut df_ls_diagnostics::DiagnosticsInfo,
            ) -> Result<Box<Self>, ()> {
                use df_ls_syntax_analysis::{TryFromArgumentGroup, Token};
                let mut token = match Token::deserialize_tokens(&mut cursor, source, &mut 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(&mut cursor)?;
                        return Err(err);
                    }
                };
                Token::consume_token(&mut cursor)?;
                // Token Name
                token.check_token_name(source, &mut diagnostics, true)?;
                // Arg 0
                let result = Self::try_from_argument_group(&mut token, source, &mut 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, &mut diagnostics, true)?;
                } else {
                    // In case of an error, mark other arguments as unchecked.
                    df_ls_syntax_analysis::mark_rest_of_token_as_unchecked(diagnostics, &token);
                }
                Ok(Box::new(result?))
            }

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

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

            fn get_allowed_tokens() -> Option<Vec<String>> {
                // Should not be set, see #61
                None
            }
        }

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

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

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

        impl df_ls_syntax_analysis::TryFromArgument for #name {
            fn try_from_argument(
                arg_opt: Option<&df_ls_syntax_analysis::Argument>,
                _source: &str,
                diagnostics: &mut df_ls_diagnostics::DiagnosticsInfo,
                add_diagnostics_on_err: bool,
            ) -> Result<Self, ()> {
                use df_ls_syntax_analysis::{TokenArgument};
                use df_ls_diagnostics::{hash_map, DMExtraInfo};
                if let Some(arg) = arg_opt {
                    match &arg.value {
                        TokenArgument::TVReference(v) => match v.as_ref() {
                            #convert_match_items
                            found_value => {
                                if add_diagnostics_on_err {
                                    diagnostics.add_message(
                                        DMExtraInfo {
                                            range: arg.node.get_range(),
                                            message_template_data: hash_map! {
                                                "found_value" => format!("`{}`", found_value),
                                                "valid_values" => #expected_values_string.to_owned(),
                                            },
                                        },
                                        "wrong_enum_value",
                                    );
                                }
                                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 {
                "Reference".to_owned()
            }
        }
    }
}