use proc_macro2::TokenStream; pub fn attribute_native_flags( native_type: TokenStream, structure: synstructure::Structure, ) -> TokenStream { let ast = structure.ast(); let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::(); let in_vis = &ast.vis; let name = &ast.ident; let hidden_impl_mod = syn::Ident::new(&format!("_{}_hidden_native_flags", name), proc_macro2::Span::call_site()); let enumdata = match &ast.data { syn::Data::Enum(de) => de, _ => panic!("not an enum"), }; let mut consts = TokenStream::new(); let mut known_mask = TokenStream::new(); let mut dbg = TokenStream::new(); for variant in &enumdata.variants { let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::(); let disc_name = &variant.ident; let disc = variant.discriminant.as_ref().map(|(_, d)| d).expect("all variants need explicit bitmask discriminants"); consts.extend(quote! { #variant_attrs pub const #disc_name: Flag = Flag { mask: #disc }; }); known_mask.extend(quote!{ | #disc }); dbg.extend(quote! { if self & Self::#disc_name { if !first { f.write_str(" | ")?; } first = false; f.write_str(stringify!(#disc_name))?; } }); } let bitops = quote! { impl core::ops::BitAnd for super::#name { type Output = bool; fn bitand(self, rhs: Flag) -> Self::Output { rhs.mask == self.0 & rhs.mask } } impl core::ops::BitAnd for &super::#name { type Output = bool; fn bitand(self, rhs: Flag) -> Self::Output { rhs.mask == self.0 & rhs.mask } } impl core::ops::BitOr for Flag { type Output = super::#name; fn bitor(self, rhs: Flag) -> Self::Output { super::#name(self.mask | rhs.mask) } } impl core::ops::BitOr for super::#name { type Output = super::#name; fn bitor(self, rhs: Flag) -> Self::Output { super::#name(self.0 | rhs.mask) } } impl core::ops::BitOr for Flag { type Output = super::#name; fn bitor(self, rhs: super::#name) -> Self::Output { super::#name(self.mask | rhs.0) } } }; quote! { #in_attrs /// /// The `Flag` constants can be used to check if a flag is set /// by using bitwise and: `flags & flag`. /// /// They can be converted to and combined with bitwise or to set /// flags. Their bitmask is available as public struct member /// `mask`. #[derive(Clone, Copy, PartialEq, Eq)] #in_vis struct #name(pub #native_type); mod #hidden_impl_mod { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Flag { pub mask: #native_type, } const known_mask: #native_type = 0 #known_mask; impl super::#name { pub fn new_flag(mask: #native_type) -> Flag { Flag { mask } } #consts } #bitops impl From for super::#name { fn from(v: Flag) -> Self { Self(v.mask) } } impl core::fmt::Debug for super::#name { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { let mut first = true; let unknown_bits = self.0 & !known_mask; #dbg if unknown_bits != 0 { if !first { f.write_str(" | ")?; } write!(f, "unknown(0x{:x})", unknown_bits)?; } else if first { f.write_str("(empty)")?; } Ok(()) } } } } }