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}