Skip to main content

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    #[allow(clippy::wrong_self_convention)]
190    pub(crate) const fn to_class(&self) -> (StrFmt, PanicClass<'_>) {
191        match &self.var {
192            &PanicVariant::Str(strfmt, Packed(str)) => {
193                let ranged = RangedBytes {
194                    start: 0,
195                    end: str.len(),
196                    bytes: str.as_bytes(),
197                };
198
199                (strfmt, PanicClass::PreFmt(ranged))
200            }
201            #[cfg(feature = "non_basic")]
202            PanicVariant::ShortString(strfmt, str) => (*strfmt, PanicClass::PreFmt(str.ranged())),
203            PanicVariant::PreFmt(str) => (StrFmt::DISPLAY, PanicClass::PreFmt(str.ranged())),
204            PanicVariant::Int(int) => (StrFmt::DISPLAY, PanicClass::Int(*int)),
205            #[cfg(feature = "non_basic")]
206            PanicVariant::Slice(slice) => (
207                StrFmt::new(slice.fmtarg.unpack()),
208                PanicClass::Slice(*slice),
209            ),
210        }
211    }
212
213    #[allow(clippy::wrong_self_convention)]
214    pub(crate) const fn to_class_truncated(
215        &self,
216        mut truncate_to: usize,
217    ) -> (StrFmt, PanicClass<'_>, WasTruncated) {
218        let (mut strfmt, class) = self.to_class();
219
220        if strfmt.leftpad as usize > truncate_to {
221            return (
222                StrFmt {
223                    leftpad: strfmt.leftpad - truncate_to as u8,
224                    rightpad: 0,
225                    fmt_kind: FmtKind::Display,
226                },
227                PanicClass::PreFmt(RangedBytes::EMPTY),
228                WasTruncated::Yes(0),
229            );
230        } else {
231            truncate_to -= strfmt.leftpad as usize;
232        };
233
234        let was_trunc: WasTruncated;
235        let orig_len: usize;
236
237        match class {
238            PanicClass::PreFmt(str) => {
239                was_trunc = if let PanicVariant::PreFmt(pfmt) = self.var {
240                    if pfmt.len() <= truncate_to {
241                        WasTruncated::No
242                    } else {
243                        WasTruncated::Yes(0)
244                    }
245                } else {
246                    if let FmtKind::Display = strfmt.fmt_kind {
247                        crate::utils::truncated_str_len(str, truncate_to)
248                    } else {
249                        crate::utils::truncated_debug_str_len(str, truncate_to)
250                    }
251                };
252                orig_len = str.len();
253            }
254            PanicClass::Int(int) => {
255                strfmt.fmt_kind = FmtKind::Display;
256                was_trunc = if int.len() <= truncate_to {
257                    WasTruncated::No
258                } else {
259                    WasTruncated::Yes(0)
260                };
261                orig_len = int.len();
262            }
263            #[cfg(feature = "non_basic")]
264            PanicClass::Slice(_) => {
265                was_trunc = WasTruncated::No;
266                orig_len = 0;
267            }
268        }
269        truncate_to -= was_trunc.get_length(orig_len);
270
271        strfmt.rightpad = crate::utils::min_usize(strfmt.rightpad as usize, truncate_to) as u8;
272
273        (strfmt, class, was_trunc)
274    }
275}
276
277#[derive(Copy, Clone)]
278pub(crate) struct IntVal {
279    sign: Sign,
280    number_fmt: NumberFmt,
281    is_alternate: bool,
282    // the size of the integer in bits
283    bits: u8,
284    // the length of the integer in bytes, once written.
285    len: u8,
286
287    value: Packed<u128>,
288}
289
290impl IntVal {
291    pub(crate) const fn from_u128(n: u128, bits: u8, f: FmtArg) -> PanicVal<'static> {
292        Self::new_pv(Sign::Positive, n, bits, f)
293    }
294    pub(crate) const fn from_i128(n: i128, bits: u8, f: FmtArg) -> PanicVal<'static> {
295        let is_neg = if n < 0 {
296            Sign::Negative
297        } else {
298            Sign::Positive
299        };
300        Self::new_pv(is_neg, n.unsigned_abs(), bits, f)
301    }
302
303    const fn new_pv(sign: Sign, n: u128, bits: u8, fmtarg: FmtArg) -> PanicVal<'static> {
304        use crate::int_formatting::compute_len;
305
306        let len = compute_len(sign, n, bits, fmtarg);
307
308        let this = IntVal {
309            sign,
310            number_fmt: fmtarg.number_fmt,
311            is_alternate: fmtarg.is_alternate,
312            bits,
313            len,
314            value: Packed(n),
315        };
316
317        let var = if len as usize <= string_cap::PREFMT {
318            PanicVariant::PreFmt(this.fmt::<{ string_cap::PREFMT }>())
319        } else {
320            PanicVariant::Int(this)
321        };
322        PanicVal { var }
323    }
324
325    pub(crate) const fn fmt<const N: usize>(self) -> TailShortString<N> {
326        use crate::int_formatting::{fmt_binary, fmt_decimal, fmt_hexadecimal};
327
328        let IntVal {
329            sign,
330            number_fmt,
331            is_alternate,
332            len: _,
333            bits,
334            value: Packed(n),
335        } = self;
336
337        match number_fmt {
338            NumberFmt::Decimal => fmt_decimal::<N>(sign, n),
339            NumberFmt::Binary => {
340                let masked = apply_mask(sign, n, bits);
341                fmt_binary::<N>(masked, is_alternate)
342            }
343            NumberFmt::Hexadecimal => {
344                let masked = apply_mask(sign, n, bits);
345                fmt_hexadecimal::<N>(masked, is_alternate)
346            }
347        }
348    }
349
350    pub(crate) const fn len(&self) -> usize {
351        self.len as usize
352    }
353}
354
355const fn apply_mask(sign: Sign, n: u128, bits: u8) -> u128 {
356    if let Sign::Negative = sign {
357        let mask: u128 = if bits == 128 { !0 } else { (1 << bits) - 1 };
358
359        (n as i128).wrapping_neg() as u128 & mask
360    } else {
361        n
362    }
363}
364
365impl crate::PanicFmt for PanicVal<'_> {
366    type This = Self;
367    type Kind = crate::fmt::IsCustomType;
368
369    const PV_COUNT: usize = 1;
370}
371
372impl<'a> PanicVal<'a> {
373    /// Wraps this `PanicVal` in a single-element array.
374    pub const fn to_panicvals(&self, _: FmtArg) -> [PanicVal<'a>; 1] {
375        [*self]
376    }
377    /// Returns a copy of this `PanicVal`.
378    pub const fn to_panicval(&self, _: FmtArg) -> PanicVal<'a> {
379        *self
380    }
381}