abi_singleton/
lib.rs

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
119
120
121
122
123
124
125
126
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{format_ident, quote};
use spanned::Spanned;
use syn::*;

fn func_ident(namespace: &str, trait_name: &str, fun_sig_ident: &Ident) -> Ident {
    format_ident!(
        "__api_{}_{}_{}",
        namespace,
        trait_name.to_lowercase(),
        fun_sig_ident
    )
}

pub fn api_trait(item: TokenStream, namespace: &str) -> TokenStream {
    let f = parse_macro_input!(item as ItemTrait);

    let trait_name = f.ident.to_string();

    let mut funcs = Vec::new();

    for item in &f.items {
        if let TraitItem::Fn(func) = item {
            let ident = func.sig.ident.clone();
            let inputs = func.sig.inputs.clone();
            let output = func.sig.output.clone();
            let safe = func.sig.unsafety;

            if func.default.is_some() {
                return parse::Error::new(item.span(), "default trait func is not supported")
                    .to_compile_error()
                    .into();
            }

            let api_name = func_ident(namespace, &trait_name, &ident);

            let mut args = Vec::new();

            for arg in &inputs {
                if let FnArg::Typed(t) = arg {
                    if let Pat::Ident(i) = t.pat.as_ref() {
                        let ident = &i.ident;
                        args.push(quote! { #ident , });
                    }
                }
            }
            funcs.push(quote! {

                pub #safe fn #ident (#inputs) #output{
                    extern "C" {
                        fn #api_name ( #inputs ) #output;
                    }

                    unsafe{ #api_name ( #(#args)* ) }
                }
            });
        } else {
            return parse::Error::new(item.span(), "only func is supported")
                .to_compile_error()
                .into();
        }
    }
    let struct_name = format_ident!("{}Impl", trait_name);

    quote! {
        #f
        pub struct #struct_name;

        impl #struct_name {
            #(#funcs)*
        }

    }
    .into()
}

pub fn api_impl(item: TokenStream, namespace: &str) -> TokenStream {
    let f = parse_macro_input!(item as ItemImpl);

    let mut funcs = Vec::new();

    let ty = f.self_ty.clone();

    let trait_name = f.trait_.as_ref().unwrap().1.get_ident().unwrap();

    for item in &f.items {
        if let ImplItem::Fn(func) = item {
            let ident = func.sig.ident.clone();
            let inputs = func.sig.inputs.clone();
            let output = func.sig.output.clone();

            let api_name = func_ident(
                namespace,
                &trait_name.to_string(),
                &func.sig.ident,
            );

            let mut args = Vec::new();

            for arg in &inputs {
                if let FnArg::Typed(t) = arg {
                    if let Pat::Ident(i) = t.pat.as_ref() {
                        let ident = &i.ident;
                        args.push(quote! { #ident , });
                    }
                }
            }

            funcs.push(quote! {
                #[no_mangle]
                unsafe extern "C" fn #api_name (#inputs) #output{
                    #ty:: #ident ( #(#args)* )
                }
            });
        }
    }

    quote! {
        #f
        #(#funcs)*

    }
    .into()
}