newer-type-macro 0.3.0

Support defining newtype wrapper with inheriting trait implementations
Documentation
use crate::ResultExt;
use derive_syn_parse::Parse;
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use syn::punctuated::Punctuated;
use syn::*;
use template_quote::{quote, ToTokens};

pub struct Output {
    pub implementor: Implementor,
    pub target_def: Adt,
}

impl syn::parse::Parse for Output {
    fn parse(input: parse::ParseStream) -> Result<Self> {
        let inner_stream;
        parenthesized!(inner_stream in input);
        let implementor = inner_stream.parse()?;
        let target_def = input.parse()?;
        let _ = input.parse::<Token![,]>();
        if input.is_empty() {
            Ok(Self {
                implementor,
                target_def,
            })
        } else {
            Err(input.error("Bad trailing tokens"))
        }
    }
}

impl template_quote::ToTokens for Output {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.extend(quote! {(#{&self.implementor}) #{&self.target_def}});
    }
}

#[derive(PartialEq, Eq, Debug, Hash, Clone)]
pub struct Implementor {
    pub generics: Option<(Token![for], Generics)>,
    pub path: Path,
}

impl core::fmt::Display for Implementor {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        quote! {#{&self.path}}.fmt(f)
    }
}

impl ToTokens for Implementor {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.extend(quote! {
                #(if let Some((for_token, generics)) = &self.generics) {
                    #for_token #generics
                }
                #{&self.path}
                #(if let Some((_, generics)) = &self.generics) {
                    #{&generics.where_clause}
                }
        });
    }
}

impl syn::parse::Parse for Implementor {
    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
        let mut generics = if let Ok(for_token) = input.parse::<Token![for]>() {
            let lt_token = Some(input.parse::<Token![<]>()?);
            let mut params = Punctuated::new();
            while input.fork().parse::<Token![>]>().is_err() {
                let param = input.parse::<GenericParam>()?;
                params.push(param);
                if input.parse::<Token![,]>().is_err() {
                    break;
                }
            }
            let gt_token = Some(input.parse::<Token![>]>()?);
            Some((
                for_token,
                Generics {
                    lt_token,
                    params,
                    gt_token,
                    where_clause: None,
                },
            ))
        } else {
            None
        };
        let path: Path = input.parse()?;
        if let Some(seg) = path.segments.last() {
            if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) =
                &seg.arguments
            {
                args.iter().for_each(|arg| match arg {
                    GenericArgument::AssocType(_)
                    | GenericArgument::AssocConst(_)
                    | GenericArgument::Constraint(_) => abort!(arg, "Not supported"),
                    _ => (),
                });
            }
        }
        if let Some(generics) = &mut generics {
            generics.1.where_clause = input.parse::<Option<WhereClause>>()?;
        }
        if generics.is_none() || input.is_empty() {
            Ok(Implementor { generics, path })
        } else {
            Err(input.error("Bad trailing tokens"))
        }
    }
}

impl Implementor {
    fn emit_impl(&self, target_def: &Adt) -> TokenStream {
        let input = Output {
            implementor: self.clone(),
            target_def: target_def.clone(),
        };
        let mut path = self.path.clone();
        if let Some(seg) = path.segments.last_mut() {
            seg.arguments = PathArguments::None
        }
        quote! {
            #path! { #input }
        }
    }
}

#[derive(Parse, PartialEq, Eq, Debug, Hash, Clone)]
pub struct Argument {
    #[call(Punctuated::parse_terminated)]
    pub implementors: Punctuated<Implementor, Token![,]>,
}

impl ToTokens for Argument {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.implementors.to_tokens(tokens);
    }
}

impl Argument {
    pub fn from_attr(attr: &Attribute) -> Result<Option<Self>> {
        match &attr.meta {
            Meta::List(MetaList { path, tokens, .. }) if path.is_ident("implement") => {
                Ok(Some(parse2(tokens.clone())?))
            }
            _ => Ok(None),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt {
    Enum(ItemEnum),
    Struct(ItemStruct),
}

impl syn::parse::Parse for Adt {
    fn parse(input: parse::ParseStream) -> Result<Self> {
        if input.fork().parse::<ItemEnum>().is_ok() {
            Ok(Self::Enum(input.parse()?))
        } else {
            Ok(Self::Struct(input.parse()?))
        }
    }
}

impl template_quote::ToTokens for Adt {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Adt::Enum(item_enum) => item_enum.to_tokens(tokens),
            Adt::Struct(item_struct) => item_struct.to_tokens(tokens),
        }
    }
}

impl Adt {
    pub fn generics_mut(&mut self) -> &mut Generics {
        match self {
            Adt::Enum(item_enum) => &mut item_enum.generics,
            Adt::Struct(item_struct) => &mut item_struct.generics,
        }
    }

    fn collect_implementors(&mut self) -> Vec<Implementor> {
        let mut ret = Vec::new();
        let mut proceed_field = |field: &mut Field| {
            field.attrs = field
                .attrs
                .clone()
                .into_iter()
                .filter(|attr| {
                    if let Some(arg) = Argument::from_attr(attr).unwrap_or_abort() {
                        for implem in arg.implementors {
                            if !ret.iter().any(|a| a == &implem) {
                                ret.push(implem);
                            }
                        }
                        false
                    } else {
                        true
                    }
                })
                .collect();
        };
        match self {
            Adt::Enum(item_enum) => {
                for variant in item_enum.variants.iter_mut() {
                    for field in variant.fields.iter_mut() {
                        proceed_field(field);
                    }
                }
            }
            Adt::Struct(item_struct) => {
                for field in item_struct.fields.iter_mut() {
                    proceed_field(field);
                }
            }
        }
        ret
    }
}

pub fn implement(arg: &Argument, target_def: &Adt) -> TokenStream {
    let mut copied_target_def = target_def.clone();
    let imp: TokenStream = copied_target_def
        .collect_implementors()
        .into_iter()
        .chain(arg.implementors.iter().cloned())
        .map(|implr| implr.emit_impl(target_def))
        .collect();
    quote! {
        #copied_target_def
        #imp
    }
}