1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{quote, ToTokens};
4use syn::{parse_macro_input, parse_quote};
5
6use crate::{cargo, construct};
7
8use self::input::Input;
9
10mod input;
11
12pub(crate) fn expand(input: TokenStream) -> TokenStream {
13 let bitflags_input = TokenStream2::from(input.clone());
14 let input = parse_macro_input!(input as Input);
15
16 // Encode package and disambiguator to provide the decoder with all info it needs (even if
17 // technically redundant, since it's also stored in the symbol we create).
18 let format_string = format!(
19 "{{={}:__internal_bitflags_{}@{}@{}@{}}}",
20 input.ty().to_token_stream(),
21 input.ident(),
22 cargo::package_name(),
23 construct::crate_local_disambiguator(),
24 cargo::crate_name(),
25 );
26 let format_tag = construct::interned_string(
27 &format_string,
28 "bitflags",
29 false,
30 None,
31 &parse_quote!(defmt),
32 );
33
34 let ident = input.ident();
35 let ty = input.ty();
36 let flag_statics = codegen_flag_statics(&input);
37 quote!(
38 const _: () = {
39 fn assert<T: defmt::export::UnsignedInt>() {}
40 assert::<#ty>;
41
42 #(#flag_statics)*
43 };
44
45 defmt::export::bitflags! {
46 #bitflags_input
47 }
48
49 impl defmt::Format for #ident {
50 fn format(&self, f: defmt::Formatter) {
51 defmt::unreachable!()
52 }
53
54 fn _format_tag() -> defmt::Str {
55 #format_tag
56 }
57
58 fn _format_data(&self) {
59 // There's a method available for every supported bitflags type.
60 defmt::export::#ty(&self.bits());
61 }
62 }
63
64 )
65 .into()
66}
67
68fn codegen_flag_statics(input: &Input) -> Vec<TokenStream2> {
69 input
70 .flags()
71 .enumerate()
72 .map(|(i, flag)| {
73 let cfg_attrs = flag.cfg_attrs();
74 let var_name = flag.ident();
75 let struct_name = input.ident();
76 let repr_ty = input.ty();
77
78 let sym_name = construct::mangled_symbol_name(
79 "bitflags_value",
80 &format!("{}::{i}::{}", input.ident(), flag.ident()),
81 );
82
83 quote! {
84 #(#cfg_attrs)*
85 #[cfg_attr(target_os = "macos", link_section = ".defmt,end")]
86 #[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")]
87 #[export_name = #sym_name]
88 static #var_name: u128 = {
89 // NB: It might be tempting to just do `#value as u128` here, but that
90 // causes a value such as `1 << 127` to be evaluated as an `i32`, which
91 // overflows. So we instead coerce (but don't cast) it to the bitflags' raw
92 // type, and then cast that to u128.
93 let coerced_value: #repr_ty = #struct_name::#var_name.bits;
94 coerced_value as u128
95 };
96 }
97 })
98 .collect::<Vec<_>>()
99}
100