const_format/
formatting.rs

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(false).set_decimal();
114    pub const __HEX: Self = Self::NEW.set_alternate(false).set_hexadecimal();
115    pub const __LOWHEX: Self = Self::NEW.set_alternate(false).set_lower_hexadecimal();
116    pub const __BIN: Self = Self::NEW.set_alternate(false).set_binary();
117
118    pub const __A_REG: Self = Self::NEW.set_alternate(true).set_decimal();
119    pub const __A_HEX: Self = Self::NEW.set_alternate(true).set_hexadecimal();
120    pub const __A_LOWHEX: Self = Self::NEW.set_alternate(true).set_lower_hexadecimal();
121    pub const __A_BIN: Self = Self::NEW.set_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};