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}