esp_hal_procmacros/
embassy.rs1use darling::ast::NestedMeta;
2use main_mod::*;
3use proc_macro::TokenStream;
4use syn::{
5 parse::{Parse, ParseBuffer},
6 punctuated::Punctuated,
7 Token,
8};
9
10pub struct Args {
11 pub(crate) meta: Vec<NestedMeta>,
12}
13
14impl Parse for Args {
15 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
16 let meta = Punctuated::<NestedMeta, Token![,]>::parse_terminated(input)?;
17 Ok(Args {
18 meta: meta.into_iter().collect(),
19 })
20 }
21}
22
23pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
24 let args = syn::parse_macro_input!(args as Args);
25 let f = syn::parse_macro_input!(item as syn::ItemFn);
26
27 run(&args.meta, f, main_fn()).unwrap_or_else(|x| x).into()
28}
29
30pub mod main_mod {
31 use std::{cell::RefCell, fmt::Display, thread};
32
33 use darling::{export::NestedMeta, FromMeta};
34 use proc_macro2::TokenStream;
35 use quote::{quote, ToTokens};
36 use syn::{ReturnType, Type};
37
38 #[derive(Debug, FromMeta)]
39 struct Args {}
40
41 pub fn run(
42 args: &[NestedMeta],
43 f: syn::ItemFn,
44 main: TokenStream,
45 ) -> Result<TokenStream, TokenStream> {
46 let _args = Args::from_list(args).map_err(|e| e.write_errors())?;
47
48 let fargs = f.sig.inputs.clone();
49
50 let ctxt = Ctxt::new();
51
52 if f.sig.asyncness.is_none() {
53 ctxt.error_spanned_by(&f.sig, "main function must be async");
54 }
55 if !f.sig.generics.params.is_empty() {
56 ctxt.error_spanned_by(&f.sig, "main function must not be generic");
57 }
58 if f.sig.generics.where_clause.is_some() {
59 ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses");
60 }
61 if f.sig.abi.is_some() {
62 ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier");
63 }
64 if f.sig.variadic.is_some() {
65 ctxt.error_spanned_by(&f.sig, "main function must not be variadic");
66 }
67 match &f.sig.output {
68 ReturnType::Default => {}
69 ReturnType::Type(_, ty) => match &**ty {
70 Type::Tuple(tuple) if tuple.elems.is_empty() => {}
71 Type::Never(_) => {}
72 _ => ctxt.error_spanned_by(
73 &f.sig,
74 "main function must either not return a value, return `()` or return `!`",
75 ),
76 },
77 }
78
79 if fargs.len() != 1 {
80 ctxt.error_spanned_by(&f.sig, "main function must have 1 argument: the spawner.");
81 }
82
83 ctxt.check()?;
84
85 let f_body = f.block;
86 let out = &f.sig.output;
87
88 let result = quote! {
89 #[doc(hidden)]
90 #[::embassy_executor::task()]
91 async fn __embassy_main(#fargs) #out {
92 #f_body
93 }
94
95 #[doc(hidden)]
96 unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {
97 ::core::mem::transmute(t)
98 }
99
100 #main
101 };
102
103 Ok(result)
104 }
105
106 #[derive(Default)]
114 pub struct Ctxt {
115 errors: RefCell<Option<Vec<syn::Error>>>,
118 }
119
120 impl Ctxt {
121 pub fn new() -> Self {
126 Ctxt {
127 errors: RefCell::new(Some(Vec::new())),
128 }
129 }
130
131 pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
135 self.errors
136 .borrow_mut()
137 .as_mut()
138 .unwrap()
139 .push(syn::Error::new_spanned(obj.into_token_stream(), msg));
141 }
142
143 pub fn check(self) -> Result<(), TokenStream> {
146 let errors = self.errors.borrow_mut().take().unwrap();
147 match errors.len() {
148 0 => Ok(()),
149 _ => Err(to_compile_errors(errors)),
150 }
151 }
152 }
153
154 fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
155 let compile_errors = errors.iter().map(syn::Error::to_compile_error);
156 quote!(#(#compile_errors)*)
157 }
158
159 impl Drop for Ctxt {
160 fn drop(&mut self) {
161 if !thread::panicking() && self.errors.borrow().is_some() {
162 panic!("forgot to check for errors");
163 }
164 }
165 }
166
167 pub fn main_fn() -> TokenStream {
168 quote! {
169 #[esp_hal::main]
170 fn main() -> ! {
171 let mut executor = ::esp_hal_embassy::Executor::new();
172 let executor = unsafe { __make_static(&mut executor) };
173 executor.run(|spawner| {
174 spawner.must_spawn(__embassy_main(spawner));
175 })
176 }
177 }
178 }
179}