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