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
//
// Wildland Project
//
// Copyright © 2022 Golem Foundation,
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as published by
// the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// This module provides user with definition of a
/// procedural macro that can be used to decorate
/// a module designated to be the FFI layer.
///
extern crate proc_macro;
use std::collections::HashMap;
use proc_macro::TokenStream;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{parse_macro_input, Lit, MetaNameValue, Token};
///
/// # Example Rust module that can be translated:
///
/// ```ignore
/// mod our_ffi_module {
/// use rusty_bind::binding_wrapper;
/// use std::sync::{Arc, Mutex};
///
///
/// // Define Error type and `()` type.
/// type ErrorType = String;
/// type VoidType = ();
///
/// pub trait SomeTrait: std::fmt::Debug {
/// fn some_trait_method(&self);
/// }
///
/// #[derive(Clone, Debug)]
/// pub struct Foo(u32);
/// impl SomeTrait for Foo {
/// fn some_trait_method(&self) {
/// }
/// }
///
/// #[derive(Clone, Debug)]
/// pub struct CustomType(u32);
/// impl CustomType {
/// pub fn return_another_custom_type(&self) -> AnotherCustomType {
/// AnotherCustomType(20u64)
/// }
/// }
///
/// #[derive(Clone, Debug)]
/// pub struct AnotherCustomType(u64);
/// impl AnotherCustomType {
/// pub fn take_primitive_type_and_return_primitive_type(&self, a: u32) -> String {
/// "Result".to_owned()
/// }
/// }
///
/// #[binding_wrapper(source = "path/to/generated/rs/file")]
/// mod ffi {
/// extern "Rust" {
/// type CustomType;
/// fn return_another_custom_type(self: &CustomType) -> AnotherCustomType;
///
/// type AnotherCustomType;
///
/// #[cfg(target_os = "macos")]
/// fn take_primitive_type_and_return_primitive_type(self: &AnotherCustomType, a: u32) -> String;
///
/// fn some_trait_method(self: &Arc<Mutex<dyn SomeTrait>>);
/// type ErrorType;
/// }
/// }
/// }
/// ```
///
#[proc_macro_attribute]
pub fn binding_wrapper(attr: TokenStream, _input: TokenStream) -> TokenStream {
let parsed_attr =
parse_macro_input!(attr with Punctuated::<MetaNameValue, Token![,]>::parse_terminated);
let name_values: HashMap<String, Lit> = parsed_attr
.iter()
.map(|nv| {
let path = &nv.path;
(quote::quote! { #path }.to_string(), nv.lit.clone())
})
.collect();
let source = match name_values.get("source") {
Some(v) => match v {
syn::Lit::Str(v) => v.value(),
_ => {
return syn::Error::new(parsed_attr.span(), "invalid source type, expected str")
.to_compile_error()
.into()
}
},
None => {
return syn::Error::new(parsed_attr.span(), "source parameter is missing")
.to_compile_error()
.into()
}
};
quote::quote! { include!(#source); }.into()
}