Idiomatic Debug impls for enums and bitflags

Improves consistency with Rust idioms. In particular, readability of
panic messages arising from unwrapped `VkResult`s is vastly improved.
This commit is contained in:
Benjamin Saunders 2019-04-21 16:04:49 -07:00
parent 775a7d035d
commit bb6427d693
4 changed files with 741 additions and 415 deletions

View file

@ -37,6 +37,20 @@ vk_bitflags_wrapped!(
0b1111111111111111111111111111111,
Flags
);
impl fmt::Debug for GpaSqShaderStageFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const KNOWN: &[(Flags, &str)] = &[
(GpaSqShaderStageFlags::PS.0, "PS"),
(GpaSqShaderStageFlags::VS.0, "VS"),
(GpaSqShaderStageFlags::GS.0, "GS"),
(GpaSqShaderStageFlags::ES.0, "ES"),
(GpaSqShaderStageFlags::HS.0, "HS"),
(GpaSqShaderStageFlags::LS.0, "LS"),
(GpaSqShaderStageFlags::CS.0, "CS"),
];
debug_flags(f, KNOWN, self.0)
}
}
impl GpaSqShaderStageFlags {
pub const PS: Self = GpaSqShaderStageFlags(0b1);
pub const VS: Self = GpaSqShaderStageFlags(0b10);

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,17 @@ extern crate ash;
use ash::vk;
#[test]
fn display_flags() {
fn debug_flags() {
assert_eq!(
(vk::AccessFlags::INDIRECT_COMMAND_READ | vk::AccessFlags::VERTEX_ATTRIBUTE_READ)
.to_string(),
format!(
"{:?}",
vk::AccessFlags::INDIRECT_COMMAND_READ | vk::AccessFlags::VERTEX_ATTRIBUTE_READ
),
"INDIRECT_COMMAND_READ | VERTEX_ATTRIBUTE_READ"
);
}
#[test]
fn display_enum() {
assert_eq!(vk::ChromaLocation::MIDPOINT.to_string(), "MIDPOINT");
fn debug_enum() {
assert_eq!(format!("{:?}", vk::ChromaLocation::MIDPOINT), "MIDPOINT");
}

View file

@ -229,11 +229,6 @@ pub fn vk_bitflags_wrapped_macro() -> Tokens {
$name(0)
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({:b})", stringify!($name), self.0)
}
}
impl $name {
#[inline]
@ -939,8 +934,8 @@ pub fn generate_extension_constants<'a>(
};
let ident = name_to_tokens(&extends);
const_values
.entry(ident.clone())
.or_insert_with(Vec::new)
.get_mut(&ident)
.unwrap()
.push(ext_constant.variant_ident(&extends));
let impl_block = bitflags_impl_block(ident, &extends, &[&ext_constant]);
let doc_string = format!("Generated from '{}'", extension_name);
@ -1037,6 +1032,7 @@ pub fn generate_typedef(typedef: &vkxml::Typedef) -> Tokens {
pub fn generate_bitmask(
bitmask: &vkxml::Bitmask,
bitflags_cache: &mut HashSet<Ident>,
const_values: &mut BTreeMap<Ident, Vec<Ident>>,
) -> Option<Tokens> {
// Workaround for empty bitmask
if bitmask.name.is_empty() {
@ -1053,6 +1049,7 @@ pub fn generate_bitmask(
return None;
};
bitflags_cache.insert(ident.clone());
const_values.insert(ident.clone(), Vec::new());
let khronos_link = khronos_link(&bitmask.name);
Some(quote! {
#[repr(transparent)]
@ -1155,11 +1152,12 @@ pub fn generate_enum<'a>(
_ => None,
})
.collect_vec();
let values = const_values.entry(ident.clone()).or_insert_with(Vec::new);
let mut values = Vec::with_capacity(constants.len());
for constant in &constants {
const_cache.insert(constant.name.as_str());
values.push(constant.variant_ident(&_enum.name));
}
const_values.insert(ident.clone(), values);
let khronos_link = khronos_link(&_enum.name);
@ -1189,7 +1187,7 @@ pub fn generate_enum<'a>(
} else {
let impl_block = bitflags_impl_block(ident, &_enum.name, &constants);
let enum_quote = quote! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
#[doc = #khronos_link]
pub struct #ident(pub(crate) i32);
@ -1253,7 +1251,7 @@ pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> Tokens {
if let Some(x) = name {
fmt.write_str(x)
} else {
write!(fmt, "{}", self.0)
self.0.fmt(fmt)
}
}
}
@ -1881,13 +1879,16 @@ pub fn generate_definition(
union_types: &HashSet<&str>,
root_structs: &HashSet<String>,
bitflags_cache: &mut HashSet<Ident>,
const_values: &mut BTreeMap<Ident, Vec<Ident>>,
) -> Option<Tokens> {
match *definition {
vkxml::DefinitionsElement::Typedef(ref typedef) => Some(generate_typedef(typedef)),
vkxml::DefinitionsElement::Struct(ref _struct) => {
Some(generate_struct(_struct, root_structs, union_types))
}
vkxml::DefinitionsElement::Bitmask(ref mask) => generate_bitmask(mask, bitflags_cache),
vkxml::DefinitionsElement::Bitmask(ref mask) => {
generate_bitmask(mask, bitflags_cache, const_values)
}
vkxml::DefinitionsElement::Handle(ref handle) => generate_handle(handle),
vkxml::DefinitionsElement::FuncPtr(ref fp) => Some(generate_funcptr(fp)),
vkxml::DefinitionsElement::Union(ref union) => Some(generate_union(union)),
@ -2012,48 +2013,45 @@ pub fn generate_feature_extension<'a>(
}
}
pub fn generate_const_displays<'a>(const_values: &BTreeMap<Ident, Vec<Ident>>) -> Tokens {
let impls = const_values
.iter()
.filter(|(ty, _)| *ty != "Result")
.map(|(ty, values)| {
if ty.to_string().contains("Flags") {
let cases = values.iter().map(|value| {
let name = value.to_string();
quote! { (#ty::#value.0, #name) }
});
quote! {
impl fmt::Display for #ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const KNOWN: &[(Flags, &str)] = &[#(#cases),*];
display_flags(f, KNOWN, self.0)
}
pub fn generate_const_debugs<'a>(const_values: &BTreeMap<Ident, Vec<Ident>>) -> Tokens {
let impls = const_values.iter().map(|(ty, values)| {
if ty.to_string().contains("Flags") {
let cases = values.iter().map(|value| {
let name = value.to_string();
quote! { (#ty::#value.0, #name) }
});
quote! {
impl fmt::Debug for #ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const KNOWN: &[(Flags, &str)] = &[#(#cases),*];
debug_flags(f, KNOWN, self.0)
}
}
} else {
let cases = values.iter().map(|value| {
let name = value.to_string();
quote! { Self::#value => Some(#name), }
});
quote! {
impl fmt::Display for #ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
#(#cases)*
_ => None,
};
if let Some(x) = name {
f.write_str(x)
} else {
write!(f, "{}", self.0)
}
}
} else {
let cases = values.iter().map(|value| {
let name = value.to_string();
quote! { Self::#value => Some(#name), }
});
quote! {
impl fmt::Debug for #ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
#(#cases)*
_ => None,
};
if let Some(x) = name {
f.write_str(x)
} else {
self.0.fmt(f)
}
}
}
}
});
}
});
quote! {
fn display_flags(f: &mut fmt::Formatter, known: &[(Flags, &'static str)], value: Flags) -> fmt::Result {
pub(crate) fn debug_flags(f: &mut fmt::Formatter, known: &[(Flags, &'static str)], value: Flags) -> fmt::Result {
let mut first = true;
let mut accum = value;
for (bit, name) in known {
@ -2227,7 +2225,15 @@ pub fn write_source_code(path: &Path) {
let root_names = root_struct_names(&definitions);
let definition_code: Vec<_> = definitions
.into_iter()
.filter_map(|def| generate_definition(def, &union_types, &root_names, &mut bitflags_cache))
.filter_map(|def| {
generate_definition(
def,
&union_types,
&root_names,
&mut bitflags_cache,
&mut const_values,
)
})
.collect();
let feature_code: Vec<_> = features
@ -2237,7 +2243,7 @@ pub fn write_source_code(path: &Path) {
let feature_extensions_code =
generate_feature_extension(&spec2, &mut const_cache, &mut const_values);
let const_displays = generate_const_displays(&const_values);
let const_debugs = generate_const_debugs(&const_values);
let mut file = File::create("../ash/src/vk.rs").expect("vk");
let bitflags_macro = vk_bitflags_wrapped_macro();
@ -2284,7 +2290,7 @@ pub fn write_source_code(path: &Path) {
#(#constants_code)*
#(#extension_code)*
#feature_extensions_code
#const_displays
#const_debugs
#(#aliases)*
};
write!(&mut file, "{}", source_code).expect("Unable to write to file");