use super :: *;
use macro_tools :: { attr, diag, Result, proc_macro2 ::TokenStream, syn ::Index, quote };
pub fn component_from(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;
let generics = &parsed.generics;
let ( _impl_generics, ty_generics, where_clause ) = generics.split_for_impl();
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, &ty_generics, where_clause ) )
} 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, &ty_generics, where_clause ) )
} else {
None }
})
.collect :: < Result< Vec< _ > > >()?
}
syn ::Fields ::Unit =>
{
vec![]
}
};
let result = qt! {
#( #for_fields )*
};
if has_debug
{
let about = format!("derive: ComponentFrom\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,
ty_generics: &syn ::TypeGenerics< '_ >,
where_clause: Option< &syn ::WhereClause >,
) -> 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"));
};
let where_tokens = if let Some( clause ) = where_clause
{
let predicates = &clause.predicates;
qt!
{
where
#predicates,
#field_type: Clone,
}
} else {
qt!
{
where
#field_type: Clone,
}
};
Ok(qt! {
impl From< &#item_name #ty_generics > for #field_type
#where_tokens
{
#[ inline( always ) ]
fn from( src: &#item_name #ty_generics ) -> Self
{
src.#field_accessor.clone()
}
}
})
}