diff --git a/crates/wiggle/crates/generate/src/types/flags.rs b/crates/wiggle/crates/generate/src/types/flags.rs index 60aa671310..b0ba9c86a6 100644 --- a/crates/wiggle/crates/generate/src/types/flags.rs +++ b/crates/wiggle/crates/generate/src/types/flags.rs @@ -13,20 +13,17 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty witx::IntRepr::U64 => witx::AtomType::I64, }); - let mut flag_constructors = vec![]; - let mut all_values = 0; + let mut names_ = vec![]; + let mut values_ = vec![]; for (i, f) in f.flags.iter().enumerate() { let name = names.flag_member(&f.name); let value = 1u128 .checked_shl(u32::try_from(i).expect("flag value overflow")) .expect("flag value overflow"); let value_token = Literal::u128_unsuffixed(value); - flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token))); - all_values += value; + names_.push(name); + values_.push(value_token); } - let all_values_token = Literal::u128_unsuffixed(all_values); - - let ident_str = ident.to_string(); quote! { #[repr(transparent)] @@ -34,18 +31,41 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty pub struct #ident(#repr); impl #ident { - #(#flag_constructors);*; - pub const EMPTY_FLAGS: #ident = #ident(0 as #repr); - pub const ALL_FLAGS: #ident = #ident(#all_values_token); + #(pub const #names_: #ident = #ident(#values_);)* + #[inline] + pub const fn empty() -> Self { + #ident(0) + } + + #[inline] + pub const fn all() -> Self { + #ident(#(#values_)|*) + } + + #[inline] pub fn contains(&self, other: &#ident) -> bool { - !*self & *other == Self::EMPTY_FLAGS + !*self & *other == Self::empty() } } impl ::std::fmt::Display for #ident { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}({:#b})", #ident_str, self.0) + let mut first = true; + #( + if self.0 & #values_ == #values_ { + if !first { + f.write_str("|")?; + } + first = false; + f.write_fmt(format_args!("{}", stringify!(#names_).to_lowercase()))?; + } + )* + if first { + f.write_str("empty")?; + } + f.write_fmt(format_args!(" ({:#x})", self.0))?; + Ok(()) } } @@ -98,7 +118,7 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty impl ::std::convert::TryFrom<#repr> for #ident { type Error = wiggle_runtime::GuestError; fn try_from(value: #repr) -> Result { - if #repr::from(!#ident::ALL_FLAGS) & value != 0 { + if #repr::from(!#ident::all()) & value != 0 { Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident))) } else { Ok(#ident(value)) diff --git a/crates/wiggle/tests/flags.rs b/crates/wiggle/tests/flags.rs index 78939fc3d6..7a6ddcaebf 100644 --- a/crates/wiggle/tests/flags.rs +++ b/crates/wiggle/tests/flags.rs @@ -25,7 +25,7 @@ impl<'a> flags::Flags for WasiCtx<'a> { } fn car_config_strat() -> impl Strategy { - (1u8..=types::CarConfig::ALL_FLAGS.into()) + (1u8..=types::CarConfig::all().into()) .prop_map(|v| { types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag") }) @@ -99,3 +99,15 @@ proptest! { e.test() } } + +#[test] +fn flags_fmt() { + let empty = format!("{}", types::CarConfig::empty()); + assert_eq!(empty, "empty (0x0)"); + let one_flag = format!("{}", types::CarConfig::AWD); + assert_eq!(one_flag, "awd (0x2)"); + let two_flags = format!("{}", types::CarConfig::AUTOMATIC | types::CarConfig::SUV); + assert_eq!(two_flags, "automatic|suv (0x5)"); + let all = format!("{}", types::CarConfig::all()); + assert_eq!(all, "automatic|awd|suv (0x7)"); +}