use darling::FromField;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{DataStruct, Field, Fields, Ident};
use crate::{CustomField, FieldAttr};
struct FieldData {
field: Field,
idx: u32,
attr: FieldAttr,
}
pub fn parse(data: &DataStruct, name: Ident) -> TokenStream {
let string_name = name.to_string();
match &data.fields {
Fields::Named(fields) => {
let named = &fields.named;
let named_len = named.len();
let mut fields_insert: Vec<FieldData> = Vec::with_capacity(named_len);
let mut seq = 0u32;
for field in named.iter() {
let attr = match FieldAttr::from_field(field) {
Ok(attr) => attr,
Err(err) => return err.write_errors(),
};
let idx = attr.idx.unwrap_or_else(|| {
let current_seq = seq;
seq += 1;
current_seq
});
let f = FieldData {
field: field.clone(),
idx,
attr,
};
fields_insert.push(f);
}
fields_insert.sort_by_key(|f| f.idx);
let fields_stream: Vec<TokenStream> = fields_insert
.iter()
.map(|f| {
let ident = f.field.ident.as_ref().unwrap().to_string();
let ty = &f.field.ty;
let typ;
if let Some(with_type) = &f.attr.with_type {
typ = with_type.to_token_stream();
} else if f.attr.unimplemented.is_present() {
typ = quote! {
::armour_core::Typ::Custom("unimplemented", &[])
};
} else if let Some(CustomField { name, types }) = &f.attr.custom {
typ = quote! {
::armour_core::Typ::Custom(#name, #types)
};
} else {
typ = quote! {
<#ty as ::armour_core::dyn_types::get_type::GetType>::TYPE
};
}
quote! {
(#ident, #typ),
}
})
.collect();
let res = quote! {
impl ::armour_core::dyn_types::get_type::GetType for #name {
const TYPE: ::armour_core::Typ = {
let fields = &[
#(#fields_stream)*
];
::armour_core::Typ::Struct(::armour_core::dyn_types::StructType {
name: #string_name,
fields: ::armour_core::dyn_types::Fields::Named(fields),
})
};
}
};
res
}
Fields::Unnamed(fields) => {
let unnamed = &fields.unnamed;
if unnamed.len() == 1 {
let field = &unnamed[0];
let attr = match FieldAttr::from_field(field) {
Ok(attr) => attr,
Err(err) => return err.write_errors(),
};
let is_flatten = attr.flatten.is_present();
if is_flatten {
let typ = &field.ty;
let res = quote! {
impl ::armour_core::dyn_types::get_type::GetType for #name {
const TYPE: ::armour_core::Typ = <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE;
}
};
return res;
}
}
let fields_stream: Vec<TokenStream> = unnamed
.iter()
.map(|field| {
let typ = &field.ty;
quote! {
<#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE,
}
})
.collect();
let res = quote! {
impl ::armour_core::dyn_types::get_type::GetType for #name {
const TYPE: ::armour_core::Typ = {
let fields = &[
#(#fields_stream)*
];
::armour_core::Typ::Struct(::armour_core::dyn_types::StructType {
name: #string_name,
fields: ::armour_core::dyn_types::Fields::Unnamed(fields),
})
};
}
};
res
}
Fields::Unit => {
let res = quote! {
impl ::armour_core::dyn_types::get_type::GetType for #name {
const TYPE: ::armour_core::Typ = ::armour_core::Typ::Void;
}
};
res
}
}
}