armour-derive 0.27.3

DDL and serialization for key-value storage
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DataEnum, Fields, Ident, Variant};

pub fn parse(data: &DataEnum, name: Ident) -> proc_macro::TokenStream {
    let string_name = name.to_string();
    let is_simple_enum = data.variants.iter().all(|item| item.fields.is_empty());

    if is_simple_enum {
        return simple_enum(data.variants.iter(), name);
    }

    let variants_stream: Vec<TokenStream> = data
        .variants
        .iter()
        .enumerate()
        .map(|(index, variant)| {
            let index = index as u8;

            let variant_name = variant.ident.to_string();

            match &variant.fields {
                Fields::Named(fields) => {
                    let items = &fields
                        .named
                        .iter()
                        .map(|field| {
                            let typ = &field.ty;
                            let name = field.ident.as_ref().unwrap().to_string();

                            quote! {
                                (#name, <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE),
                            }
                        })
                        .collect::<Vec<_>>();

                    quote! {
                        (#index, (#variant_name, ::armour_core::dyn_types::Typ::Struct({
                            ::armour_core::dyn_types::StructType {
                                name: "",
                                fields: ::armour_core::dyn_types::Fields::Named(&[ #(#items)* ]),
                            }
                        }))),
                    }
                }
                Fields::Unnamed(fields) => {
                    let fields = &fields.unnamed;

                    if fields.len() == 1 {
                        let field = fields.iter().next().unwrap();
                        let typ = &field.ty;

                        quote! {
                            (#index, (#variant_name, <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE)),
                        }
                    } else {
                        let items = fields
                            .iter()
                            .map(|field| {
                                let typ = &field.ty;

                                quote! {
                                    <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE,
                                }
                            })
                            .collect::<Vec<_>>();

                        quote! {
                            (#index, (#variant_name, ::armour_core::dyn_types::Typ::Struct({
                                ::armour_core::dyn_types::StructType {
                                    name: "",
                                    fields: ::armour_core::dyn_types::Fields::Unnamed(&[ #(#items)* ]),
                                }
                            }))),
                        }
                    }
                }
                Fields::Unit => {
                    quote! {
                        (#index, (#variant_name, ::armour_core::Typ::Void)),
                    }
                }
            }
        })
        .collect();

    let res = quote! {
        impl ::armour_core::dyn_types::get_type::GetType for #name {
            const TYPE: ::armour_core::Typ = ::armour_core::Typ::Enum(::armour_core::dyn_types::EnumType {
                name: #string_name,
                variants: &[
                    #(#variants_stream)*
                ]
            });
        }
    };

    proc_macro::TokenStream::from(res)
}

fn simple_enum<'a>(
    variants: impl Iterator<Item = &'a Variant>,
    name: Ident,
) -> proc_macro::TokenStream {
    let string_name = name.to_string();

    let variants_stream: Vec<TokenStream> = variants
        .enumerate()
        .map(|(index, variant)| {
            let index = index as u8;

            let variant_name = variant.ident.to_string();

            quote!((#index, #variant_name),)
        })
        .collect();

    let res = quote! {
        impl ::armour_core::dyn_types::get_type::GetType for #name {
            const TYPE: ::armour_core::Typ = ::armour_core::Typ::SimpleEnum(::armour_core::dyn_types::SimpleEnumType {
                name: #string_name,
                variants: &[
                    #(#variants_stream)*
                ]
            });
        }
    };

    proc_macro::TokenStream::from(res)
}