1use crate::{BitFlag, BitFlags};
2use core::fmt::{self, Binary, Debug};
3
4impl<T> fmt::Debug for BitFlags<T>
5where
6 T: BitFlag + fmt::Debug,
7{
8 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
9 let name = T::BITFLAGS_TYPE_NAME;
10 let bits = DebugBinaryFormatter(&self.val);
11 let iter = if !self.is_empty() {
12 Some(FlagFormatter(self.iter()))
13 } else {
14 None
15 };
16
17 if !fmt.alternate() {
18 // Concise tuple formatting is a better default
19 let mut debug = fmt.debug_tuple(name);
20 debug.field(&bits);
21 if let Some(iter) = iter {
22 debug.field(&iter);
23 }
24 debug.finish()
25 } else {
26 // Pretty-printed tuples are ugly and hard to read, so use struct format
27 let mut debug = fmt.debug_struct(name);
28 debug.field("bits", &bits);
29 if let Some(iter) = iter {
30 debug.field("flags", &iter);
31 }
32 debug.finish()
33 }
34 }
35}
36
37impl<T> fmt::Display for BitFlags<T>
38where
39 T: BitFlag + fmt::Debug,
40{
41 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
42 fmt::Debug::fmt(&FlagFormatter(self.iter()), f:fmt)
43 }
44}
45
46impl<T> fmt::Binary for BitFlags<T>
47where
48 T: BitFlag,
49 T::Numeric: fmt::Binary,
50{
51 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
52 fmt::Binary::fmt(&self.bits(), f:fmt)
53 }
54}
55
56impl<T> fmt::Octal for BitFlags<T>
57where
58 T: BitFlag,
59 T::Numeric: fmt::Octal,
60{
61 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
62 fmt::Octal::fmt(&self.bits(), f:fmt)
63 }
64}
65
66impl<T> fmt::LowerHex for BitFlags<T>
67where
68 T: BitFlag,
69 T::Numeric: fmt::LowerHex,
70{
71 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
72 fmt::LowerHex::fmt(&self.bits(), f:fmt)
73 }
74}
75
76impl<T> fmt::UpperHex for BitFlags<T>
77where
78 T: BitFlag,
79 T::Numeric: fmt::UpperHex,
80{
81 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
82 fmt::UpperHex::fmt(&self.bits(), f:fmt)
83 }
84}
85
86// Format an iterator of flags into "A | B | etc"
87struct FlagFormatter<I>(I);
88
89impl<T: Debug, I: Clone + Iterator<Item = T>> Debug for FlagFormatter<I> {
90 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
91 let mut iter: I = self.0.clone();
92 if let Some(val: T) = iter.next() {
93 Debug::fmt(&val, f:fmt)?;
94 for val: T in iter {
95 fmt.write_str(data:" | ")?;
96 Debug::fmt(&val, f:fmt)?;
97 }
98 Ok(())
99 } else {
100 fmt.write_str(data:"<empty>")
101 }
102 }
103}
104
105// A formatter that obeys format arguments but falls back to binary when
106// no explicit format is requested. Supports {:08?}, {:08x?}, etc.
107struct DebugBinaryFormatter<'a, F>(&'a F);
108
109impl<'a, F: Debug + Binary + 'a> Debug for DebugBinaryFormatter<'a, F> {
110 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
111 // Check if {:x?} or {:X?} was used; this is determined via the
112 // discriminator of core::fmt::FlagV1::{DebugLowerHex, DebugUpperHex},
113 // which is not an accessible type: https://github.com/rust-lang/rust/blob/d65e272a9fe3e61aa5f229c5358e35a909435575/src/libcore/fmt/mod.rs#L306
114 // See also: https://github.com/rust-lang/rfcs/pull/2226
115 #[allow(deprecated)]
116 let format_hex: u32 = fmt.flags() >> 4;
117 let width: usize = fmt.width().unwrap_or(default:0);
118
119 if format_hex & 1 != 0 {
120 // FlagV1::DebugLowerHex
121 write!(fmt, "{:#0width$x?}", &self.0, width = width)
122 } else if format_hex & 2 != 0 {
123 // FlagV1::DebugUpperHex
124 write!(fmt, "{:#0width$X?}", &self.0, width = width)
125 } else {
126 // Fall back to binary otheriwse
127 write!(fmt, "{:#0width$b}", &self.0, width = width)
128 }
129 }
130}
131
132#[test]
133fn flag_formatter() {
134 use core::iter;
135
136 macro_rules! assert_fmt {
137 ($fmt:expr, $expr:expr, $expected:expr) => {
138 assert_eq!(format!($fmt, FlagFormatter($expr)), $expected)
139 };
140 }
141
142 assert_fmt!("{:?}", iter::empty::<u8>(), "<empty>");
143 assert_fmt!("{:?}", iter::once(1), "1");
144 assert_fmt!("{:?}", [1, 2].iter(), "1 | 2");
145 assert_fmt!("{:?}", [1, 2, 10].iter(), "1 | 2 | 10");
146 assert_fmt!("{:02x?}", [1, 2, 10].iter(), "01 | 02 | 0a");
147 assert_fmt!("{:#04X?}", [1, 2, 10].iter(), "0x01 | 0x02 | 0x0A");
148}
149
150#[test]
151fn debug_binary_formatter() {
152 macro_rules! assert_fmt {
153 ($fmt:expr, $expr:expr, $expected:expr) => {
154 assert_eq!(format!($fmt, DebugBinaryFormatter(&$expr)), $expected)
155 };
156 }
157
158 assert_fmt!("{:?}", 10, "0b1010");
159 assert_fmt!("{:#?}", 10, "0b1010");
160 assert_fmt!("{:010?}", 10, "0b00001010");
161 assert_fmt!("{:010x?}", 10, "0x0000000a");
162 assert_fmt!("{:#010X?}", 10, "0x0000000A");
163}
164