use syn::parse_quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use crate::utils::deref_expr;
use crate::utils::generics_declaration_to_generics;
use crate::utils::signature_to_associated_function_call;
use crate::utils::signature_to_method_call;
use crate::utils::trait_to_generic_ident;
#[derive(Debug, PartialEq)]
pub enum Receiver {
Arbitrary,
Ref,
Mut,
Owned,
}
pub trait WrapperType {
const NAME: &'static str;
const RECEIVERS: &'static [Receiver];
const BOUNDS: &'static [&'static str] = &[];
fn wrap(ty: &syn::Ident) -> syn::Type;
fn check_receiver(r: &syn::Receiver) -> syn::Result<()> {
let receivers = Self::RECEIVERS;
let err = if r.colon_token.is_some() && !receivers.contains(&Receiver::Arbitrary) {
Some(format!(
"cannot derive `{}` for a trait declaring methods with arbitrary receiver types",
Self::NAME
))
} else if r.mutability.is_some() && !receivers.contains(&Receiver::Mut) {
Some(format!(
"cannot derive `{}` for a trait declaring `&mut self` methods",
Self::NAME
))
} else if r.reference.is_none() && !receivers.contains(&Receiver::Owned) {
Some(format!(
"cannot derive `{}` for a trait declaring `self` methods",
Self::NAME
))
} else {
None
};
if let Some(msg) = err {
Err(syn::Error::new(r.span(), msg))
} else {
Ok(())
}
}
fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> {
let trait_ident = &trait_.ident;
let generic_type = trait_to_generic_ident(trait_);
let wrapper_type = Self::wrap(&generic_type);
let trait_generics = &trait_.generics;
let where_clause = &trait_.generics.where_clause;
let mut impl_generics = trait_generics.clone();
let mut trait_generic_names = trait_generics.clone();
trait_generic_names.params = generics_declaration_to_generics(&trait_generics.params)?;
let mut methods: Vec<syn::ImplItemFn> = Vec::new();
let mut assoc_types: Vec<syn::ImplItemType> = Vec::new();
for item in trait_.items.iter() {
if let syn::TraitItem::Fn(ref m) = item {
methods.push(Self::derive_method(
m,
trait_ident,
&generic_type,
&trait_generic_names,
)?)
}
if let syn::TraitItem::Type(t) = item {
let t_ident = &t.ident;
let attrs = &t.attrs;
let t_generics = &t.generics;
let where_clause = &t.generics.where_clause;
let mut t_generic_names = t_generics.clone();
t_generic_names.params = generics_declaration_to_generics(&t_generics.params)?;
let item = parse_quote!( #(#attrs)* type #t_ident #t_generics = <#generic_type as #trait_ident #trait_generic_names>::#t_ident #t_generic_names #where_clause ; );
assoc_types.push(item);
}
}
let mut sized = false;
for item in trait_.items.iter() {
if let syn::TraitItem::Fn(ref m) = item {
if let Some(r) = m.sig.receiver() {
sized |= r.reference.is_none();
}
}
}
let span = generic_type.span();
let mut bounds: Punctuated<_, _> = parse_quote!(#trait_ident #trait_generic_names);
if !sized {
bounds.push(parse_quote!(?Sized));
}
for bound in Self::BOUNDS {
let bound_ident = syn::Ident::new(bound, span);
bounds.push(parse_quote!(#bound_ident))
}
impl_generics
.params
.push(syn::GenericParam::Type(syn::TypeParam {
attrs: Vec::new(),
ident: generic_type.clone(),
colon_token: Some(syn::Token),
bounds,
eq_token: None,
default: None,
}));
Ok(parse_quote!(
#[automatically_derived]
impl #impl_generics #trait_ident #trait_generic_names for #wrapper_type #where_clause {
#(#assoc_types)*
#(#methods)*
}
))
}
fn derive_method(
m: &syn::TraitItemFn,
trait_ident: &syn::Ident,
generic_type: &syn::Ident,
trait_generic_names: &syn::Generics,
) -> syn::Result<syn::ImplItemFn> {
let mut call: syn::Expr = if let Some(r) = m.sig.receiver() {
Self::check_receiver(r)?;
let mut call = signature_to_method_call(&m.sig)?;
if r.reference.is_some() {
call.receiver = Box::new(deref_expr(deref_expr(*call.receiver)));
} else {
call.receiver = Box::new(deref_expr(*call.receiver));
}
call.into()
} else {
let call = signature_to_associated_function_call(
&m.sig,
trait_ident,
generic_type,
trait_generic_names,
)?;
call.into()
};
if let Some(async_) = m.sig.asyncness {
let span = async_.span();
call = syn::ExprAwait {
attrs: Vec::new(),
base: Box::new(call),
dot_token: syn::Token,
await_token: syn::Token,
}
.into();
}
let signature = &m.sig;
Ok(syn::parse_quote!(#[inline] #signature { #call }))
}
}