use crate::prelude::*;
use beet_core::prelude::*;
use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;
use syn::Ident;
use syn::Result;
use syn::Type;
pub fn impl_flatten(
target_ident: &Ident,
input: &DeriveInput,
fields: &Vec<NodeField>,
) -> Result<TokenStream> {
let (impl_generics, type_generics, where_clause) =
input.generics.split_for_impl();
let flatten_impls = fields.iter()
.filter(|field|field.field_attributes.contains("flatten"))
.map(|field| {
let field_ident = &field.ident;
let field_type = &field.ty;
let second_order_as_mut = second_order_impl(target_ident,input, field)?;
Ok(quote! {
impl #impl_generics AsRef<#field_type> for #target_ident #type_generics #where_clause {
fn as_ref(&self) -> &#field_type { &self.#field_ident }
}
impl #impl_generics AsMut<#field_type> for #target_ident #type_generics #where_clause {
fn as_mut(&mut self) -> &mut #field_type { &mut self.#field_ident }
}
#(#second_order_as_mut)*
})
}
).collect::<Result<Vec<_>>>()?;
Ok(quote! {#(#flatten_impls)*})
}
fn second_order_impl(
target_ident: &Ident,
input: &DeriveInput,
field: &NamedField,
) -> Result<Vec<TokenStream>> {
let (impl_generics, type_generics, where_clause) =
input.generics.split_for_impl();
let field_ident = &field.ident;
let flatten_attrs = field.field_attributes.get_many("flatten");
flatten_attrs.iter().xtry_filter_map(|attr|{
let Some(ty) = attr.value_parsed::<Type>()? else{
return Ok(None);
};
if ty == *field.ty {
return Ok(Some(TokenStream::default()));
}
Ok(Some(quote!{
impl #impl_generics AsRef<#ty> for #target_ident #type_generics #where_clause {
fn as_ref(&self) -> &#ty { self.#field_ident.as_ref() }
}
impl #impl_generics AsMut<#ty> for #target_ident #type_generics #where_clause {
fn as_mut(&mut self) -> &mut #ty { self.#field_ident.as_mut() }
}
}))
})
}