ft_derive/
lib.rs

1extern crate self as ft_derive;
2
3#[proc_macro_attribute]
4pub fn processor(
5    _attr: proc_macro::TokenStream,
6    item: proc_macro::TokenStream,
7) -> proc_macro::TokenStream {
8    handle(item, "processor", "handler")
9}
10
11#[proc_macro_attribute]
12pub fn wrapped_processor(
13    _attr: proc_macro::TokenStream,
14    item: proc_macro::TokenStream,
15) -> proc_macro::TokenStream {
16    handle(item, "processor", "wrapped_processor")
17}
18
19#[proc_macro_attribute]
20pub fn data(
21    _attr: proc_macro::TokenStream,
22    item: proc_macro::TokenStream,
23) -> proc_macro::TokenStream {
24    handle(item, "data", "handler")
25}
26
27#[proc_macro_attribute]
28pub fn form(
29    _attr: proc_macro::TokenStream,
30    item: proc_macro::TokenStream,
31) -> proc_macro::TokenStream {
32    handle(item, "form", "handler")
33}
34
35fn handle(item: proc_macro::TokenStream, kind: &str, handler: &str) -> proc_macro::TokenStream {
36    let syn::ItemFn {
37        attrs,
38        vis,
39        sig,
40        block,
41    } = syn::parse_macro_input!(item as syn::ItemFn);
42
43    let fn_name = &sig.ident;
44    let fn_name_entrypoint =
45        syn::Ident::new(format!("{}__entrypoint", fn_name).as_str(), fn_name.span());
46    let return_type: syn::Type =
47        syn::parse_str(format!("ft_sdk::{kind}::Result").as_str()).unwrap();
48    let handler: syn::Path =
49        syn::parse_str(format!("ft_sdk::from_request::{handler}::handle").as_str()).unwrap();
50
51    match sig.output {
52        syn::ReturnType::Default => {
53            return compiler_error(
54                format!("The return type must be ft_sdk::{kind}::Result").as_str(),
55            );
56        }
57        syn::ReturnType::Type(_, ref ty) => {
58            if ty.as_ref() != &return_type {
59                return compiler_error(
60                    format!(
61                        "The return type must be ft_sdk::{kind}::Result, not {}.",
62                        proc_macro::TokenStream::from(quote::quote! { #ty })
63                    )
64                    .as_str(),
65                );
66            }
67        }
68    };
69
70    let expanded = quote::quote! {
71        #[unsafe(no_mangle)]
72        pub extern "C" fn #fn_name_entrypoint() {
73            #handler(#fn_name)
74        }
75
76        #(#attrs)*
77        #vis #sig {
78            #block
79        }
80    };
81
82    // println!("{expanded}");
83    proc_macro::TokenStream::from(expanded)
84}
85
86fn compiler_error(msg: &str) -> proc_macro::TokenStream {
87    proc_macro::TokenStream::from(quote::quote! {
88        compile_error!(#msg);
89    })
90}