rusty_bind/
lib.rs

1//
2// Wildland Project
3//
4// Copyright © 2022 Golem Foundation,
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License version 3 as published by
8// the Free Software Foundation.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18/// This module provides user with definition of a
19/// procedural macro that can be used to decorate
20/// a module designated to be the FFI layer.
21///
22extern crate proc_macro;
23use std::collections::HashMap;
24
25use proc_macro::TokenStream;
26use quote::ToTokens;
27use syn::punctuated::Punctuated;
28use syn::spanned::Spanned;
29use syn::{parse_macro_input, Expr, ExprLit, Lit, MetaNameValue, Token};
30
31///
32/// # Example Rust module that can be translated:
33///
34/// ```ignore
35/// mod our_ffi_module {
36///     use rusty_bind::binding_wrapper;
37///     use std::sync::{Arc, Mutex};
38///
39///
40///     // Define Error type and `()` type.
41///     type ErrorType = String;
42///     type VoidType = ();
43///
44///     pub trait SomeTrait: std::fmt::Debug {
45///         fn some_trait_method(&self);
46///     }
47///
48///     #[derive(Clone, Debug)]
49///     pub struct Foo(u32);
50///     impl SomeTrait for Foo {
51///         fn some_trait_method(&self) {
52///         }
53///     }
54///
55///     #[derive(Clone, Debug)]
56///     pub struct CustomType(u32);
57///     impl CustomType {
58///         pub fn return_another_custom_type(&self) -> AnotherCustomType {
59///             AnotherCustomType(20u64)
60///         }
61///     }
62///
63///     #[derive(Clone, Debug)]
64///     pub struct AnotherCustomType(u64);
65///     impl AnotherCustomType {
66///         pub fn take_primitive_type_and_return_primitive_type(&self, a: u32) -> String {
67///             "Result".to_owned()
68///         }
69///     }
70///
71///     #[binding_wrapper(source = "path/to/generated/rs/file")]
72///     mod ffi {
73///         extern "Rust" {
74///             type CustomType;
75///             fn return_another_custom_type(self: &CustomType) -> AnotherCustomType;
76///
77///             type AnotherCustomType;
78///
79///             #[cfg(target_os = "macos")]
80///             fn take_primitive_type_and_return_primitive_type(self: &AnotherCustomType, a: u32) -> String;
81///
82///             fn some_trait_method(self: &Arc<Mutex<dyn SomeTrait>>);
83///             type ErrorType;
84///         }
85///     }
86/// }
87/// ```
88///
89#[proc_macro_attribute]
90pub fn binding_wrapper(attr: TokenStream, _input: TokenStream) -> TokenStream {
91    let parsed_attr =
92        parse_macro_input!(attr with Punctuated::<MetaNameValue, Token![,]>::parse_terminated);
93
94    let span = parsed_attr.span();
95
96    let name_values: HashMap<String, Expr> = parsed_attr
97        .into_iter()
98        .map(|nv| (nv.path.into_token_stream().to_string(), nv.value))
99        .collect();
100
101    let source = match name_values.get("source") {
102        Some(v) => match v {
103            Expr::Lit(ExprLit {
104                lit: Lit::Str(v), ..
105            }) => v.value(),
106            _ => {
107                return syn::Error::new(span, "invalid source type, expected str")
108                    .to_compile_error()
109                    .into()
110            }
111        },
112        None => {
113            return syn::Error::new(span, "source parameter is missing")
114                .to_compile_error()
115                .into()
116        }
117    };
118
119    quote::quote! { include!(#source); }.into()
120}