1#![deny(missing_docs)]
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{
11 parse::{Error as ParseError, Parse, ParseStream, Result},
12 parse_macro_input,
13 punctuated::Punctuated,
14 spanned::Spanned,
15 token::Comma,
16 FnArg, Ident, ItemFn, ItemUse, Pat, ReturnType, Type,
17};
18
19struct IdentList {
20 boards: Punctuated<Ident, Comma>,
21}
22
23impl Parse for IdentList {
24 fn parse(input: ParseStream) -> Result<Self> {
25 Ok(IdentList {
26 boards: input.parse_terminated(Ident::parse)?,
27 })
28 }
29}
30
31#[proc_macro_attribute]
36pub fn prelude_fn(args: TokenStream, input: TokenStream) -> TokenStream {
37 let input_use = parse_macro_input!(input as ItemUse);
38 let boards = parse_macro_input!(args as IdentList);
39
40 let cfgs = boards
41 .boards
42 .iter()
43 .map(|board| {
44 let board_name = format!("{}", board);
45 quote!(board = #board_name)
46 })
47 .collect::<Vec<_>>();
48
49 quote!(
50 #[cfg(any(#(#cfgs),*, doc))]
51 #[cfg_attr(feature = "doc-cfg", doc(cfg(any(#(#cfgs),*))))]
52 #input_use
53 )
54 .into()
55}
56
57#[proc_macro_attribute]
63pub fn board_fn(args: TokenStream, input: TokenStream) -> TokenStream {
64 let input_fn = parse_macro_input!(input as ItemFn);
65 let attrs = &input_fn.attrs;
66 let vis = &input_fn.vis;
67 let sig = &input_fn.sig;
68 let fn_name = &sig.ident;
69 let boards = parse_macro_input!(args as IdentList);
70 let module = &boards.boards[0];
71
72 let cfgs = boards
73 .boards
74 .iter()
75 .skip(1)
76 .map(|board| {
77 let board_name = format!("{}", board);
78 quote!(board = #board_name)
79 })
80 .collect::<Vec<_>>();
81
82 let args = sig
84 .inputs
85 .iter()
86 .filter_map(|input| match input {
87 FnArg::Receiver(_) => None,
88 FnArg::Typed(pat) => match *pat.pat {
89 Pat::Ident(ref ident) => Some(ident.clone()),
90 _ => None,
91 },
92 })
93 .collect::<Vec<_>>();
94
95 let impls = boards
96 .boards
97 .iter()
98 .skip(1)
99 .map(|board| {
100 let board_name = format!("{}", board);
101 quote!(
102 #[cfg(board = #board_name)]
103 {
104 crate::hw::board::#board::#module::#fn_name(#(#args),*)
105 }
106 )
107 })
108 .collect::<Vec<_>>();
109
110 quote!(
111 #[cfg(any(#(#cfgs),*, doc))]
112 #[cfg_attr(feature = "doc-cfg", doc(cfg(any(#(#cfgs),*))))]
113 #(#attrs)*
114 #vis #sig {
115 #(#impls)*
116 }
117 )
118 .into()
119}
120
121#[proc_macro_attribute]
128pub fn entry(_args: TokenStream, input: TokenStream) -> TokenStream {
129 let input_fn = parse_macro_input!(input as ItemFn);
130 let sig = &input_fn.sig;
131 let fn_name = &sig.ident;
132
133 let main_is_valid = sig.asyncness.is_some()
134 && sig.generics.params.is_empty()
135 && sig.generics.where_clause.is_none()
136 && sig.inputs.is_empty()
137 && match sig.output {
138 ReturnType::Type(_, ref typ) => matches!(**typ, Type::Never(_)),
139 ReturnType::Default => false,
140 };
141 if !main_is_valid {
142 return ParseError::new(
143 input_fn.sig.span(),
144 format!(
145 "Cntrlr entry function must be of the form `async fn {}() -> !`",
146 fn_name
147 ),
148 )
149 .to_compile_error()
150 .into();
151 }
152
153 quote!(
154 #[export_name = "__cntrlr_main"]
155 pub unsafe extern "C" fn #fn_name() -> ! {
158 #input_fn
159
160 let mut executor = ::cntrlr::task::Executor::new();
161 executor.add_task(#fn_name());
162 executor.run()
164 }
165 )
166 .into()
167}
168
169#[proc_macro_attribute]
175pub fn raw_entry(_args: TokenStream, input: TokenStream) -> TokenStream {
176 let input_fn = parse_macro_input!(input as ItemFn);
177 let sig = &input_fn.sig;
178 let fn_name = &sig.ident;
179
180 let main_is_valid = sig.asyncness.is_none()
181 && match sig.abi {
182 None => false,
183 Some(ref abi) => match abi.name {
184 None => true,
185 Some(ref abi) => abi.value() == "C",
186 },
187 }
188 && sig.generics.params.is_empty()
189 && sig.generics.where_clause.is_none()
190 && sig.inputs.is_empty()
191 && match sig.output {
192 ReturnType::Type(_, ref typ) => matches!(**typ, Type::Never(_)),
193 ReturnType::Default => false,
194 };
195 if !main_is_valid {
196 return ParseError::new(
197 input_fn.sig.span(),
198 format!(
199 "Cntrlr entry function must be of the form `extern \"C\" fn {}() -> !`",
200 fn_name
201 ),
202 )
203 .to_compile_error()
204 .into();
205 }
206
207 quote!(
208 #[export_name = "__cntrlr_main"]
209 #input_fn
210 )
211 .into()
212}
213
214#[proc_macro_attribute]
222pub fn reset(_args: TokenStream, input: TokenStream) -> TokenStream {
223 let input_fn = parse_macro_input!(input as ItemFn);
224 let sig = &input_fn.sig;
225 let fn_name = &sig.ident;
226
227 let reset_is_valid = sig.asyncness.is_none()
228 && match sig.abi {
229 None => false,
230 Some(ref abi) => match abi.name {
231 None => true,
232 Some(ref abi) => abi.value() == "C",
233 },
234 }
235 && sig.generics.params.is_empty()
236 && sig.generics.where_clause.is_none()
237 && sig.inputs.is_empty()
238 && match sig.output {
239 ReturnType::Type(_, ref typ) => matches!(**typ, Type::Never(_)),
240 ReturnType::Default => false,
241 };
242 if !reset_is_valid {
243 return ParseError::new(
244 input_fn.sig.span(),
245 format!(
246 "Cntrlr reset function must be of the form `extern \"C\" fn {}() -> !`",
247 fn_name
248 ),
249 )
250 .to_compile_error()
251 .into();
252 }
253
254 quote!(
255 #[link_section = ".__CNTRLR_START"]
256 #[export_name = "__cntrlr_reset"]
257 #input_fn
258 )
259 .into()
260}