cffi_impl/
lib.rs

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(&params) {
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
45// fn json_output_path() -> PathBuf {
46//     std::path::Path::new(&std::env::var("OUT_DIR").unwrap()).join("cthulhu.json")
47// }
48
49// fn is_exporting() -> bool {
50//     let target = std::env::var("CTHULHU_PKG").ok();
51
52//     if let Some(target) = target {
53//         let name = match std::env::var("CARGO_PKG_NAME").ok() {
54//             Some(v) => v,
55//             None => return false,
56//         };
57//         let version = match std::env::var("CARGO_PKG_VERSION").ok() {
58//             Some(v) => v,
59//             None => return false,
60//         };
61//         format!("{}-{}", name, version) == target
62//     } else {
63//         false
64//     }
65// }
66
67// pub(crate) struct Context {
68//     pkg_name: String,
69//     pkg_version: String,
70//     cargo_manifest_dir: PathBuf,
71// }
72
73// impl Default for Context {
74//     fn default() -> Context {
75//         Context {
76//             pkg_name:
77//         }
78//         std::env::var("CARGO_PKG_NAME")
79//         std::env::var("CARGO_PKG_VERSION")
80//         std::env::var("CARGO_MANIFEST_DIR")
81//     }
82// }
83
84fn call_with(invoke_params: InvokeParams, item: TokenStream) -> Result<TokenStream, syn::Error> {
85    // if let Some(value) = invoke_params.send_help.as_ref() {
86    //     log::debug!("HELP REQUESTED: {}", value);
87    //     return Ok(item);
88    // }
89
90    // log::debug!("{:?} {:?} {:?}", std::env::var("CARGO_PKG_NAME"), std::env::var("CARGO_PKG_VERSION"), std::env::var("CARGO_MANIFEST_DIR"));
91
92    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}