[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:
Jakub Konka
2020-03-13 20:27:34 +01:00
committed by GitHub
parent e495570f07
commit 5024d7bf09
2 changed files with 46 additions and 14 deletions

View File

@@ -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))

View File

@@ -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)");
}