mod util;
use std::mem;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
parse::{Parse, ParseStream},
parse_quote, token,
visit_mut::VisitMut as _,
};
#[cfg(doc)]
use syn::{Generics, Type};
use crate::MacroPath;
use self::util::{ElideLifetimes as _, GenericBinder, GenericsExt as _};
#[derive(Debug)]
pub(super) struct Definition {
template: syn::ItemImpl,
generics: syn::Generics,
trait_path: syn::Path,
self_ty: syn::Type,
wrapper_ty: syn::Path,
macro_path: MacroPath,
}
impl Parse for Definition {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let template = input.parse()?;
_ = input.parse::<token::Impl>()?;
let mut generics = input.parse::<syn::Generics>()?;
let trait_path = input.parse()?;
_ = input.parse::<token::As>()?;
let wrapper_ty = input.parse()?;
_ = input.parse::<token::For>()?;
let self_ty = input.parse()?;
if let Some(where_clause) = input.parse::<Option<syn::WhereClause>>()? {
generics.where_clause = Some(where_clause);
}
let mut this = Self {
template,
generics,
trait_path,
self_ty,
wrapper_ty,
macro_path: MacroPath::default(),
};
this.specify_type();
this.specify_trait()?;
this.specify_methods();
this.specify_generics();
Ok(this)
}
}
impl ToTokens for Definition {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.template.to_tokens(tokens);
}
}
impl Definition {
fn is_local_trait(&self) -> bool {
let macro_path = &self.macro_path;
self.wrapper_ty == parse_quote! { #macro_path ::Wrapper }
}
fn specify_type(&mut self) {
self.template.self_ty = self.self_ty.clone().into();
}
fn specify_trait(&mut self) -> syn::Result<()> {
self.template.trait_ =
Some((None, self.trait_path.clone(), token::For::default()));
if let syn::PathArguments::AngleBracketed(trait_generic_args) = &self
.trait_path
.segments
.last()
.unwrap_or_else(|| unreachable!("empty trait path"))
.arguments
{
let mut binder = GenericBinder {
generics: &self
.template
.generics
.bind_arguments(trait_generic_args)?,
};
for i in &mut self.template.items {
if let syn::ImplItem::Fn(m) = i {
binder.visit_impl_item_fn_mut(m);
};
}
}
Ok(())
}
fn specify_methods(&mut self) {
let macro_path = &self.macro_path;
let wrapper_ty = &self.wrapper_ty;
let is_external = !self.is_local_trait();
let mut self_ty = self.self_ty.clone();
self_ty.elide_lifetimes();
let mut trait_path = self.trait_path.clone();
trait_path.elide_lifetimes();
let wrapper: syn::Type = if is_external {
parse_quote! {
<#macro_path::External as #wrapper_ty<#self_ty> >::Wrapper
}
} else {
parse_quote! { #wrapper_ty < #self_ty > }
};
let replace_sig_type = |ty: &mut syn::Type| {
let syn::Type::Path(syn::TypePath {
qself: Some(syn::QSelf { ty: qself_ty, .. }),
..
}) = ty
else {
return;
};
let syn::Type::Path(syn::TypePath {
qself: Some(syn::QSelf { position, .. }),
path,
}) = qself_ty.as_mut()
else {
return;
};
let prev_path =
mem::replace(path, parse_quote! { #wrapper_ty <Self> });
*position = path.segments.len();
for seg in prev_path.segments.into_iter().skip(1) {
path.segments.push(seg);
}
};
let replace_selfcall = |block: &mut syn::Block| {
let Some(syn::Stmt::Expr(
syn::Expr::Call(syn::ExprCall { func, .. }),
_,
)) = block.stmts.first_mut()
else {
return;
};
let syn::Expr::Path(syn::ExprPath {
qself: Some(qself), path, ..
}) = func.as_mut()
else {
return;
};
let orig_path = mem::replace(path, trait_path.clone());
path.segments
.extend(orig_path.segments.into_iter().skip(qself.position));
*qself = syn::QSelf {
lt_token: qself.lt_token,
ty: wrapper.clone().into(),
position: trait_path.segments.len(),
as_token: qself.as_token,
gt_token: qself.gt_token,
};
};
for i in &mut self.template.items {
if let syn::ImplItem::Fn(m) = i {
if is_external {
for arg in &mut m.sig.inputs {
if let syn::FnArg::Typed(syn::PatType { ty, .. }) = arg
{
replace_sig_type(ty);
}
}
if let syn::ReturnType::Type(_, ret_ty) = &mut m.sig.output
{
replace_sig_type(&mut *ret_ty);
};
}
replace_selfcall(&mut m.block);
};
}
}
fn specify_generics(&mut self) {
self.template.generics = self.generics.clone();
}
}