[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,
|
witx::IntRepr::U64 => witx::AtomType::I64,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut flag_constructors = vec![];
|
let mut names_ = vec![];
|
||||||
let mut all_values = 0;
|
let mut values_ = vec![];
|
||||||
for (i, f) in f.flags.iter().enumerate() {
|
for (i, f) in f.flags.iter().enumerate() {
|
||||||
let name = names.flag_member(&f.name);
|
let name = names.flag_member(&f.name);
|
||||||
let value = 1u128
|
let value = 1u128
|
||||||
.checked_shl(u32::try_from(i).expect("flag value overflow"))
|
.checked_shl(u32::try_from(i).expect("flag value overflow"))
|
||||||
.expect("flag value overflow");
|
.expect("flag value overflow");
|
||||||
let value_token = Literal::u128_unsuffixed(value);
|
let value_token = Literal::u128_unsuffixed(value);
|
||||||
flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token)));
|
names_.push(name);
|
||||||
all_values += value;
|
values_.push(value_token);
|
||||||
}
|
}
|
||||||
let all_values_token = Literal::u128_unsuffixed(all_values);
|
|
||||||
|
|
||||||
let ident_str = ident.to_string();
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
@@ -34,18 +31,41 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty
|
|||||||
pub struct #ident(#repr);
|
pub struct #ident(#repr);
|
||||||
|
|
||||||
impl #ident {
|
impl #ident {
|
||||||
#(#flag_constructors);*;
|
#(pub const #names_: #ident = #ident(#values_);)*
|
||||||
pub const EMPTY_FLAGS: #ident = #ident(0 as #repr);
|
|
||||||
pub const ALL_FLAGS: #ident = #ident(#all_values_token);
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn empty() -> Self {
|
||||||
|
#ident(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn all() -> Self {
|
||||||
|
#ident(#(#values_)|*)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn contains(&self, other: &#ident) -> bool {
|
pub fn contains(&self, other: &#ident) -> bool {
|
||||||
!*self & *other == Self::EMPTY_FLAGS
|
!*self & *other == Self::empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for #ident {
|
impl ::std::fmt::Display for #ident {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
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 {
|
impl ::std::convert::TryFrom<#repr> for #ident {
|
||||||
type Error = wiggle_runtime::GuestError;
|
type Error = wiggle_runtime::GuestError;
|
||||||
fn try_from(value: #repr) -> Result<Self, 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)))
|
Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident)))
|
||||||
} else {
|
} else {
|
||||||
Ok(#ident(value))
|
Ok(#ident(value))
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl<'a> flags::Flags for WasiCtx<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn car_config_strat() -> impl Strategy<Value = types::CarConfig> {
|
fn car_config_strat() -> impl Strategy<Value = types::CarConfig> {
|
||||||
(1u8..=types::CarConfig::ALL_FLAGS.into())
|
(1u8..=types::CarConfig::all().into())
|
||||||
.prop_map(|v| {
|
.prop_map(|v| {
|
||||||
types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag")
|
types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag")
|
||||||
})
|
})
|
||||||
@@ -99,3 +99,15 @@ proptest! {
|
|||||||
e.test()
|
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