ft_derive/
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
extern crate self as ft_derive;

#[proc_macro_attribute]
pub fn processor(
    _attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    handle(item, "processor", "handler")
}

#[proc_macro_attribute]
pub fn wrapped_processor(
    _attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    handle(item, "processor", "wrapped_processor")
}

#[proc_macro_attribute]
pub fn data(
    _attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    handle(item, "data", "handler")
}

#[proc_macro_attribute]
pub fn form(
    _attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    handle(item, "form", "handler")
}

fn handle(item: proc_macro::TokenStream, kind: &str, handler: &str) -> proc_macro::TokenStream {
    let syn::ItemFn {
        attrs,
        vis,
        sig,
        block,
    } = syn::parse_macro_input!(item as syn::ItemFn);

    let fn_name = &sig.ident;
    let fn_name_entrypoint =
        syn::Ident::new(format!("{}__entrypoint", fn_name).as_str(), fn_name.span());
    let return_type: syn::Type =
        syn::parse_str(format!("ft_sdk::{kind}::Result").as_str()).unwrap();
    let handler: syn::Path =
        syn::parse_str(format!("ft_sdk::from_request::{handler}::handle").as_str()).unwrap();

    match sig.output {
        syn::ReturnType::Default => {
            return compiler_error(
                format!("The return type must be ft_sdk::{kind}::Result").as_str(),
            );
        }
        syn::ReturnType::Type(_, ref ty) => {
            if ty.as_ref() != &return_type {
                return compiler_error(
                    format!(
                        "The return type must be ft_sdk::{kind}::Result, not {}.",
                        proc_macro::TokenStream::from(quote::quote! { #ty })
                    )
                    .as_str(),
                );
            }
        }
    };

    let expanded = quote::quote! {
        #[no_mangle]
        pub extern "C" fn #fn_name_entrypoint() {
            #handler(#fn_name)
        }

        #(#attrs)*
        #vis #sig {
            #block
        }
    };

    // println!("{expanded}");
    proc_macro::TokenStream::from(expanded)
}

fn compiler_error(msg: &str) -> proc_macro::TokenStream {
    proc_macro::TokenStream::from(quote::quote! {
        compile_error!(#msg);
    })
}