#![deny(missing_docs)]
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::{
parse::{Parse, ParseStream, Parser},
visit_mut::{self, VisitMut},
AttrStyle, Attribute, AttributeArgs, DataStruct, DeriveInput, Error, FnArg, Generics, Lifetime,
Pat, Result, Signature, Token, Type, TypeReference, TypeTuple, Visibility,
};
#[derive(Clone)]
struct MaybeItemFn {
attrs: Vec<Attribute>,
vis: Visibility,
sig: Signature,
block: TokenStream2,
}
impl Parse for MaybeItemFn {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let attrs = input.call(syn::Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
let block: TokenStream2 = input.parse()?;
Ok(Self {
attrs,
vis,
sig,
block,
})
}
}
impl ToTokens for MaybeItemFn {
fn to_tokens(&self, tokens: &mut TokenStream2) {
tokens.append_all(
self.attrs
.iter()
.filter(|attr| matches!(attr.style, AttrStyle::Outer)),
);
self.vis.to_tokens(tokens);
self.sig.to_tokens(tokens);
self.block.to_tokens(tokens);
}
}
fn parse_attribute_args(input: ParseStream) -> Result<AttributeArgs> {
let mut metas = Vec::new();
loop {
if input.is_empty() {
break;
}
let value = input.parse()?;
metas.push(value);
if input.is_empty() {
break;
}
input.parse::<Token![,]>()?;
}
Ok(metas)
}
struct LifetimeInjector {
lifetime: Lifetime,
}
impl VisitMut for LifetimeInjector {
fn visit_type_reference_mut(&mut self, i: &mut TypeReference) {
visit_mut::visit_type_reference_mut(self, i);
if i.lifetime.is_none() {
i.lifetime = Some(self.lifetime.clone())
}
}
fn visit_lifetime_mut(&mut self, i: &mut Lifetime) {
visit_mut::visit_lifetime_mut(self, i);
if i.ident == "_" {
*i = self.lifetime.clone();
}
}
}
fn net_impl(attr: TokenStream, item: TokenStream) -> Result<TokenStream2> {
let root = quote! { ::rnet::hidden };
let _args = parse_attribute_args.parse(attr)?;
let mut inner_fn = MaybeItemFn::parse.parse(item)?;
let fn_name_str = inner_fn.sig.ident.to_string();
let fn_name = format_ident!("rnet_export_{}", fn_name_str);
inner_fn.sig.ident = Ident::new("inner", Span::call_site());
let args = inner_fn
.sig
.inputs
.iter()
.enumerate()
.map(|(i, arg)| match arg {
FnArg::Receiver(_) => Err(Error::new_spanned(arg, "`self` parameter not supported")),
FnArg::Typed(t) => Ok((
match &*t.pat {
Pat::Ident(x) => x.ident.to_string(),
_ => format!("arg{}", i),
},
t.ty.clone(),
)),
})
.collect::<Result<Vec<_>>>()?;
let (arg_names, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip();
let mut lifetime_injector = LifetimeInjector {
lifetime: Lifetime::new("'net", Span::call_site()),
};
let local_arg_types = arg_types.iter().cloned().map(|mut arg| {
lifetime_injector.visit_type_mut(&mut arg);
arg
});
let ret_type = match &inner_fn.sig.output {
syn::ReturnType::Default => Box::new(Type::Tuple(TypeTuple {
paren_token: Default::default(),
elems: Default::default(),
})),
syn::ReturnType::Type(_, t) => t.clone(),
};
let arg_idents: Vec<_> = (0..arg_types.len())
.map(|i| format_ident!("arg{}", i))
.collect();
Ok(quote! {
#[no_mangle]
pub unsafe extern "C" fn #fn_name<'net>(#(
#arg_idents: <<#local_arg_types as #root::FromNetArg<'net>>::Owned as #root::Net>::Raw
),*) -> <#ret_type as #root::ToNetReturn>::RawReturn {
#[#root::linkme::distributed_slice(#root::EXPORTED_FNS)]
static EXPORTED_FN: #root::FnDesc = #root::FnDesc {
name: #fn_name_str,
args: &[#(
#root::ArgDesc {
name: #arg_names,
ty_: <<#arg_types as #root::FromNetArg>::Owned as #root::FromNet>::FROM_DESC,
}
),*],
ret_ty: <#ret_type as #root::ToNetReturn>::RETURN_DESC,
};
#inner_fn
#root::ToNetReturn::to_raw_return(inner(#(
#root::FromNetArg::borrow_or_take(&mut Some(#root::FromNet::from_raw(#arg_idents)))
),*))
}
})
}
#[proc_macro_attribute]
pub fn net(attr: TokenStream, item: TokenStream) -> TokenStream {
match net_impl(attr, item.clone()) {
Ok(res) => res.into(),
Err(e) => {
let mut res: TokenStream = e.into_compile_error().into();
res.extend(item);
res
}
}
}
fn derive_net_struct_impl(
name: &Ident,
generics: &Generics,
data: &DataStruct,
) -> Result<TokenStream2> {
let root = quote! { ::rnet::hidden };
let name_str = name.to_string();
let (field_name_ident, field_type): (Vec<_>, Vec<_>) = data
.fields
.iter()
.enumerate()
.map(|(i, field)| {
let ident = field
.ident
.clone()
.unwrap_or_else(|| format_ident!("elem{}", i));
((ident.to_string(), ident), &field.ty)
})
.unzip();
let (field_name_str, field_name): (Vec<_>, Vec<_>) = field_name_ident.into_iter().unzip();
let raw_name = format_ident!("Raw{}", name);
let raw_name_str = format!("_Struct{}", name);
Ok(quote! {
const _: () = {
#[#root::linkme::distributed_slice(#root::EXPORTED_STRUCTS)]
static EXPORTED_STRUCT: #root::StructDesc = #root::StructDesc {
name: #name_str,
fields: &[#(
#root::FieldDesc {
name: #field_name_str,
ty_: &#root::TypeDesc {
marshal_in: Some(<#field_type as #root::FromNet>::gen_marshal),
marshal_out: Some(<#field_type as #root::ToNet>::gen_marshal),
..*<#field_type as #root::Net>::DESC
},
}
),*],
};
#[repr(C)]
pub struct #raw_name #generics {
#(
pub #field_name: <#field_type as #root::Net>::Raw,
)*
}
impl Copy for #raw_name {}
impl Clone for #raw_name {
fn clone(&self) -> Self {
*self
}
}
impl Default for #raw_name {
fn default() -> Self {
Self {
#(
#field_name: Default::default(),
)*
}
}
}
unsafe impl #root::Net for #name {
type Raw = #raw_name;
fn gen_type(_ctx: &mut #root::GeneratorContext) -> Box<str> {
#name_str.into()
}
fn gen_raw_type(_ctx: &mut #root::GeneratorContext) -> Box<str> {
#raw_name_str.into()
}
fn is_nullable(_ctx: &mut #root::GeneratorContext) -> bool {
true
}
}
unsafe impl #root::FromNet for #name {
unsafe fn from_raw(arg: Self::Raw) -> Self {
Self {
#(
#field_name: <#field_type as #root::FromNet>::from_raw(arg.#field_name),
)*
}
}
fn gen_marshal(ctx: &mut #root::GeneratorContext, arg: &str) -> Box<str> {
format!("{}.Encode({})", Self::gen_raw_type(ctx), arg).into()
}
}
unsafe impl #root::ToNet for #name {
fn into_raw(self) -> Self::Raw {
#raw_name {
#(
#field_name: #root::ToNet::into_raw(self.#field_name),
)*
}
}
fn gen_marshal(_ctx: &mut #root::GeneratorContext, arg: &str) -> Box<str> {
format!("({}).Decode()", arg).into()
}
}
};
})
}
fn derive_net_impl(item: TokenStream) -> Result<TokenStream2> {
let derive_input = DeriveInput::parse.parse(item)?;
match &derive_input.data {
syn::Data::Struct(s) => {
derive_net_struct_impl(&derive_input.ident, &derive_input.generics, s)
}
_ => Err(Error::new_spanned(
derive_input,
"Net derive can only be applied to structs",
)),
}
}
#[proc_macro_derive(Net)]
pub fn derive_net(item: TokenStream) -> TokenStream {
match derive_net_impl(item) {
Ok(res) => res.into(),
Err(e) => e.into_compile_error().into(),
}
}