use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{parse_macro_input, parse_quote}; use crate::{cargo, construct}; use self::input::Input; mod input; pub(crate) fn expand(input: TokenStream) -> TokenStream { let bitflags_input = TokenStream2::from(input.clone()); let input = parse_macro_input!(input as Input); // Encode package and disambiguator to provide the decoder with all info it needs (even if // technically redundant, since it's also stored in the symbol we create). let format_string = format!( "{{={}:__internal_bitflags_{}@{}@{}@{}}}", input.ty().to_token_stream(), input.ident(), cargo::package_name(), construct::crate_local_disambiguator(), cargo::crate_name(), ); let format_tag = construct::interned_string( &format_string, "bitflags", false, None, &parse_quote!(defmt), ); let ident = input.ident(); let ty = input.ty(); let flag_statics = codegen_flag_statics(&input); quote!( const _: () = { fn assert() {} assert::<#ty>; #(#flag_statics)* }; defmt::export::bitflags! { #bitflags_input } impl defmt::Format for #ident { fn format(&self, f: defmt::Formatter) { defmt::unreachable!() } fn _format_tag() -> defmt::Str { #format_tag } fn _format_data(&self) { // There's a method available for every supported bitflags type. defmt::export::#ty(&self.bits()); } } ) .into() } fn codegen_flag_statics(input: &Input) -> Vec { input .flags() .enumerate() .map(|(i, flag)| { let cfg_attrs = flag.cfg_attrs(); let var_name = flag.ident(); let struct_name = input.ident(); let repr_ty = input.ty(); let sym_name = construct::mangled_symbol_name( "bitflags_value", &format!("{}::{i}::{}", input.ident(), flag.ident()), ); quote! { #(#cfg_attrs)* #[cfg_attr(target_os = "macos", link_section = ".defmt,end")] #[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")] #[export_name = #sym_name] static #var_name: u128 = { // NB: It might be tempting to just do `#value as u128` here, but that // causes a value such as `1 << 127` to be evaluated as an `i32`, which // overflows. So we instead coerce (but don't cast) it to the bitflags' raw // type, and then cast that to u128. let coerced_value: #repr_ty = #struct_name::#var_name.bits; coerced_value as u128 }; } }) .collect::>() }