rust-dnsbox/lib/dnsbox-derive/src/native_flags.rs

137 lines
3.2 KiB
Rust

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::<TokenStream>();
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::<TokenStream>();
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<Flag> for super::#name {
type Output = bool;
fn bitand(self, rhs: Flag) -> Self::Output {
rhs.mask == self.0 & rhs.mask
}
}
impl core::ops::BitAnd<Flag> 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<Flag> for super::#name {
type Output = super::#name;
fn bitor(self, rhs: Flag) -> Self::Output {
super::#name(self.0 | rhs.mask)
}
}
impl core::ops::BitOr<super::#name> 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<Flag> 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(())
}
}
}
}
}