use std::iter;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use crate::util::set_sig_arg_span;
pub(crate) struct Generator(pub(crate) Arg);
impl portrait_framework::Generate for Generator {
fn generate_const(
&mut self,
ctx: portrait_framework::Context,
item: &syn::TraitItemConst,
) -> syn::Result<syn::ImplItemConst> {
let Arg { ty: delegate_ty, .. } = &self.0;
let trait_path = &ctx.impl_block.trait_.as_ref().expect("checked in framework").1;
let item_ident = &item.ident;
let expr = syn::parse2(quote!(<#delegate_ty as #trait_path>::#item_ident)).unwrap();
Ok(syn::ImplItemConst {
attrs: item.attrs.iter().filter(|attr| attr.path().is_ident("cfg")).cloned().collect(),
vis: syn::Visibility::Inherited,
defaultness: None,
const_token: item.const_token,
ident: item.ident.clone(),
generics: item.generics.clone(),
colon_token: item.colon_token,
ty: item.ty.clone(),
eq_token: syn::Token),
expr,
semi_token: item.semi_token,
})
}
fn generate_fn(
&mut self,
ctx: portrait_framework::Context,
item: &syn::TraitItemFn,
) -> syn::Result<syn::ImplItemFn> {
let Arg { ty: delegate_ty, value: delegate_value } = &self.0;
let trait_path = &ctx.impl_block.trait_.as_ref().expect("checked in framework").1;
let mut sig = item.sig.clone();
if let Some(delegate_expr) = delegate_value {
set_sig_arg_span(&mut sig, delegate_expr.expr.span())?;
}
let sig_ident = sig.ident.clone();
let args = sig
.inputs
.iter_mut()
.map(|fn_arg| match fn_arg {
syn::FnArg::Receiver(receiver) => {
let arg_attrs: Vec<_> = receiver
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
.cloned()
.collect();
let ref_ = if let Some((and, _lifetime)) = &receiver.reference {
Some(quote!(#and))
} else {
None
};
let mut_ = receiver.mutability;
let delegate_expr = &delegate_value
.as_ref()
.ok_or_else(|| {
syn::Error::new_spanned(
&receiver,
"Delegate value must be passed to implement traits with references",
)
})?
.expr;
Ok(quote! { #(#arg_attrs)* #ref_ #mut_ #delegate_expr })
}
syn::FnArg::Typed(typed) => {
let arg_attrs: Vec<_> = typed
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
.cloned()
.collect();
if let syn::Pat::Ident(pat) = &mut *typed.pat {
if pat.ident == "self" {
let delegate_expr = &delegate_value
.as_ref()
.ok_or_else(|| {
syn::Error::new_spanned(
typed,
"Delegate value must be passed to implement traits with \
references",
)
})?
.expr;
return Ok(quote! { #(#arg_attrs)* #delegate_expr });
} else {
pat.mutability = None;
}
}
let pat = &typed.pat;
Ok(quote! { #(#arg_attrs)* #pat })
}
})
.collect::<syn::Result<Vec<_>>>()?;
let inline_attr = syn::Attribute {
pound_token: syn::Token),
style: syn::AttrStyle::Outer,
bracket_token: syn::token::Bracket(Span::call_site()),
meta: syn::Meta::Path(syn::parse2(quote!(inline)).unwrap()),
};
Ok(syn::ImplItemFn {
attrs: item
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
.cloned()
.chain(iter::once(inline_attr))
.collect(),
vis: syn::Visibility::Inherited,
defaultness: None,
sig,
block: syn::parse2(quote! {{
<#delegate_ty as #trait_path>::#sig_ident(#(#args,)*)
}})
.unwrap(),
})
}
fn generate_type(
&mut self,
ctx: portrait_framework::Context,
item: &syn::TraitItemType,
) -> syn::Result<syn::ImplItemType> {
let Arg { ty: delegate_ty, .. } = &self.0;
let trait_path = &ctx.impl_block.trait_.as_ref().expect("checked in framework").1;
let item_ident = &item.ident;
let generics_unbound: Vec<_> = item
.generics
.params
.iter()
.map(|param| match param {
syn::GenericParam::Type(ty) => ty.ident.to_token_stream(),
syn::GenericParam::Lifetime(lt) => lt.lifetime.to_token_stream(),
syn::GenericParam::Const(const_) => const_.ident.to_token_stream(),
})
.collect();
let generics_unbound =
(!generics_unbound.is_empty()).then(|| quote!(< #(#generics_unbound),* >));
let ty = syn::parse2(quote!(<#delegate_ty as #trait_path>::#item_ident #generics_unbound))
.unwrap();
Ok(syn::ImplItemType {
attrs: item.attrs.iter().filter(|attr| attr.path().is_ident("cfg")).cloned().collect(),
vis: syn::Visibility::Inherited,
defaultness: None,
type_token: item.type_token,
ident: item.ident.clone(),
generics: item.generics.clone(),
eq_token: syn::Token),
ty,
semi_token: item.semi_token,
})
}
}
pub(crate) struct Arg {
ty: syn::Type,
value: Option<ArgValue>,
}
struct ArgValue {
_semi_token: syn::Token![;],
expr: syn::Expr,
}
impl Parse for Arg {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ty = input.parse()?;
let value = if input.peek(syn::Token![;]) {
let semi_token = input.parse().expect("peeked");
let expr = input.parse()?;
Some(ArgValue { _semi_token: semi_token, expr })
} else {
None
};
Ok(Self { ty, value })
}
}