const_panic/
fmt.rs

1//! Formatting-related items
2//!
3//! Panic formatting for custom types can be done in these ways
4//! (in increasing order of verbosity):
5//! - Using the [`PanicFmt` derive] macro
6//! (requires the opt-in `"derive"` feature)
7//! - Using the [`impl_panicfmt`] macro
8//! (requires the default-enabled `"non_basic"` feature)
9//! - Using the [`flatten_panicvals`] macro
10//! (requires the default-enabled `"non_basic"` feature)
11//! - Manually implementing the [`PanicFmt`] trait as described in its docs.
12//!
13//! [`PanicFmt` derive]: derive@crate::PanicFmt
14//! [`PanicFmt`]: trait@crate::fmt::PanicFmt
15//! [`impl_panicfmt`]: crate::impl_panicfmt
16//! [`flatten_panicvals`]: crate::flatten_panicvals
17
18#[cfg(feature = "non_basic")]
19mod non_basic_fmt;
20
21#[cfg(feature = "non_basic")]
22mod fmt_compressed;
23
24pub mod char_formatting;
25
26#[cfg(feature = "non_basic")]
27pub use self::{fmt_compressed::PackedFmtArg, non_basic_fmt::*};
28
29use crate::wrapper::StdWrapper;
30
31use core::marker::PhantomData;
32
33use typewit::{Identity, TypeEq};
34
35/// Trait for types that can be formatted by const panics.
36///
37/// # Implementor
38///
39/// Implementors are expected to also define this inherent method to format the type:
40/// ```rust
41/// # use const_panic::fmt::{FmtArg, IsCustomType, PanicFmt};
42/// # use const_panic::PanicVal;
43/// # struct Foo;
44/// # impl Foo {
45/// const fn to_panicvals<'a>(&'a self, f: FmtArg) -> [PanicVal<'a>; <Self as PanicFmt>::PV_COUNT]
46/// # { loop{} }
47/// # }
48/// # impl PanicFmt for Foo {
49/// #   type This = Self;
50/// #   type Kind = IsCustomType;
51/// #   const PV_COUNT: usize = 1;
52/// # }
53/// ```
54/// The returned [`PanicVal`](crate::PanicVal) can also be `PanicVal<'static>`.
55///
56/// # Implementation examples
57///
58/// This trait can be implemented in these ways (in increasing order of verbosity):
59/// - Using the [`PanicFmt` derive] macro
60/// (requires the opt-in `"derive"` feature)
61/// - Using the [`impl_panicfmt`](impl_panicfmt#examples) macro
62/// (requires the default-enabled `"non_basic"` feature)
63/// - Using the [`flatten_panicvals`](flatten_panicvals#examples) macro
64/// (requires the default-enabled `"non_basic"` feature)
65/// - Using no macros at all
66///
67/// ### Macro-less impl
68///
69/// Implementing this trait for a simple enum without using macros.
70///
71#[cfg_attr(feature = "non_basic", doc = "```rust")]
72#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
73/// use const_panic::{ArrayString, FmtArg, PanicFmt, PanicVal};
74///
75/// // `ArrayString` requires the "non_basic" crate feature (enabled by default),
76/// // everything else in this example works with no enabled crate features.
77/// assert_eq!(
78///     ArrayString::<99>::concat_panicvals(&[
79///         &Foo::Bar.to_panicvals(FmtArg::DEBUG),
80///         &[PanicVal::write_str(",")],
81///         &Foo::Baz.to_panicvals(FmtArg::DEBUG),
82///         &[PanicVal::write_str(",")],
83///         &Foo::Qux.to_panicvals(FmtArg::DEBUG),
84///     ]).unwrap(),
85///     "Bar,Baz,Qux",
86/// );
87///
88///
89/// enum Foo {
90///     Bar,
91///     Baz,
92///     Qux,
93/// }
94///
95/// impl PanicFmt for Foo {
96///     type This = Self;
97///     type Kind = const_panic::IsCustomType;
98///     const PV_COUNT: usize = 1;
99/// }
100///
101/// impl Foo {
102///     pub const fn to_panicvals(self, _: FmtArg) -> [PanicVal<'static>; Foo::PV_COUNT] {
103///         match self {
104///             Self::Bar => [PanicVal::write_str("Bar")],
105///             Self::Baz => [PanicVal::write_str("Baz")],
106///             Self::Qux => [PanicVal::write_str("Qux")],
107///         }
108///     }
109/// }
110///
111/// ```
112/// [`PanicFmt` derive]: derive@crate::PanicFmt
113/// [`PanicFmt`]: trait@crate::fmt::PanicFmt
114/// [`impl_panicfmt`]: crate::impl_panicfmt
115/// [`flatten_panicvals`]: crate::flatten_panicvals
116pub trait PanicFmt {
117    /// The type after dereferencing all references.
118    ///
119    /// User-defined types should generally set this to `Self`.
120    type This: ?Sized;
121    /// Whether this is a user-defined type or standard library type.
122    ///
123    /// User-defined types should generally set this to [`IsCustomType`].
124    type Kind;
125
126    /// The length of the array returned in `Self::to_panicvals`
127    /// (an inherent method that formats the type for panic messages).
128    const PV_COUNT: usize;
129
130    /// A marker type that proves that `Self` implements `PanicFmt`.
131    ///
132    /// Used by const_panic macros to coerce both standard library and
133    /// user-defined types into some type that has a `to_panicvals` method.
134    const PROOF: IsPanicFmt<Self, Self::This, Self::Kind> = IsPanicFmt::NEW;
135}
136
137impl<'a, T: PanicFmt + ?Sized> PanicFmt for &'a T {
138    type This = T::This;
139    type Kind = T::Kind;
140    const PV_COUNT: usize = T::PV_COUNT;
141}
142
143/// Marker type used as the [`PanicFmt::Kind`] associated type for std types.
144pub struct IsStdType;
145
146/// Marker type used as the [`PanicFmt::Kind`] for user-defined types.
147pub struct IsCustomType;
148
149/// A marker type that proves that `S` implements
150/// [`PanicFmt<This = T, Kind = K>`](PanicFmt).
151///
152/// Used by const_panic macros to coerce both standard library and
153/// user-defined types into some type that has a `to_panicvals` method.
154///
155pub struct IsPanicFmt<S: ?Sized, T: ?Sized, K> {
156    self_: PhantomData<fn() -> S>,
157    this: PhantomData<fn() -> T>,
158    kind: PhantomData<fn() -> K>,
159    _priv: (),
160}
161
162impl<T: PanicFmt + ?Sized> IsPanicFmt<T, T::This, T::Kind> {
163    /// Constucts an `IsPanicFmt`
164    pub const NEW: Self = Self {
165        self_: PhantomData,
166        this: PhantomData,
167        kind: PhantomData,
168        _priv: (),
169    };
170}
171
172impl<S: ?Sized, T: ?Sized, K> IsPanicFmt<S, T, K> {
173    /// Infers the `S` type parameter with the argument.
174    ///
175    /// Because the only ways to construct `IsPanicFmt`
176    /// use `IsPanicFmt<S, S::This, S::Kind>`,
177    /// the other type parameters are inferred along with `S`.
178    pub const fn infer(self, _: &S) -> Self {
179        self
180    }
181
182    /// For coercing `&T` to `StdWrapper<&T>`.
183    pub const fn coerce<'a>(self, x: &'a T) -> CoerceReturnOutput<&'a T, K>
184    where
185        // hack to make this bound work in 1.57.0:
186        // K: CoerceReturn<&'a T>,
187        // (before trait bounds were officially supported)
188        <K as Identity>::Type: CoerceReturn<&'a T>,
189    {
190        match <K as CoerceReturn<&'a T>>::__COERCE_TO_WITNESS {
191            __CoerceToWitness::IsStdType(te) => te.to_left(StdWrapper(x)),
192            __CoerceToWitness::IsCustomType(te) => te.to_left(x),
193        }
194    }
195}
196
197impl<S: ?Sized, T: ?Sized, K> Copy for IsPanicFmt<S, T, K> {}
198impl<S: ?Sized, T: ?Sized, K> Clone for IsPanicFmt<S, T, K> {
199    fn clone(&self) -> Self {
200        *self
201    }
202}
203
204/////////////////////////////////////////////////////////////////////
205
206/// Computes the type that the `T` argument is converted into by [`IsPanicFmt::coerce`].
207pub type CoerceReturnOutput<T, K> = <K as CoerceReturn<T>>::CoerceTo;
208
209/// Computes the type that the `T` argument is converted into by [`IsPanicFmt::coerce`].
210///
211/// This trait is sealed, it's implemented by [`IsStdType`] and [`IsCustomType`],
212/// and cannot be implemented by any other type.
213pub trait CoerceReturn<T>: Sized {
214    /// The type that the `T` argument is converted into.
215    type CoerceTo;
216
217    #[doc(hidden)]
218    const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self>;
219}
220
221impl<T> CoerceReturn<T> for IsStdType {
222    type CoerceTo = StdWrapper<T>;
223
224    #[doc(hidden)]
225    const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self> =
226        __CoerceToWitness::IsStdType(TypeEq::NEW);
227}
228impl<T> CoerceReturn<T> for IsCustomType {
229    type CoerceTo = T;
230
231    #[doc(hidden)]
232    const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self> =
233        __CoerceToWitness::IsCustomType(TypeEq::NEW);
234}
235
236#[doc(hidden)]
237pub enum __CoerceToWitness<T, K: CoerceReturn<T>> {
238    #[non_exhaustive]
239    IsStdType(TypeEq<<K as CoerceReturn<T>>::CoerceTo, StdWrapper<T>>),
240
241    #[non_exhaustive]
242    IsCustomType(TypeEq<<K as CoerceReturn<T>>::CoerceTo, T>),
243}
244
245/////////////////////////////////////////////////////////////////////
246
247/// Carries all of the configuration for formatting functions.
248///
249/// # Example
250///
251#[cfg_attr(feature = "non_basic", doc = "```rust")]
252#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
253/// use const_panic::{ArrayString, FmtArg, StdWrapper};
254///
255/// // `StdWrapper` wraps references to std types to provide their `to_panicvals` methods
256/// const ARRAY: &[&str] = &["3", "foo\nbar", "\0qux"];
257///
258/// // Debug formatting
259/// assert_eq!(
260///     const_panic::concat_!(FmtArg::DEBUG; ARRAY),
261///     r#"["3", "foo\nbar", "\x00qux"]"#
262/// );
263///
264/// // Alternate-Debug formatting
265/// assert_eq!(
266///     const_panic::concat_!(FmtArg::ALT_DEBUG; ARRAY),
267///     concat!(
268///         "[\n",
269///         "    \"3\",\n",
270///         "    \"foo\\nbar\",\n",
271///         "    \"\\x00qux\",\n",
272///         "]",
273///     )
274/// );
275///
276/// // Display formatting
277/// assert_eq!(
278///     const_panic::concat_!(FmtArg::DISPLAY; ARRAY),
279///     "[3, foo\nbar, \x00qux]"
280/// );
281///
282/// // Alternate-Display formatting
283/// assert_eq!(
284///     const_panic::concat_!(FmtArg::ALT_DISPLAY; ARRAY),
285///     concat!(
286///         "[\n",
287///         "    3,\n",
288///         "    foo\n",
289///         "bar,\n",
290///         "    \x00qux,\n",
291///         "]",
292///     )
293/// );
294///
295/// ```
296#[derive(Debug, Copy, Clone, PartialEq)]
297pub struct FmtArg {
298    /// How much indentation is needed for a field/array element.
299    ///
300    /// Indentation is used by [`fmt::Delimiter`](crate::fmt::Delimiter)
301    /// and by [`fmt::Separator`](crate::fmt::Separator),
302    /// when the [`is_alternate` field](#structfield.is_alternate) flag is enabled.
303    pub indentation: u8,
304    /// Whether alternate formatting is being used.
305    pub is_alternate: bool,
306    /// Whether this is intended to be `Display` or `Debug` formatted.
307    pub fmt_kind: FmtKind,
308    /// What integers are formatted as: decimal, hexadecimal, or binary.
309    pub number_fmt: NumberFmt,
310}
311
312impl FmtArg {
313    /// A `FmtArg` with no indentation and `Display` formatting.
314    pub const DISPLAY: Self = Self {
315        indentation: 0,
316        fmt_kind: FmtKind::Display,
317        is_alternate: false,
318        number_fmt: NumberFmt::Decimal,
319    };
320
321    /// A `FmtArg` with alternate `Display` formatting, starting with no indentation.
322    pub const ALT_DISPLAY: Self = Self::DISPLAY.set_alternate(true);
323
324    /// A `FmtArg` with `Debug` formatting and no indentation.
325    pub const DEBUG: Self = Self::DISPLAY.set_debug();
326
327    /// A `FmtArg` with alternate `Debug` formatting, starting with no indentation.
328    pub const ALT_DEBUG: Self = Self::DEBUG.set_alternate(true);
329
330    /// A `FmtArg` with `Debug` and `Binary` formatting and no indentation.
331    pub const BIN: Self = Self::DISPLAY.set_bin();
332
333    /// A `FmtArg` with alternate `Debug` and `Binary` formatting,
334    /// starting with no indentation.
335    pub const ALT_BIN: Self = Self::BIN.set_alternate(true);
336
337    /// A `FmtArg` with `Debug` and `Hexadecimal` formatting and no indentation.
338    pub const HEX: Self = Self::DISPLAY.set_hex();
339
340    /// A `FmtArg` with alternate `Debug` and `Hexadecimal` formatting,
341    /// starting with no indentation.
342    pub const ALT_HEX: Self = Self::HEX.set_alternate(true);
343
344    /// Sets whether alternate formatting is enabled
345    pub const fn set_alternate(mut self, is_alternate: bool) -> Self {
346        self.is_alternate = is_alternate;
347        self
348    }
349
350    /// Changes the formatting to `Display`.
351    pub const fn set_display(mut self) -> Self {
352        self.fmt_kind = FmtKind::Display;
353        self
354    }
355
356    /// Changes the formatting to `Debug`.
357    pub const fn set_debug(mut self) -> Self {
358        self.fmt_kind = FmtKind::Debug;
359        self
360    }
361
362    /// Changes the formatting to `Debug`, and number formatting to `Hexadecimal`.
363    pub const fn set_hex(mut self) -> Self {
364        self.fmt_kind = FmtKind::Debug;
365        self.number_fmt = NumberFmt::Hexadecimal;
366        self
367    }
368
369    /// Changes the formatting to `Debug`, and number formatting to `Binary`.
370    pub const fn set_bin(mut self) -> Self {
371        self.fmt_kind = FmtKind::Debug;
372        self.number_fmt = NumberFmt::Binary;
373        self
374    }
375}
376
377#[cfg(feature = "non_basic")]
378#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
379impl FmtArg {
380    /// Increments the indentation by [`INDENTATION_STEP`] spaces.
381    pub const fn indent(mut self) -> Self {
382        self.indentation += INDENTATION_STEP;
383        self
384    }
385
386    /// Decrement the indentation by [`INDENTATION_STEP`] spaces.
387    pub const fn unindent(mut self) -> Self {
388        self.indentation = self.indentation.saturating_sub(INDENTATION_STEP);
389        self
390    }
391}
392
393////////////////////////////////////////////////////////////////////////////////
394
395/// What kind of formatting to do, either `Display` or `Debug`.
396#[non_exhaustive]
397#[derive(Debug, Copy, Clone, PartialEq)]
398pub enum FmtKind {
399    /// `Debug` formatting
400    Debug = 0,
401    /// `Display` formatting
402    Display = 1,
403}
404
405////////////////////////////////////////////////////////////////////////////////
406
407/// What integers are formatted as.
408#[non_exhaustive]
409#[derive(Debug, Copy, Clone, PartialEq)]
410pub enum NumberFmt {
411    /// Formatted as decimal.
412    Decimal = 0,
413    /// Formatted as binary, eg: `101`, `0b110`.
414    Binary = 1,
415    /// Formatted as hexadecimal, eg: `FAD`, `0xDE`.
416    Hexadecimal = 2,
417}