[wiggle] Impl different formatters for flags (#1299)
* Impl different formatters for flags Rather than forcing only binary formatting of flags types, how about we implement all relevant traits (`Binary`, `Octal`, `LowerHex`, and `UpperHex`) and allow the user to pick the most relevant one for their use case? Also, we use at least `Octal` and `LowerHex` in a couple of places in `wasi-common`. * fmt::Display for flags now inspired by bitflags Flags is now by default formatted similarly to how `bitflags` crate does it, namely, `dsync|append (0x11)`. In case we're dealing with an empty set, we get `empty (0x0)`. Because of this, any `Octal`, `LowerHex`, etc., formatters are redundant now. Furthermore, while here, I've rewritten `EMPTY_FLAGS` and `ALL_FLAGS` (where the former means `0x0` and the latter is the union of all possible values) to be `const fn empty()` and `const fn all()` where the latter is an expanded union of primitive representation values out of a macro. This is again largely inspired by the `bitflags` crate. * Test fmt::Display for flags
This commit is contained in:
@@ -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<Self, wiggle_runtime::GuestError> {
|
||||
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))
|
||||
|
||||
@@ -25,7 +25,7 @@ impl<'a> flags::Flags for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn car_config_strat() -> impl Strategy<Value = types::CarConfig> {
|
||||
(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)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user