use proc_macro::TokenStream;
use quote::quote;
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::parse_macro_input;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::GenericParam;
use syn::Ident;
use syn::ItemFn;
use syn::Lifetime;
use syn::Token;
use syn::TypePath;
#[proc_macro_attribute]
pub fn mono(attr: TokenStream, func: TokenStream) -> TokenStream {
let mono_attr = parse_macro_input!(attr as TypeEqs);
let input = func.clone();
let fn_sig = parse_macro_input!(input as ItemFn).sig;
let fn_span = fn_sig.span();
let func_ident = fn_sig.ident.clone();
let mut params = vec![];
for g in fn_sig.generics.params.into_iter() {
if let Some(t) = mono_attr
.eqs
.iter()
.find(|eq| match (&g, &eq.type_or_lifetime) {
(GenericParam::Type(t1), TypeOrLifetime::Type(t2)) => &t1.ident == t2,
(_, _) => false,
})
{
params.push(t.param.clone());
} else if matches!(g, GenericParam::Type(_)) {
let err = syn::Error::new(fn_span, "all the type parameters should be spelled out")
.into_compile_error()
.into();
return err;
}
}
let mut expand = TokenStream::from(quote! {
pub const _: *const () = (&#func_ident::<#(#params,)*>) as *const _ as _;
});
expand.extend(func);
expand
}
#[proc_macro]
pub fn mono_macro(input: TokenStream) -> TokenStream {
let path = parse_macro_input!(input as TypePath);
TokenStream::from(quote! {
pub const _: *const () = (&#path) as *const _ as _;
})
}
struct TypeEqs {
eqs: Punctuated<TypeEqTo, Token![,]>,
}
impl Parse for TypeEqs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(TypeEqs {
eqs: { input.parse_terminated(TypeEqTo::parse)? },
})
}
}
struct TypeEqTo {
type_or_lifetime: TypeOrLifetime,
#[allow(dead_code)]
eq_token: Token![=],
param: TypeOrLifetime,
}
impl Parse for TypeEqTo {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(TypeEqTo {
type_or_lifetime: input.parse()?,
eq_token: input.parse()?,
param: input.parse()?,
})
}
}
#[derive(Clone, Debug)]
enum TypeOrLifetime {
Type(Ident),
Lifetime(syn::Lifetime),
}
impl Parse for TypeOrLifetime {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Lifetime) {
input.parse().map(TypeOrLifetime::Lifetime)
} else if lookahead.peek(Ident) {
input.parse().map(TypeOrLifetime::Type)
} else {
Err(lookahead.error())
}
}
}
extern crate proc_macro2;
use proc_macro2::TokenStream as TokenStream2;
impl quote::ToTokens for TypeOrLifetime {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
TypeOrLifetime::Lifetime(l) => l.to_tokens(tokens),
TypeOrLifetime::Type(t) => t.to_tokens(tokens),
}
}
}