use proc_macro2::TokenStream;
use quote::quote;
use syn::{ImplItem, ItemImpl, ReturnType, Type, TypeReference};
pub fn generate(input: &ItemImpl) -> TokenStream {
let ItemImpl {
generics,
self_ty,
items,
..
} = input;
let Some(deref) = items.iter().find_map(|item| match item {
ImplItem::Fn(fn_) if fn_.sig.ident == "deref" => Some(fn_),
_ => None,
}) else {
return TokenStream::new();
};
let target = if let Some(target) = items.iter().find_map(|item| match item {
ImplItem::Type(target) if target.ident == "Target" => Some(target),
_ => None,
}) {
&target.ty
} else if let ReturnType::Type(_, ref ty) = deref.sig.output {
if let Type::Reference(TypeReference {
lifetime: None,
mutability: None,
elem: ref ty,
..
}) = **ty
{
ty
} else {
panic!("invalid return type for deref")
}
} else {
panic!("Target type is not specified")
};
let fn_attrs = &deref.attrs;
let fn_stmts = &deref.block.stmts;
let (impl_generics, _, where_clause) = generics.split_for_impl();
quote! {
impl #impl_generics ::core::ops::Deref for #self_ty
#where_clause {
type Target = #target;
#(#fn_attrs)*
fn deref(&self) -> &Self::Target {
#(#fn_stmts)*
}
}
impl #impl_generics ::core::convert::AsRef<<Self as ::core::ops::Deref>::Target> for #self_ty
#where_clause {
fn as_ref(&self) -> &<Self as ::core::ops::Deref>::Target {
<Self as ::core::ops::Deref>::deref(self)
}
}
}
}