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)
}