use super :: *;
use macro_tools :: { qt, attr, diag, Result, proc_macro2 ::TokenStream, syn ::Index, quote };
pub fn component_assign(input: proc_macro ::TokenStream) -> Result< proc_macro2 ::TokenStream >
{
let original_input = input.clone();
let parsed = syn ::parse :: < syn ::ItemStruct >(input)?;
let has_debug = attr ::has_debug(parsed.attrs.iter())?;
let item_name = &parsed.ident.clone();
let mut seen_types = std ::collections ::HashSet ::new();
let for_fields = match &parsed.fields
{
syn ::Fields ::Named(fields_named) =>
{
fields_named.named.iter()
.filter_map( | field |
{
let field_type = &field.ty;
let type_string = quote ::quote!( #field_type ).to_string();
if seen_types.insert( type_string )
{
Some( for_each_field( field, None, item_name ) )
} else {
None }
})
.collect :: < Result< Vec< _ > > >()?
}
syn ::Fields ::Unnamed(fields_unnamed) =>
{
fields_unnamed.unnamed.iter().enumerate()
.filter_map( |( index, field )|
{
let field_type = &field.ty;
let type_string = quote ::quote!( #field_type ).to_string();
if seen_types.insert( type_string )
{
Some( for_each_field( field, Some( index ), item_name ) )
} else {
None }
})
.collect :: < Result< Vec< _ > > >()?
}
syn ::Fields ::Unit =>
{
vec![]
}
};
let result = qt! {
#( #for_fields )*
};
if has_debug
{
let about = format!("derive: Assign\nstructure: {item_name}");
diag ::report_print(about, &original_input, &result);
}
Ok(result)
}
fn for_each_field(
field: &syn ::Field,
index: Option< usize >, item_name: &syn ::Ident,
) -> Result< proc_macro2 ::TokenStream > {
let field_type = &field.ty;
let field_accessor: TokenStream = if let Some(ident) = &field.ident
{
quote! { #ident }
} else if let Some(idx) = index
{
let index_lit = Index ::from(idx);
quote! { #index_lit }
} else {
return Err(syn ::Error ::new_spanned(field, "Field has neither ident nor index"));
};
Ok(qt! {
#[ allow( non_snake_case ) ] impl< IntoT > Assign< #field_type, IntoT > for #item_name
where
IntoT: Into< #field_type >,
{
#[ inline( always ) ]
fn assign( &mut self, component: IntoT )
{
self.#field_accessor = component.into(); }
}
})
}