1use std::str::FromStr;
2
3use darling::export::NestedMeta;
4use darling::FromMeta;
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::{ReturnType, Type};
8
9use crate::util::*;
10
11enum Flavor {
12 Standard,
13 Wasm,
14}
15
16pub(crate) struct Arch {
17 default_entry: Option<&'static str>,
18 flavor: Flavor,
19}
20
21pub static ARCH_AVR: Arch = Arch {
22 default_entry: Some("avr_device::entry"),
23 flavor: Flavor::Standard,
24};
25
26pub static ARCH_RISCV: Arch = Arch {
27 default_entry: Some("riscv_rt::entry"),
28 flavor: Flavor::Standard,
29};
30
31pub static ARCH_CORTEX_M: Arch = Arch {
32 default_entry: Some("cortex_m_rt::entry"),
33 flavor: Flavor::Standard,
34};
35
36pub static ARCH_SPIN: Arch = Arch {
37 default_entry: None,
38 flavor: Flavor::Standard,
39};
40
41pub static ARCH_STD: Arch = Arch {
42 default_entry: None,
43 flavor: Flavor::Standard,
44};
45
46pub static ARCH_WASM: Arch = Arch {
47 default_entry: Some("wasm_bindgen::prelude::wasm_bindgen(start)"),
48 flavor: Flavor::Wasm,
49};
50
51#[derive(Debug, FromMeta, Default)]
52struct Args {
53 #[darling(default)]
54 entry: Option<String>,
55}
56
57pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {
58 let mut errors = TokenStream::new();
59
60 // If any of the steps for this macro fail, we still want to expand to an item that is as close
61 // to the expected output as possible. This helps out IDEs such that completions and other
62 // related features keep working.
63 let f: ItemFn = match syn::parse2(item.clone()) {
64 Ok(x) => x,
65 Err(e) => return token_stream_with_error(item, e),
66 };
67
68 let args = match NestedMeta::parse_meta_list(args) {
69 Ok(x) => x,
70 Err(e) => return token_stream_with_error(item, e),
71 };
72
73 let args = match Args::from_list(&args) {
74 Ok(x) => x,
75 Err(e) => {
76 errors.extend(e.write_errors());
77 Args::default()
78 }
79 };
80
81 let fargs = f.sig.inputs.clone();
82
83 if f.sig.asyncness.is_none() {
84 error(&mut errors, &f.sig, "main function must be async");
85 }
86 if !f.sig.generics.params.is_empty() {
87 error(&mut errors, &f.sig, "main function must not be generic");
88 }
89 if !f.sig.generics.where_clause.is_none() {
90 error(&mut errors, &f.sig, "main function must not have `where` clauses");
91 }
92 if !f.sig.abi.is_none() {
93 error(&mut errors, &f.sig, "main function must not have an ABI qualifier");
94 }
95 if !f.sig.variadic.is_none() {
96 error(&mut errors, &f.sig, "main function must not be variadic");
97 }
98 match &f.sig.output {
99 ReturnType::Default => {}
100 ReturnType::Type(_, ty) => match &**ty {
101 Type::Tuple(tuple) if tuple.elems.is_empty() => {}
102 Type::Never(_) => {}
103 _ => error(
104 &mut errors,
105 &f.sig,
106 "main function must either not return a value, return `()` or return `!`",
107 ),
108 },
109 }
110
111 if fargs.len() != 1 {
112 error(&mut errors, &f.sig, "main function must have 1 argument: the spawner.");
113 }
114
115 let entry = match args.entry.as_deref().or(arch.default_entry) {
116 None => TokenStream::new(),
117 Some(x) => match TokenStream::from_str(x) {
118 Ok(x) => quote!(#[#x]),
119 Err(e) => {
120 error(&mut errors, &f.sig, e);
121 TokenStream::new()
122 }
123 },
124 };
125
126 let f_body = f.body;
127 let out = &f.sig.output;
128
129 let (main_ret, mut main_body) = match arch.flavor {
130 Flavor::Standard => (
131 quote!(!),
132 quote! {
133 unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {
134 ::core::mem::transmute(t)
135 }
136
137 let mut executor = ::embassy_executor::Executor::new();
138 let executor = unsafe { __make_static(&mut executor) };
139 executor.run(|spawner| {
140 spawner.must_spawn(__embassy_main(spawner));
141 })
142 },
143 ),
144 Flavor::Wasm => (
145 quote!(Result<(), wasm_bindgen::JsValue>),
146 quote! {
147 let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new()));
148
149 executor.start(|spawner| {
150 spawner.must_spawn(__embassy_main(spawner));
151 });
152
153 Ok(())
154 },
155 ),
156 };
157
158 if !errors.is_empty() {
159 main_body = quote! {loop{}};
160 }
161
162 let result = quote! {
163 #[::embassy_executor::task()]
164 async fn __embassy_main(#fargs) #out {
165 #f_body
166 }
167
168 #entry
169 fn main() -> #main_ret {
170 #main_body
171 }
172
173 #errors
174 };
175
176 result
177}
178