ambassador 0.5.1

Trait implementation delegation via procedural macros
Documentation
use crate::delegate_shared::{self, add_auto_where_clause};
use crate::register::{macro_name, match_name};
use crate::util::{error, process_results, try_option};
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::quote;
use std::default::Default;
use syn::spanned::Spanned;
use syn::{parse_macro_input, parse_quote, DeriveInput, Generics, LitStr, Result, WherePredicate};

#[derive(Debug)]
struct DelegateImplementer {
    generics: Generics,
    ty: Ident,
    info: DelegateImplementerInfo,
}

#[derive(Debug)]
enum DelegateImplementerInfo {
    Enum {
        variant_idents: Vec<Ident>,
        first_type: syn::Type,
        other_types: Vec<syn::Type>,
    },
    SingleFieldStruct {
        field_ident: syn::Member,
        field_type: syn::Type,
    },
    MultiFieldStruct {
        fields: Vec<(syn::Member, syn::Type)>,
    },
}

fn try_info_from_data(span: Span, data: syn::Data) -> Result<DelegateImplementerInfo> {
    let res = match data {
        syn::Data::Enum(enum_data) => {
            let iter = enum_data.variants.into_iter().map(|n| {
                let span = n.span();
                let mut it = n.fields.into_iter();
                match it.next() {
                    None => error!(span, "enum variant has no fields"),
                    Some(_) if it.count() != 0 => error!(span, "enum variant has multiple fields"),
                    Some(f) => Ok((n.ident, f.ty)),
                }
            });
            let (variant_idents, mut variant_types): (Vec<_>, Vec<_>) =
                process_results(iter, |iter| iter.unzip())?;
            let first_type = variant_types.pop().expect("enum has no variants");
            DelegateImplementerInfo::Enum {
                variant_idents,
                first_type,
                other_types: variant_types,
            }
        }
        syn::Data::Struct(struct_data) => match struct_data.fields.len() {
            1 => {
                let field = struct_data.fields.into_iter().next().unwrap();
                let field_ident = match field.ident {
                    Some(id) => syn::Member::Named(id),
                    None => syn::Member::Unnamed(0.into()),
                };
                DelegateImplementerInfo::SingleFieldStruct {
                    field_ident,
                    field_type: field.ty,
                }
            }
            _ => {
                let fields = struct_data
                    .fields
                    .into_iter()
                    .enumerate()
                    .map(|(i, field)| match field.ident {
                        Some(id) => (syn::Member::Named(id), field.ty),
                        None => (syn::Member::Unnamed(i.into()), field.ty),
                    })
                    .collect();
                DelegateImplementerInfo::MultiFieldStruct { fields }
            }
        },
        _ => {
            return error!(
                span,
                "ambassador currently only supports #[derive(Delegate)] for: \n\
             - single-field enums\n\
             - (tuple) structs"
            )
        }
    };
    Ok(res)
}

#[derive(Default)]
enum DelegateTarget {
    Field(syn::Member),
    #[default]
    TrgNone,
    TrgSelf,
}

impl delegate_shared::DelegateTarget for DelegateTarget {
    fn try_update(&mut self, key: &str, lit: LitStr) -> Option<Result<()>> {
        match key {
            "target" => {
                if !matches!(self, DelegateTarget::TrgNone) {
                    try_option!(error!(
                        lit.span(),
                        "\"target\" value for delegate attribute can only be specified once"
                    ));
                }
                *self = if lit.value() == "self" {
                    DelegateTarget::TrgSelf
                } else {
                    let target_val = try_option!(lit.parse());
                    DelegateTarget::Field(target_val)
                };
                Some(Ok(()))
            }
            _ => None,
        }
    }
}

type DelegateArgs = delegate_shared::DelegateArgs<DelegateTarget>;

fn unknown_field<X>(target: &syn::Member) -> Result<X> {
    error!(
        target.span(),
        "Unknown field specified as \"target\" value in #[delegate] attribute"
    )
}

/// Select the correct field_ident based on the `target`.
fn get_field<'a>(
    target: &syn::Member,
    field_idents: &'a [(syn::Member, syn::Type)],
) -> Result<&'a (syn::Member, syn::Type)> {
    let field = field_idents.iter().find(|n| n.0 == *target);
    match field {
        Some(field) => Ok(field),
        None => unknown_field(target),
    }
}

pub fn delegate_macro(input: TokenStream) -> TokenStream {
    // Parse the input tokens into a syntax tree
    let input = parse_macro_input!(input as DeriveInput);
    let info = match try_info_from_data(input.span(), input.data) {
        Ok(info) => info,
        Err(err) => return err.to_compile_error().into(),
    };
    let implementer = DelegateImplementer {
        info,
        generics: input.generics,
        ty: input.ident,
    };
    delegate_shared::delegate_macro(&implementer, input.attrs, delegate_single_attr).into()
}

fn delegate_single_attr(
    implementer: &DelegateImplementer,
    delegate_attr: TokenStream2,
) -> Result<TokenStream2> {
    let span = delegate_attr.span();
    let (trait_path_full, args) = DelegateArgs::from_tokens(delegate_attr)?;
    let (trait_ident, trait_generics_p) = delegate_shared::trait_info(&trait_path_full)?;
    let macro_name: Ident = macro_name(trait_ident);

    let generics = &implementer.generics;
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
    let mut where_clause = delegate_shared::build_where_clause(args.where_clauses, where_clause);
    let impl_generics = delegate_shared::merge_impl_generics(impl_generics, args.generics);
    let inline_attr = match args.inline {
        Some(mode) => mode.as_bracket_tokens(),
        None => TokenStream2::new(),
    };
    let implementer_ident = &implementer.ty;
    use {DelegateImplementerInfo::*, DelegateTarget::*};
    let res = match (&args.target, &implementer.info) {
        (TrgSelf, _) => quote! {
            impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause {
                #macro_name!{body_self(#inline_attr <#trait_generics_p>)}
            }
        },
        (Field(field), Enum {..}) => return error!(
            field.span(),
            "\"target\" value on #[delegate] attribute can not be specified for enums"
        ),
        (TrgNone, Enum {variant_idents, first_type, other_types}) => {
            if !args.inhibit_automatic_where_clause {
                add_auto_where_clause(&mut where_clause, &trait_path_full, first_type);
            }
            let match_name = match_name(trait_ident);
            where_clause
                .predicates
                .extend(other_types.iter().map::<WherePredicate, _>(
                    |arg| parse_quote!(#arg : #match_name<#trait_generics_p #first_type>),
                ));
            let mod_name = quote::format_ident!(
                "ambassador_module_{}_for_{}",
                trait_ident,
                implementer_ident
            );
            quote! {
                #[allow(non_snake_case)]
                mod #mod_name {
                    use super::*;
                    #macro_name!{use_assoc_ty_bounds}
                    impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause {
                        #macro_name!{body_enum(#inline_attr <#trait_generics_p>, #first_type, (#(#other_types),*), (#(#implementer_ident::#variant_idents),*))}
                    }
                }
            }
        }
        (trg, SingleFieldStruct {field_ident, field_type}) => {
            match trg {
                Field(f) if f != field_ident => {
                    unknown_field(f)?;
                }
                _ => {}
            }
            if !args.inhibit_automatic_where_clause {
                add_auto_where_clause(&mut where_clause, &trait_path_full, field_type);
            }

            quote! {
                impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause {
                    #macro_name!{body_struct(#inline_attr <#trait_generics_p>, #field_type, (#field_ident), (#field_ident), (#field_ident))}
                }
            }
        }
        (TrgNone, MultiFieldStruct {..}) => return error!(
            span,
            "\"target\" value on #[delegate] attribute has to be specified for structs with multiple fields"
        ),
        (Field(field), MultiFieldStruct {fields}) => {
            let field = get_field(field, fields)?;
            let field_ident = &field.0;
            let field_type = &field.1;
            if !args.inhibit_automatic_where_clause {
                add_auto_where_clause(&mut where_clause, &trait_path_full, field_type);
            }

            quote! {
                impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause {
                    #macro_name!{body_struct(#inline_attr <#trait_generics_p>, #field_type, (#field_ident), (#field_ident), (#field_ident))}
                }
            }
        }
    };
    Ok(res)
}