const_panic/
panic_val.rs

1use crate::{
2    fmt::{FmtArg, FmtKind, NumberFmt},
3    utils::{string_cap, Packed, PreFmtString, RangedBytes, Sign, TailShortString, WasTruncated},
4};
5
6#[cfg(feature = "non_basic")]
7use crate::{
8    array_string::TinyString,
9    fmt::{IsLast, ShortString},
10};
11
12/// An opaque enum of the values that this crate knows how to format,
13/// along with some formatting metadata.
14///
15/// This has constructor functions to make a `PanicVal` from:
16/// - `bool`
17/// - Integers
18/// - `&str`
19/// - Arrays/Slices of primitives (with the "non_basic" feature, enabled by default)
20/// - [`ShortString`](crate::fmt::ShortString)
21/// (with the "non_basic" feature, enabled by default)
22///
23#[derive(Copy, Clone)]
24pub struct PanicVal<'a> {
25    pub(crate) var: PanicVariant<'a>,
26}
27
28#[derive(Copy, Clone)]
29pub(crate) enum PanicVariant<'a> {
30    Str(StrFmt, Packed<&'a str>),
31    #[cfg(feature = "non_basic")]
32    ShortString(StrFmt, TinyString<{ string_cap::TINY }>),
33    PreFmt(PreFmtString),
34    Int(IntVal),
35    #[cfg(feature = "non_basic")]
36    Slice(crate::slice_stuff::Slice<'a>),
37}
38
39pub(crate) enum PanicClass<'a> {
40    PreFmt(RangedBytes<&'a [u8]>),
41    Int(IntVal),
42    #[cfg(feature = "non_basic")]
43    Slice(crate::slice_stuff::Slice<'a>),
44}
45
46#[derive(Copy, Clone)]
47pub(crate) struct StrFmt {
48    pub(crate) leftpad: u8,
49    pub(crate) rightpad: u8,
50    pub(crate) fmt_kind: FmtKind,
51}
52
53impl StrFmt {
54    const DISPLAY: Self = Self {
55        leftpad: 0,
56        rightpad: 0,
57        fmt_kind: FmtKind::Display,
58    };
59
60    pub const fn new(fmtarg: FmtArg) -> Self {
61        Self {
62            leftpad: 0,
63            rightpad: 0,
64            fmt_kind: fmtarg.fmt_kind,
65        }
66    }
67}
68
69impl<'a> PanicVal<'a> {
70    /// A `PanicVal` that formats to nothing.
71    pub const EMPTY: Self = PanicVal::write_str("");
72
73    /// How many spaces are printed before this
74    pub const fn leftpad(&self) -> u8 {
75        use self::PanicVariant as PV;
76
77        match self.var {
78            PV::Str(strfmt, ..) => strfmt.leftpad,
79            #[cfg(feature = "non_basic")]
80            PV::ShortString(strfmt, ..) => strfmt.leftpad,
81            _ => 0,
82        }
83    }
84    /// How many spaces are printed after this
85    pub const fn rightpad(&self) -> u8 {
86        use self::PanicVariant as PV;
87
88        match self.var {
89            PV::Str(strfmt, ..) => strfmt.rightpad,
90            #[cfg(feature = "non_basic")]
91            PV::ShortString(strfmt, ..) => strfmt.rightpad,
92            _ => 0,
93        }
94    }
95}
96
97macro_rules! mutate_strfmt {
98    ($self:ident, |$strfmt:ident| $mutator:expr) => {
99        match $self.var {
100            PanicVariant::Str(mut $strfmt, str) => {
101                $mutator;
102                PanicVal {
103                    var: PanicVariant::Str($strfmt, str),
104                }
105            }
106            #[cfg(feature = "non_basic")]
107            PanicVariant::ShortString(mut $strfmt, str) => {
108                $mutator;
109                PanicVal {
110                    var: PanicVariant::ShortString($strfmt, str),
111                }
112            }
113            var => PanicVal { var },
114        }
115    };
116}
117
118impl<'a> PanicVal<'a> {
119    /// Sets the amount of spaces printed before this to `fmtarg.indentation`.
120    ///
121    /// Note that only strings can be padded.
122    pub const fn with_leftpad(self, fmtarg: FmtArg) -> Self {
123        mutate_strfmt! {self, |strfmt| strfmt.leftpad = fmtarg.indentation}
124    }
125
126    /// Sets the amount of spaces printed after this to `fmtarg.indentation`.
127    ///
128    /// Note that only strings can be padded.
129    pub const fn with_rightpad(self, fmtarg: FmtArg) -> Self {
130        mutate_strfmt! {self, |strfmt| strfmt.rightpad = fmtarg.indentation}
131    }
132
133    /// Constructs a PanicVal which outputs the contents of `string` verbatim.
134    ///
135    /// Equivalent to `PanicVal::from_str(string, FmtArg::DISPLAY)`
136    pub const fn write_str(string: &'a str) -> Self {
137        PanicVal {
138            var: PanicVariant::Str(StrFmt::DISPLAY, Packed(string)),
139        }
140    }
141
142    /// Constructs a PanicVal from a [`ShortString`], which outputs the string verbatim.
143    #[cfg(feature = "non_basic")]
144    pub const fn write_short_str(string: ShortString) -> Self {
145        Self {
146            var: PanicVariant::ShortString(StrFmt::DISPLAY, string.to_compact()),
147        }
148    }
149
150    /// Constructs a `PanicVal` usable as a separator between fields or elements.
151    ///
152    /// This is sensitive to the [`fmtarg.is_alternate`] flag,
153    /// for more details on that you can look at the docs for
154    /// [`Separator::to_panicval`](crate::fmt::Separator#method.to_panicval)
155    ///
156    /// # Panics
157    ///
158    /// This panics if `string.len()` is greater than 12.
159    ///
160    /// [`fmtarg.is_alternate`]: crate::FmtArg#structfield.is_alternate
161    #[cfg(feature = "non_basic")]
162    pub const fn from_element_separator(
163        separator: &str,
164        is_last_field: IsLast,
165        fmtarg: FmtArg,
166    ) -> Self {
167        let (concat, rightpad) = match (is_last_field, fmtarg.is_alternate) {
168            (IsLast::No, false) => (ShortString::concat(&[separator, " "]), 0),
169            (IsLast::Yes, false) => (ShortString::new(""), 0),
170            (IsLast::No, true) => (ShortString::concat(&[separator, "\n"]), fmtarg.indentation),
171            (IsLast::Yes, true) => (ShortString::concat(&[separator, "\n"]), 0),
172        };
173
174        let strfmt = StrFmt {
175            leftpad: 0,
176            rightpad,
177            fmt_kind: FmtKind::Display,
178        };
179        Self {
180            var: PanicVariant::ShortString(strfmt, concat.to_compact()),
181        }
182    }
183
184    #[inline(always)]
185    pub(crate) const fn __new(var: PanicVariant<'a>) -> Self {
186        Self { var }
187    }
188
189    pub(crate) const fn to_class(&self) -> (StrFmt, PanicClass<'_>) {
190        match &self.var {
191            &PanicVariant::Str(strfmt, Packed(str)) => {
192                let ranged = RangedBytes {
193                    start: 0,
194                    end: str.len(),
195                    bytes: str.as_bytes(),
196                };
197
198                (strfmt, PanicClass::PreFmt(ranged))
199            }
200            #[cfg(feature = "non_basic")]
201            PanicVariant::ShortString(strfmt, str) => (*strfmt, PanicClass::PreFmt(str.ranged())),
202            PanicVariant::PreFmt(str) => (StrFmt::DISPLAY, PanicClass::PreFmt(str.ranged())),
203            PanicVariant::Int(int) => (StrFmt::DISPLAY, PanicClass::Int(*int)),
204            #[cfg(feature = "non_basic")]
205            PanicVariant::Slice(slice) => (
206                StrFmt::new(slice.fmtarg.unpack()),
207                PanicClass::Slice(*slice),
208            ),
209        }
210    }
211
212    pub(crate) const fn to_class_truncated(
213        &self,
214        mut truncate_to: usize,
215    ) -> (StrFmt, PanicClass<'_>, WasTruncated) {
216        let (mut strfmt, class) = self.to_class();
217
218        if strfmt.leftpad as usize > truncate_to {
219            return (
220                StrFmt {
221                    leftpad: strfmt.leftpad - truncate_to as u8,
222                    rightpad: 0,
223                    fmt_kind: FmtKind::Display,
224                },
225                PanicClass::PreFmt(RangedBytes::EMPTY),
226                WasTruncated::Yes(0),
227            );
228        } else {
229            truncate_to -= strfmt.leftpad as usize;
230        };
231
232        let was_trunc: WasTruncated;
233        let orig_len: usize;
234
235        match class {
236            PanicClass::PreFmt(str) => {
237                was_trunc = if let PanicVariant::PreFmt(pfmt) = self.var {
238                    if pfmt.len() <= truncate_to {
239                        WasTruncated::No
240                    } else {
241                        WasTruncated::Yes(0)
242                    }
243                } else {
244                    if let FmtKind::Display = strfmt.fmt_kind {
245                        crate::utils::truncated_str_len(str, truncate_to)
246                    } else {
247                        crate::utils::truncated_debug_str_len(str, truncate_to)
248                    }
249                };
250                orig_len = str.len();
251            }
252            PanicClass::Int(int) => {
253                strfmt.fmt_kind = FmtKind::Display;
254                was_trunc = if int.len() <= truncate_to {
255                    WasTruncated::No
256                } else {
257                    WasTruncated::Yes(0)
258                };
259                orig_len = int.len();
260            }
261            #[cfg(feature = "non_basic")]
262            PanicClass::Slice(_) => {
263                was_trunc = WasTruncated::No;
264                orig_len = 0;
265            }
266        }
267        truncate_to -= was_trunc.get_length(orig_len);
268
269        strfmt.rightpad = crate::utils::min_usize(strfmt.rightpad as usize, truncate_to) as u8;
270
271        (strfmt, class, was_trunc)
272    }
273}
274
275#[derive(Copy, Clone)]
276pub(crate) struct IntVal {
277    sign: Sign,
278    number_fmt: NumberFmt,
279    is_alternate: bool,
280    // the size of the integer in bits
281    bits: u8,
282    // the length of the integer in bytes, once written.
283    len: u8,
284
285    value: Packed<u128>,
286}
287
288impl IntVal {
289    pub(crate) const fn from_u128(n: u128, bits: u8, f: FmtArg) -> PanicVal<'static> {
290        Self::new(Sign::Positive, n, bits, f)
291    }
292    pub(crate) const fn from_i128(n: i128, bits: u8, f: FmtArg) -> PanicVal<'static> {
293        let is_neg = if n < 0 {
294            Sign::Negative
295        } else {
296            Sign::Positive
297        };
298        Self::new(is_neg, n.unsigned_abs(), bits, f)
299    }
300
301    const fn new(sign: Sign, n: u128, bits: u8, fmtarg: FmtArg) -> PanicVal<'static> {
302        use crate::int_formatting::compute_len;
303
304        let len = compute_len(sign, n, bits, fmtarg);
305
306        let this = IntVal {
307            sign,
308            number_fmt: fmtarg.number_fmt,
309            is_alternate: fmtarg.is_alternate,
310            bits,
311            len,
312            value: Packed(n),
313        };
314
315        let var = if len as usize <= string_cap::PREFMT {
316            PanicVariant::PreFmt(this.fmt::<{ string_cap::PREFMT }>())
317        } else {
318            PanicVariant::Int(this)
319        };
320        PanicVal { var }
321    }
322
323    pub(crate) const fn fmt<const N: usize>(self) -> TailShortString<N> {
324        use crate::int_formatting::{fmt_binary, fmt_decimal, fmt_hexadecimal};
325
326        let IntVal {
327            sign,
328            number_fmt,
329            is_alternate,
330            len: _,
331            bits,
332            value: Packed(n),
333        } = self;
334
335        match number_fmt {
336            NumberFmt::Decimal => fmt_decimal::<N>(sign, n),
337            NumberFmt::Binary => {
338                let masked = apply_mask(sign, n, bits);
339                fmt_binary::<N>(masked, is_alternate)
340            }
341            NumberFmt::Hexadecimal => {
342                let masked = apply_mask(sign, n, bits);
343                fmt_hexadecimal::<N>(masked, is_alternate)
344            }
345        }
346    }
347
348    pub(crate) const fn len(&self) -> usize {
349        self.len as usize
350    }
351}
352
353const fn apply_mask(sign: Sign, n: u128, bits: u8) -> u128 {
354    if let Sign::Negative = sign {
355        let mask: u128 = if bits == 128 { !0 } else { (1 << bits) - 1 };
356
357        (n as i128).wrapping_neg() as u128 & mask
358    } else {
359        n
360    }
361}
362
363impl crate::PanicFmt for PanicVal<'_> {
364    type This = Self;
365    type Kind = crate::fmt::IsCustomType;
366
367    const PV_COUNT: usize = 1;
368}
369
370impl<'a> PanicVal<'a> {
371    /// Wraps this `PanicVal` in a single-element array.
372    pub const fn to_panicvals(&self, _: FmtArg) -> [PanicVal<'a>; 1] {
373        [*self]
374    }
375    /// Returns a copy of this `PanicVal`.
376    pub const fn to_panicval(&self, _: FmtArg) -> PanicVal<'a> {
377        *self
378    }
379}