d1_rom_rt_macros/
lib.rs

1//! D1 ROM runtime procedural macros.
2
3use proc_macro2::Span;
4use quote::quote;
5use syn::{
6    parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn, ReturnType, Type, Visibility,
7};
8
9use proc_macro::TokenStream;
10
11/// ROM stage function entry.
12#[proc_macro_attribute]
13pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
14    let f = parse_macro_input!(input as ItemFn);
15
16    // check the function arguments
17    if f.sig.inputs.len() > 1 {
18        return parse::Error::new(
19            f.sig.inputs.last().unwrap().span(),
20            "`#[entry]` function should include at most one parameter",
21        )
22        .to_compile_error()
23        .into();
24    }
25
26    for arg in &f.sig.inputs {
27        match arg {
28            FnArg::Receiver(_) => {
29                return parse::Error::new(arg.span(), "invalid argument")
30                    .to_compile_error()
31                    .into();
32            }
33            FnArg::Typed(t) => {
34                if let Type::Path(_p) = &*t.ty {
35                    // empty
36                } else {
37                    return parse::Error::new(t.ty.span(), "argument type must be a path")
38                        .to_compile_error()
39                        .into();
40                }
41            }
42        }
43    }
44
45    // check the function signature
46    let valid_signature = f.sig.constness.is_none()
47        && f.sig.asyncness.is_none()
48        && f.vis == Visibility::Inherited
49        && f.sig.abi.is_none()
50        && f.sig.generics.params.is_empty()
51        && f.sig.generics.where_clause.is_none()
52        && f.sig.variadic.is_none()
53        && match f.sig.output {
54            ReturnType::Default => true,
55            ReturnType::Type(_, ref ty) => {
56                if let Type::Path(_path) = &**ty {
57                    true
58                } else {
59                    false
60                }
61            }
62        };
63
64    if !valid_signature {
65        return parse::Error::new(
66            f.span(),
67            "`#[entry]` function must have signature `[unsafe] fn([params: Parameters]) [-> Handover]`",
68        )
69        .to_compile_error()
70        .into();
71    }
72
73    if !args.is_empty() {
74        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
75            .to_compile_error()
76            .into();
77    }
78
79    let attrs = f.attrs;
80    let unsafety = f.sig.unsafety;
81    let args = f.sig.inputs;
82    let stmts = f.block.stmts;
83    let ret = f.sig.output;
84
85    quote!(
86        #[allow(non_snake_case)]
87        #[export_name = "main"]
88        #(#attrs)*
89        pub #unsafety fn __rom_rt__main(#args) #ret {
90            #(#stmts)*
91        }
92    )
93    .into()
94}