Skip to content

Commit d91b98f

Browse files
committed
impl Debug for BitFlags
The internal `BitFlagsFmt` trait has been removed and replaced with a `bitflags_type_name()` method.
1 parent 0728ac1 commit d91b98f

File tree

3 files changed

+68
-39
lines changed

3 files changed

+68
-39
lines changed

enumflags/src/lib.rs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use core::iter::FromIterator;
5252
mod details {
5353
use core::ops::{BitAnd, BitOr, BitXor, Not};
5454
use core::cmp::PartialOrd;
55+
use core::fmt::{self, Debug};
5556

5657
pub trait BitFlagNum
5758
: Default
@@ -69,21 +70,56 @@ mod details {
6970
impl BitFlagNum for u32 {}
7071
impl BitFlagNum for u64 {}
7172
impl BitFlagNum for usize {}
72-
}
7373

74-
use details::BitFlagNum;
74+
// Format an iterator of flags into "A | B | etc"
75+
pub(crate) struct FlagFormatter<I>(pub I);
76+
77+
impl<T: Debug, I: Clone + Iterator<Item=T>> Debug for FlagFormatter<I> {
78+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
79+
let mut iter = self.0.clone();
80+
if let Some(val) = iter.next() {
81+
write!(fmt, "{:?}", val)?
82+
}
83+
for val in iter {
84+
write!(fmt, " | {:?}", val)?
85+
}
86+
Ok(())
87+
}
88+
}
7589

76-
/// A trait automatically implemented by `derive(EnumFlags)` on `T` to enable debug printing of
77-
/// `BitFlags<T>`. This is necessary because the names of the variants are needed.
78-
#[doc(hidden)]
79-
pub trait BitFlagsFmt
80-
where
81-
Self: RawBitFlags,
82-
{
83-
/// The implementation of Debug redirects here.
84-
fn fmt(flags: BitFlags<Self>, f: &mut fmt::Formatter) -> fmt::Result;
90+
// A formatter that obeys format arguments but falls back to binary when
91+
// no explicit format is requested. Supports {:08?}, {:08x?}, etc.
92+
pub(crate) struct DebugBinaryFormatter<'a, F>(pub &'a F);
93+
94+
impl<'a, F: fmt::Debug + fmt::Binary + 'a> fmt::Debug for DebugBinaryFormatter<'a, F> {
95+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
96+
// Check if {:x?} or {:X?} was used; this is determined via the
97+
// discriminator of core::fmt::FlagV1::{DebugLowerHex, DebugUpperHex},
98+
// which is not an accessible type: https://github.com/rust-lang/rust/blob/d65e272a9fe3e61aa5f229c5358e35a909435575/src/libcore/fmt/mod.rs#L306
99+
// See also: https://github.com/rust-lang/rfcs/pull/2226
100+
#[allow(deprecated)]
101+
let format_hex = fmt.flags() & (3 << 4) != 0;
102+
103+
if format_hex {
104+
// fmt::Debug machinery handles everything properly
105+
if !fmt.alternate() {
106+
write!(fmt, "0x")?
107+
}
108+
fmt::Debug::fmt(self.0, fmt)
109+
} else {
110+
// Fall back to binary otheriwse but retain all other arguments
111+
// such as padding, alignment, etc.
112+
if !fmt.alternate() {
113+
write!(fmt, "0b")?
114+
}
115+
fmt::Binary::fmt(self.0, fmt)
116+
}
117+
}
118+
}
85119
}
86120

121+
use details::BitFlagNum;
122+
87123
/// A trait automatically implemented by `derive(EnumFlags)` to make the enum a valid type parameter
88124
/// for BitFlags.
89125
#[doc(hidden)]
@@ -99,6 +135,13 @@ pub trait RawBitFlags: Copy + Clone + 'static {
99135

100136
/// Return a slice that contains each variant exactly one.
101137
fn flag_list() -> &'static [Self];
138+
139+
/// Return the name of the type for debug formatting purposes.
140+
///
141+
/// This is typically `BitFlags<EnumName>`
142+
fn bitflags_type_name() -> &'static str {
143+
"BitFlags"
144+
}
102145
}
103146

104147
/// Represents a set of flags of some type `T`.
@@ -111,10 +154,17 @@ pub struct BitFlags<T: RawBitFlags> {
111154

112155
impl<T> fmt::Debug for BitFlags<T>
113156
where
114-
T: RawBitFlags + BitFlagsFmt,
157+
T: RawBitFlags + fmt::Debug,
158+
T::Type: fmt::Binary + fmt::Debug,
115159
{
116160
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
117-
T::fmt(self.clone(), fmt)
161+
let mut debug = fmt.debug_tuple(T::bitflags_type_name());
162+
debug.field(&details::DebugBinaryFormatter(&self.bits()));
163+
if !self.is_empty() {
164+
let iter = T::flag_list().iter().filter(|&&flag| self.contains(flag));
165+
debug.field(&details::FlagFormatter(iter));
166+
}
167+
debug.finish()
118168
}
119169
}
120170

enumflags_derive/src/lib.rs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ fn extract_repr(attrs: &[syn::Attribute]) -> Option<syn::Ident> {
7979
fn gen_enumflags(ident: &Ident, item: &DeriveInput, data: &DataEnum) -> TokenStream {
8080
let span = Span::call_site();
8181
let variants = data.variants.iter().map(|v| &v.ident);
82-
let variants_names = variants.clone().map(ToString::to_string);
8382
let flag_values: Vec<_> = data.variants.iter()
8483
.map(|v| v.discriminant.as_ref().map(|d| fold_expr(&d.1)).expect("No discriminant")).collect();
8584
let variants_len = flag_values.len();
@@ -158,30 +157,6 @@ fn gen_enumflags(ident: &Ident, item: &DeriveInput, data: &DataEnum) -> TokenStr
158157
}
159158
}
160159

161-
impl ::enumflags2::BitFlagsFmt for #ident {
162-
fn fmt(flags: ::enumflags2::BitFlags<#ident>,
163-
fmt: &mut #std_path::fmt::Formatter)
164-
-> #std_path::fmt::Result {
165-
use ::enumflags2::RawBitFlags;
166-
use #std_path::iter::Iterator;
167-
const VARIANT_NAMES: [&'static str; #variants_len] = [#(#variants_names, )*];
168-
let mut vals =
169-
VARIANTS.iter().zip(VARIANT_NAMES.iter())
170-
.filter(|&(&val, _)| val as #ty & flags.bits() != 0)
171-
.map(|(_, name)| name);
172-
write!(fmt, "BitFlags<{}>(0b{:b}",
173-
stringify!(#ident),
174-
flags.bits())?;
175-
if let #std_path::option::Option::Some(val) = vals.next() {
176-
write!(fmt, ", {}", val)?;
177-
for val in vals {
178-
write!(fmt, " | {}", val)?;
179-
}
180-
}
181-
write!(fmt, ")")
182-
}
183-
}
184-
185160
impl ::enumflags2::RawBitFlags for #ident {
186161
type Type = #ty;
187162

@@ -196,6 +171,10 @@ fn gen_enumflags(ident: &Ident, item: &DeriveInput, data: &DataEnum) -> TokenStr
196171
fn flag_list() -> &'static [Self] {
197172
&VARIANTS
198173
}
174+
175+
fn bitflags_type_name() -> &'static str {
176+
concat!("BitFlags<", stringify!(#ident), ">")
177+
}
199178
}
200179
}
201180
}

example/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ extern crate enumflags2;
22
#[macro_use]
33
extern crate enumflags2_derive;
44
use enumflags2::*;
5-
#[derive(EnumFlags, Copy, Clone)]
5+
#[derive(EnumFlags, Copy, Clone, Debug)]
66
#[repr(u8)]
77
pub enum Test {
88
A = 0b0001,

0 commit comments

Comments
 (0)