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}