1extern crate proc_macro;
2
3use ctor::ctor;
4use darling::FromMeta;
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::AttributeArgs;
8
9mod attr;
10mod call_fn;
11mod call_impl;
12mod ext;
13mod function;
14mod ptr_type;
15mod return_type;
16
17use attr::invoke::InvokeParams;
18use ext::*;
19
20#[proc_macro_attribute]
21pub fn marshal(
22 params: proc_macro::TokenStream,
23 function: proc_macro::TokenStream,
24) -> proc_macro::TokenStream {
25 let params = syn::parse_macro_input!(params as AttributeArgs);
26
27 let params = match InvokeParams::from_list(¶ms) {
28 Ok(v) => v,
29 Err(err) => return err.write_errors().into(),
30 };
31
32 match call_with(params, function.into()) {
33 Ok(tokens) => tokens.into(),
34 Err(err) => proc_macro::TokenStream::from(
35 syn::Error::new(err.span(), err.to_string()).to_compile_error(),
36 ),
37 }
38}
39
40#[ctor]
41fn init() {
42 pretty_env_logger::init();
43}
44
45fn call_with(invoke_params: InvokeParams, item: TokenStream) -> Result<TokenStream, syn::Error> {
85 let item: syn::Item = syn::parse2(item.clone()).context("error parsing function body")?;
93 let result = match item {
94 syn::Item::Fn(item) => call_fn::call_with_function(
95 invoke_params.return_marshaler,
96 invoke_params.callback,
97 item,
98 None,
99 ),
100 syn::Item::Impl(item) => call_impl::call_with_impl(invoke_params.prefix, item),
101 item => {
102 log::error!("{:?}", &item);
103 Err(syn::Error::new_spanned(
104 &item,
105 "Only supported on functions and impls",
106 ))
107 }
108 };
109
110 if result.is_err() {
111 log::debug!("macro finished with error");
112 } else {
113 log::debug!("macro finished successfully");
114 }
115
116 result
117}
118
119include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
120
121pub(crate) fn default_marshaler(ty: &syn::Type) -> Option<syn::Path> {
122 DEFAULT_MARSHALERS
123 .get(&*quote! { #ty }.to_string())
124 .and_then(|x| syn::parse_str(x).ok())
125}
126
127pub(crate) fn is_passthrough_type(ty: &syn::Type) -> bool {
128 match ty {
129 syn::Type::BareFn(bare_fn) => bare_fn.abi.is_some(),
130 _ => PASSTHROUGH_TYPES.contains(&&*quote! { #ty }.to_string()),
131 }
132}