1#[doc(hidden)]
2#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3pub enum Formatting {
4 Debug,
5 Display,
6}
7
8impl Formatting {
9 /// Whether the current variant is `Display`
10 #[inline(always)]
11 pub const fn is_display(self) -> bool {
12 matches!(self, Formatting::Display)
13 }
14}
15
16/// How numbers are formatted in debug formatters.
17///
18/// Hexadecimal or binary formatting in the formatting string from this crate imply
19/// debug formatting.
20///
21///
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub enum NumberFormatting {
24 /// Formats numbers as decimal
25 Decimal,
26 /// Formats numbers as hexadecimal
27 Hexadecimal,
28 /// Formats numbers as binary
29 Binary,
30}
31
32#[doc(hidden)]
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34#[repr(u8)]
35pub enum HexFormatting {
36 // micro-optimization
37 // by having these values for the discriminants,
38 // going from a number greater than 9 and smaller than 16
39 // to their ascii digits is as simple as `number + (hex_fmt as u8)`
40 Upper = b'A' - 10,
41 Lower = b'a' - 10,
42}
43
44impl NumberFormatting {
45 #[cfg(test)]
46 #[cfg(feature = "fmt")]
47 pub(crate) const ALL: &'static [Self; 3] = &[
48 NumberFormatting::Decimal,
49 NumberFormatting::Hexadecimal,
50 NumberFormatting::Binary,
51 ];
52}
53
54////////////////////////////////////////////////////////////////////////////////
55
56/// This type bundles configuration for how to format data into strings, including.
57///
58/// # Number formatting
59///
60/// How numbers are formatted in debug formatters,
61/// It can be accessed with the `num_fmt` method, and set with the `set_num_fmt` method.
62///
63/// Each type of number formatting corresponds to a [`NumberFormatting`] variant:
64///
65/// - `NumberFormatting::Decimal` (eg: `formatc!("{:?}", FOO)`):
66/// formats numbers as decimal.
67///
68/// - `NumberFormatting::Hexadecimal` (eg: `formatc!("{:x}", FOO)`):
69/// formats numbers as hexadecimal.
70///
71/// - `NumberFormatting::Binary` (eg: `formatc!("{:b}", FOO)`):
72/// formats numbers as binary.
73///
74/// Hexadecimal or binary formatting in the formatting string from this crate imply
75/// debug formatting,
76/// and can be used to for example print an array of binary numbers.
77///
78/// Note: Lowercase hexadecimal formatting requires calling the
79/// [`set_lower_hexadecimal`](#method.set_lower_hexadecimal) method.
80///
81/// # Alternate flag
82///
83/// A flag that types can use to be formatted differently when it's enabled,
84/// checked with the `.is_alternate()` method.
85///
86/// The default behavior when it is enabled is this:
87///
88/// - The Debug formater (eg: `formatc!("{:#?}", FOO)`):
89/// pretty print structs and enums.
90///
91/// - The hexadecimal formater (eg: `formatc!("{:#x}", FOO)`):
92/// prefixes numbers with `0x`.
93///
94/// - The binary formater (eg: `formatc!("{:#b}", FOO)`):
95/// prefixes numbers with `0b`.`
96///
97/// [`Formatter`]: ./struct.Formatter.html
98///
99#[must_use]
100#[derive(Debug, Copy, Clone)]
101pub struct FormattingFlags {
102 num_fmt: NumberFormatting,
103 // Whether the `NumberFormatting` prints hexadecimal digits in lowercase
104 // (e.g: 0xf00, 0xF00)
105 //
106 // move this in 0.3.0 to `NumberFormatting`.
107 hex_fmt: HexFormatting,
108 is_alternate: bool,
109}
110
111#[doc(hidden)]
112impl FormattingFlags {
113 pub const __REG: Self = Self::NEW.set_alternate(is_alternate:false).set_decimal();
114 pub const __HEX: Self = Self::NEW.set_alternate(is_alternate:false).set_hexadecimal();
115 pub const __LOWHEX: Self = Self::NEW.set_alternate(is_alternate:false).set_lower_hexadecimal();
116 pub const __BIN: Self = Self::NEW.set_alternate(is_alternate:false).set_binary();
117
118 pub const __A_REG: Self = Self::NEW.set_alternate(is_alternate:true).set_decimal();
119 pub const __A_HEX: Self = Self::NEW.set_alternate(is_alternate:true).set_hexadecimal();
120 pub const __A_LOWHEX: Self = Self::NEW.set_alternate(is_alternate:true).set_lower_hexadecimal();
121 pub const __A_BIN: Self = Self::NEW.set_alternate(is_alternate:true).set_binary();
122}
123impl FormattingFlags {
124 #[doc(hidden)]
125 pub const DEFAULT: Self = Self {
126 num_fmt: NumberFormatting::Decimal,
127 hex_fmt: HexFormatting::Upper,
128 is_alternate: false,
129 };
130
131 /// Constructs a `FormattingFlags` with these values:
132 ///
133 /// - number formatting: NumberFormatting::Decimal
134 ///
135 /// - is alternate: false
136 ///
137 pub const NEW: Self = Self {
138 num_fmt: NumberFormatting::Decimal,
139 hex_fmt: HexFormatting::Upper,
140 is_alternate: false,
141 };
142
143 /// Constructs a `FormattingFlags` with these values:
144 ///
145 /// - number formatting: NumberFormatting::Decimal
146 ///
147 /// - is alternate: false
148 ///
149 #[inline]
150 pub const fn new() -> Self {
151 Self::NEW
152 }
153
154 /// Sets the integer formatting,
155 ///
156 /// This usually doesn't affect the outputted text in display formatting.
157 #[inline]
158 pub const fn set_num_fmt(mut self, num_fmt: NumberFormatting) -> Self {
159 self.num_fmt = num_fmt;
160 self
161 }
162
163 /// Sets the number formatting to `NumberFormatting::Decimal`.
164 ///
165 /// This means that numbers are written as decimal.
166 #[inline]
167 pub const fn set_decimal(mut self) -> Self {
168 self.num_fmt = NumberFormatting::Decimal;
169 self
170 }
171
172 /// Sets the number formatting to `NumberFormatting::Hexadecimal`.
173 ///
174 /// This means that numbers are written as uppercase hexadecimal.
175 #[inline]
176 pub const fn set_hexadecimal(mut self) -> Self {
177 self.num_fmt = NumberFormatting::Hexadecimal;
178 self.hex_fmt = HexFormatting::Upper;
179 self
180 }
181
182 /// Sets the number formatting to `NumberFormatting::Hexadecimal`,
183 /// and uses lowercase for alphabetic hexadecimal digits.
184 ///
185 /// This means that numbers are written as lowercase hexadecimal.
186 #[inline]
187 pub const fn set_lower_hexadecimal(mut self) -> Self {
188 self.num_fmt = NumberFormatting::Hexadecimal;
189 self.hex_fmt = HexFormatting::Lower;
190 self
191 }
192
193 /// Sets the number formatting to `NumberFormatting::Binary`.
194 ///
195 /// This means that numbers are written as binary.
196 #[inline]
197 pub const fn set_binary(mut self) -> Self {
198 self.num_fmt = NumberFormatting::Binary;
199 self
200 }
201
202 /// Sets whether the formatting flag is enabled.
203 #[inline]
204 pub const fn set_alternate(mut self, is_alternate: bool) -> Self {
205 self.is_alternate = is_alternate;
206 self
207 }
208
209 /// Gets the current `NumberFormatting`.
210 #[inline]
211 pub const fn num_fmt(self) -> NumberFormatting {
212 self.num_fmt
213 }
214
215 /// Gets whether the alternate flag is enabled
216 #[inline]
217 pub const fn is_alternate(self) -> bool {
218 self.is_alternate
219 }
220
221 pub(crate) const fn hex_fmt(self) -> HexFormatting {
222 self.hex_fmt
223 }
224}
225
226////////////////////////////////////////////////////////////////////////////////
227
228#[doc(hidden)]
229/// For writing into an array from the start
230pub struct LenAndArray<T: ?Sized> {
231 /// The amount of elements written in `array`
232 pub len: usize,
233 pub array: T,
234}
235
236#[doc(hidden)]
237/// For writing into an array from the end
238pub struct StartAndArray<T: ?Sized> {
239 /// The first element in `array`
240 pub start: usize,
241 pub array: T,
242}
243
244////////////////////////////////////////////////////////////////////////////////
245
246#[doc(hidden)]
247pub struct ForEscaping {
248 pub is_escaped: u128,
249 pub is_backslash_escaped: u128,
250 pub escape_char: [u8; 16],
251}
252
253impl ForEscaping {
254 /// Gets the backslash escape for a character that is kwown to be escaped with a backslash.
255 #[inline(always)]
256 pub const fn get_backslash_escape(b: u8) -> u8 {
257 FOR_ESCAPING.escape_char[(b & 0b1111) as usize]
258 }
259}
260
261#[doc(hidden)]
262/// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F'
263#[inline(always)]
264pub const fn hex_as_ascii(n: u8, hex_fmt: HexFormatting) -> u8 {
265 if n < 10 {
266 n + b'0'
267 } else {
268 n + (hex_fmt as u8)
269 }
270}
271
272#[doc(hidden)]
273// Really clippy? Array indexing can panic you know.
274#[allow(clippy::no_effect)]
275pub const FOR_ESCAPING: &ForEscaping = {
276 let mut is_backslash_escaped = 0;
277
278 let escaped = [
279 (b'\t', b't'),
280 (b'\n', b'n'),
281 (b'\r', b'r'),
282 (b'\'', b'\''),
283 (b'"', b'"'),
284 (b'\\', b'\\'),
285 ];
286
287 // Using the fact that the characters above all have different bit patterns for
288 // the lowest 4 bits.
289 let mut escape_char = [0u8; 16];
290
291 __for_range! {i in 0..escaped.len() =>
292 let (code, escape) = escaped[i];
293 is_backslash_escaped |= 1 << code;
294
295 let ei = (code & 0b1111) as usize;
296 let prev_escape = escape_char[ei] as usize;
297 ["Oh no, some escaped character uses the same 4 lower bits as another"][prev_escape];
298 escape_char[ei] = escape;
299 }
300
301 // Setting all the control characters as being escaped.
302 let is_escaped = is_backslash_escaped | 0xFFFF_FFFF;
303
304 &ForEscaping {
305 escape_char,
306 is_backslash_escaped,
307 is_escaped,
308 }
309};
310