152 lines
3.3 KiB
Rust
152 lines
3.3 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(())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|