1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
use quote::quote;
use syn::parse::Parse;
#[proc_macro_attribute]
pub fn box_dyn(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
// let mut input: syn::DeriveInput = syn::parse2(input.into()).unwrap();
let additional_bounds = syn::parse_macro_input!(args with syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated);
// let args_parsed: syn::punctuated::Punctuated<syn::Path, syn::Token![,]>::parse_terminated =
// syn::parse2(input.into()).unwrap();
// let args_parsed = syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated
// .parse2(args)
// .unwrap();
let trait_item: syn::ItemTrait = syn::parse2(input.into()).unwrap();
let trait_name = &trait_item.ident;
let trait_generics = &trait_item.generics;
// let trait_with_generics = quote! { #trait_name };
// generics
// dbg!(trait_name.to_string());
let trait_items: Vec<_> = trait_item
.items
.clone()
.into_iter()
.filter_map(|mut item| {
match item {
// An associated constant within the definition of a trait.
syn::TraitItem::Const(ref mut val) => {
// default
// syn::TraitItemConst
val.default = todo!();
Some(item)
// Some(val.into_token_stream())
}
// An associated function within the definition of a trait.
syn::TraitItem::Fn(ref mut func) => {
// syn::TraitItemFn
let func_name = &func.sig.ident;
// dbg!(&func_name);
let receiver = func
.sig
.inputs
.iter()
.find_map(|arg| match arg {
syn::FnArg::Receiver(ty) => Some(ty.clone()),
_ => None,
})
.expect("trait functions need receiver type");
let self_typ =
match (receiver.reference.is_some(), receiver.mutability.is_some()) {
(true, true) => quote! { self.as_mut() },
(true, false) => quote! { self.as_ref() },
(false, _) => quote! { self },
};
// pub reference: Option<(Token![&], Option<Lifetime>)>,
// pub mutability: Option<Token![mut]>,
let param_names: Vec<_> = func
.sig
.inputs
.iter()
.filter_map(|arg| match arg {
syn::FnArg::Typed(ty) => Some(ty.pat.clone()),
_ => None,
})
.collect();
// dbg!(&trait_name);
// dbg!(&func_name);
// dbg!(&self_typ);
// dbg!(¶m_names
// .iter()
// .map(|p| quote! { #p })
// .collect::<Vec<_>>());
// println!(
// "{}",
// pretty_print(quote! {{
// #trait_name::#func_name(#self_typ, #(#param_names),*)
// }})
// );
func.default = Some(
syn::parse2::<syn::Block>(quote! {{
#trait_name::#func_name(#self_typ, #(#param_names),*)
}})
.unwrap(),
);
Some(item)
// Some(func)
// pub attrs: Vec<Attribute>,
// pub sig: Signature,
// pub default: Option<Block>,
//
}
// An associated type within the definition of a trait.
syn::TraitItem::Type(typ) => {
// syn::TraitItemType
typ.default = todo!();
Some(item)
}
// A macro invocation within the definition of a trait.
// syn::TraitItemMacro
// syn::TraitItem::Macro(_) => None,
// Tokens within the definition of a trait not interpreted by Syn.
// syn::TokenStream
// syn::TraitItem::Verbatim(_) => None,
_ => None,
}
})
.collect();
let t = quote! { __BoxDynT };
let trait_where_predicates = &trait_generics
.where_clause
.as_ref()
.map(|clause| &clause.predicates);
let trait_generic_params = &trait_generics.params;
let t_bounds: Vec<_> = [quote! { #trait_name #trait_generics }]
.into_iter()
.chain(additional_bounds.into_iter().map(|b| quote! { #b }))
.collect();
let out = quote! {
#trait_item
impl<#t, #trait_generic_params> #trait_name #trait_generics for Box<#t>
where
#t: #(#t_bounds)+*,
#trait_where_predicates
{
#(#trait_items)*
}
};
println!("{}", pretty_print(&out));
out.into()
}
#[allow(dead_code)]
fn pretty_print<T>(input: T) -> String
where
T: quote::ToTokens,
{
let file: syn::File = syn::parse2(quote! {
fn main() {
#input
}
})
.unwrap();
prettyplease::unparse(&file)
}