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()
}