const_panic/macros/non_basic_macros.rs
1/// Formats multiple values into an array of `PanicVal`s.
2///
3/// The `flatten`ing part comes from the fact that each argument
4/// is converted to an array of `PanicVal`,
5/// which are then concatenated into a single array.
6///
7/// # Arguments
8///
9/// The syntax for this macro is
10/// ```text
11/// flatten_panicvals!(
12/// $fmtarg:expr $(, $pv_count:expr)?;
13/// $(
14/// $($Type:ty => )? $($format_override:tt :)? $arg_to_fmt:expr
15/// ),*
16/// $()?
17/// )
18/// ```
19///
20/// `$fmtarg` is a [`FmtArg`](crate::FmtArg) argument
21/// which determines how non-literal `$arg_to_fmt` arguments are formatted.
22///
23/// `$pv_count` is an optional argument which overrides the length of the array
24/// that this returns.
25/// <br>If this argument is smaller than the flattened arrays would create,
26/// it produces a compile-time error.
27/// If this is larger, this fills the trailing elements with `PanicVal::EMPTY`.
28///
29/// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument,
30/// changing how that `$arg_to_fmt` argument is formatted.
31///
32/// `$arg_to_fmt` are the formatted arguments,
33/// which must implement the [`PanicFmt`](crate::fmt::PanicFmt) trait.
34///
35///
36/// If the `$Type =>` syntax is used, this calls the `to_panicvals`
37/// method on `$arg_to_fmt`.<br>
38/// If the `$Type =>` syntax is *not* used, this calls the `to_panicval`
39/// method on `$arg_to_fmt`.
40///
41/// These are the signatures of those methods:
42/// ```rust
43/// # use const_panic::{FmtArg, PanicFmt, PanicVal};
44/// # struct Foo;
45/// #
46/// # impl PanicFmt for Foo {
47/// # type This = Self;
48/// # type Kind = const_panic::IsCustomType;
49/// # const PV_COUNT: usize = 1;
50/// # }
51/// # impl Foo {
52/// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; <Foo as PanicFmt>::PV_COUNT]
53/// # { loop{} }
54/// # }
55/// ```
56/// ```rust
57/// # use const_panic::{FmtArg, PanicVal};
58/// # struct Bar;
59/// #
60/// # impl Bar {
61/// const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_>
62/// # { loop{} }
63/// # }
64/// ```
65///
66/// ### Parsing limitation
67///
68/// Because of limitations of `macro_rules!` macros,
69/// you'll sometimes need to wrap `$arg_to_fmt` arguments in parentheses to fix
70/// this error:
71/// ```text
72/// error: expected type, found `foo`
73/// ```
74///
75///
76#[doc = formatting_docs!("
77- `open`: increments `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`]
78before formatting the argument, and uses Display formatting for that argument.
79
80- `close`: decrements `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`]
81before formatting the argument, and uses Display formatting for that argument.
82
83[`fmt::INDENTATION_STEP`]: crate::fmt::INDENTATION_STEP
84")]
85///
86///
87/// # Examples
88///
89/// [Struct](#struct-formatting) and [Enum](#enum-formatting) Formatting examples below.
90///
91/// ### Basic
92///
93/// ```rust
94/// use const_panic::{ArrayString, FmtArg, flatten_panicvals};
95///
96///
97/// assert_eq!(
98/// ArrayString::<999>::from_panicvals(
99/// // Formatting literals
100/// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello")
101/// ).unwrap(),
102/// "100hello"
103/// );
104///
105/// assert_eq!(
106/// ArrayString::<999>::from_panicvals(
107/// // Formatting non-literals.
108/// // `"foo"` is considered a non-literal, because it's inside other tokens.
109/// &flatten_panicvals!(FmtArg::ALT_DEBUG; ("foo"), [100u8, 200])
110/// ).unwrap(),
111/// concat!(
112/// "\"foo\"[\n",
113/// " 100,\n",
114/// " 200,\n",
115/// "]",
116/// )
117/// );
118///
119/// assert_eq!(
120/// ArrayString::<999>::from_panicvals(&
121/// // Alternate-Debug Formatting composite types.
122/// flatten_panicvals!(
123/// FmtArg::ALT_DEBUG;
124/// Foo => Foo(3, "5"),
125/// ", ",
126/// Bar => Bar{x: "hello"}
127/// )
128/// ).unwrap(),
129/// concat!(
130/// "Foo(\n",
131/// " 3,\n",
132/// " \"5\",\n",
133/// "), Bar {\n",
134/// " x: \"hello\",\n",
135/// "}",
136/// )
137/// );
138///
139/// assert_eq!(
140/// ArrayString::<999>::from_panicvals(&
141/// // Overriding the formatting of arguments.
142/// //
143/// // The `open` and `close` overrides are demonstrated in the
144/// // struct and enum examples.
145/// flatten_panicvals!(
146/// FmtArg::DEBUG;
147/// Foo => display: Foo(3, "5"),
148/// debug: ", ",
149/// Bar => Bar{x: "hello"}
150/// )
151/// ).unwrap(),
152/// r#"Foo(3, 5)", "Bar { x: "hello" }"#
153/// );
154///
155///
156///
157/// struct Foo(u32, &'static str);
158///
159/// const_panic::impl_panicfmt!{
160/// struct Foo(u32, &'static str);
161/// }
162///
163/// struct Bar {
164/// x: &'static str,
165/// }
166///
167/// const_panic::impl_panicfmt!{
168/// struct Bar {
169/// x: &'static str,
170/// }
171/// }
172///
173/// ```
174///
175/// ### Struct formatting
176///
177/// Implementing panic formatting for braced and tuple structs.
178///
179/// ```rust
180/// use const_panic::{
181/// fmt::{self, FmtArg, PanicFmt, ComputePvCount},
182/// ArrayString, PanicVal,
183/// flatten_panicvals,
184/// };
185///
186/// fn main(){
187/// let foo = Foo {
188/// x: &[3, 5, 8, 13],
189/// y: 21,
190/// z: Bar(false, true),
191/// };
192///
193/// assert_eq!(
194/// ArrayString::<100>::from_panicvals(&foo.to_panicvals(FmtArg::DEBUG)).unwrap(),
195/// "Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }",
196/// );
197/// assert_eq!(
198/// ArrayString::<200>::from_panicvals(&foo.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
199/// concat!(
200/// "Foo {\n",
201/// " x: [\n",
202/// " 3,\n",
203/// " 5,\n",
204/// " 8,\n",
205/// " 13,\n",
206/// " ],\n",
207/// " y: 21,\n",
208/// " z: Bar(\n",
209/// " false,\n",
210/// " true,\n",
211/// " ),\n",
212/// "}",
213/// ),
214/// );
215/// }
216///
217///
218///
219/// struct Foo<'a> {
220/// x: &'a [u8],
221/// y: u8,
222/// z: Bar,
223/// }
224///
225/// struct Bar(bool, bool);
226///
227///
228/// impl PanicFmt for Foo<'_> {
229/// type This = Self;
230/// type Kind = const_panic::IsCustomType;
231///
232/// // `ComputePvCount` allows computing the length of the array of `PanicVal`s
233/// // returned by `Foo::to_panicvals` below.
234/// //
235/// // Note that ComputePvCount only calculates the correct number if you
236/// // follow the pattern in this example.
237/// const PV_COUNT: usize = ComputePvCount{
238/// field_amount: 3,
239/// summed_pv_count: <&[u8]>::PV_COUNT
240/// + <u8>::PV_COUNT
241/// + <Bar>::PV_COUNT,
242/// delimiter: fmt::TypeDelim::Braced,
243/// }.call();
244/// }
245///
246/// impl<'a> Foo<'a> {
247/// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'a>; Foo::PV_COUNT] {
248/// // These constants from `fmt` add newlines and padding
249/// // when the `fmtarg.is_alternate` flag is enabled,
250/// // to match the standard behavior for `Debug` formatting.
251/// flatten_panicvals! {fmtarg;
252/// "Foo",
253/// // the `open:` format override increments `fmtarg.indentation`
254/// // by `const_panic::fmt::INDENTATION_STEP` spaces.
255/// // The indentation field is used by these constants when the
256/// // `fmtarg.is_alternate` flag is enabled.
257/// open: fmt::OpenBrace,
258/// // fmt::COMMA_SEP must only be used between fields
259/// "x: ", &[u8] => self.x, fmt::COMMA_SEP,
260/// "y: ", u8 => self.y, fmt::COMMA_SEP,
261/// // fmt::COMMA_TERM must only be used after the last field
262/// "z: ", Bar => self.z, fmt::COMMA_TERM,
263/// // the `close:` format override decrements the indentation.
264/// close: fmt::CloseBrace,
265/// }
266/// }
267/// }
268///
269/// impl PanicFmt for Bar {
270/// type This = Self;
271/// type Kind = const_panic::IsCustomType;
272///
273/// const PV_COUNT: usize = ComputePvCount{
274/// field_amount: 2,
275/// summed_pv_count: <bool>::PV_COUNT * 2,
276/// delimiter: fmt::TypeDelim::Tupled,
277/// }.call();
278/// }
279///
280/// impl Bar {
281/// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'static>; Bar::PV_COUNT] {
282/// flatten_panicvals! {f;
283/// "Bar",
284/// open: fmt::OpenParen,
285/// // fmt::COMMA_SEP must only be used between fields
286/// self.0, fmt::COMMA_SEP,
287/// // fmt::COMMA_TERM must only be used after the last field
288/// self.1, fmt::COMMA_TERM,
289/// close: fmt::CloseParen,
290/// }
291/// }
292/// }
293/// ```
294///
295/// ### Enum Formatting
296///
297/// This example demonstrates formatting of generic enum types.
298///
299/// ```rust
300/// use const_panic::{
301/// fmt::{self, FmtArg, PanicFmt, ComputePvCount},
302/// ArrayString, PanicVal,
303/// flatten_panicvals,
304/// };
305///
306/// fn main() {
307/// let up: Qux<u8> = Qux::Up;
308/// // Debug formatting the Up variant
309/// assert_eq!(
310/// ArrayString::<100>::from_panicvals(&up.to_panicvals(FmtArg::DEBUG)).unwrap(),
311/// "Up",
312/// );
313///
314///
315/// let down: Qux<u16> = Qux::Down { x: 21, y: 34, z: 55 };
316/// // Debug formatting the Down variant
317/// assert_eq!(
318/// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::DEBUG)).unwrap(),
319/// "Down { x: 21, y: 34, z: 55 }",
320/// );
321/// // Alternate-Debug formatting the Down variant
322/// assert_eq!(
323/// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
324/// concat!(
325/// "Down {\n",
326/// " x: 21,\n",
327/// " y: 34,\n",
328/// " z: 55,\n",
329/// "}",
330/// )
331/// );
332///
333///
334/// let left: Qux<u32> = Qux::Left(89);
335/// // Debug formatting the Left variant
336/// assert_eq!(
337/// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::DEBUG)).unwrap(),
338/// "Left(89)",
339/// );
340/// // Alternate-Debug formatting the Left variant
341/// assert_eq!(
342/// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
343/// concat!(
344/// "Left(\n",
345/// " 89,\n",
346/// ")",
347/// )
348/// );
349/// }
350///
351/// enum Qux<T> {
352/// Up,
353/// Down { x: T, y: T, z: T },
354/// Left(u64),
355/// }
356///
357///
358/// impl<T: PanicFmt> PanicFmt for Qux<T> {
359/// type This = Self;
360/// type Kind = const_panic::IsCustomType;
361///
362/// const PV_COUNT: usize = {
363/// // `ComputePvCount` computes the length of the array of `PanicVal`s
364/// // produced by each variant.
365/// //
366/// // `slice_max_usize` returns the maximum usize in a slice.
367/// // In this case, to return the longest array produced by the variants.
368/// const_panic::utils::slice_max_usize(&[
369/// ComputePvCount{
370/// field_amount: 0,
371/// summed_pv_count: 0,
372/// delimiter: fmt::TypeDelim::Braced,
373/// }.call(),
374/// ComputePvCount{
375/// field_amount: 3,
376/// summed_pv_count: <T>::PV_COUNT * 3,
377/// delimiter: fmt::TypeDelim::Braced,
378/// }.call(),
379/// ComputePvCount{
380/// field_amount: 1,
381/// summed_pv_count: <u64>::PV_COUNT,
382/// delimiter: fmt::TypeDelim::Tupled,
383/// }.call(),
384/// ])
385/// };
386/// }
387///
388/// // Because of limitations of stable const evaluation,
389/// // you have to use macros to implement the `to_panicvals` method
390/// // for more than one concrete type (ignoring lifetimes).
391/// //
392/// // This macro implements panic formatting for
393/// // - `Qux<u8>`
394/// // - `Qux<u16>`
395/// // - `Qux<u32>`
396/// const_panic::inline_macro! {
397/// (u8),
398/// (u16),
399/// (u32);
400///
401/// ($T:ty) =>
402///
403/// impl Qux<$T> {
404/// pub const fn to_panicvals(
405/// &self,
406/// fmtarg: FmtArg,
407/// ) -> [PanicVal<'static>; <Qux<$T>>::PV_COUNT] {
408/// match self {
409/// Self::Up =>
410/// // The `<Qux<$T>>::PV_COUNT` argument tells `flatten_panicvals` to
411/// // create an array of that length.
412/// // Variants that would otherwise produce shorter arrays
413/// // pad that array with trailing `PanicVal::EMPTY`.
414/// flatten_panicvals! {fmtarg, <Qux<$T>>::PV_COUNT;
415/// "Up"
416/// },
417/// Self::Down{x, y, z} =>
418/// // These constants from `fmt` add newlines and padding
419/// // when the `fmtarg.is_alternate` flag is enabled,
420/// // to match the standard behavior for `Debug` formatting.
421/// flatten_panicvals! {fmtarg, <Qux<$T>>::PV_COUNT;
422/// "Down",
423/// // the `open:` format override increments `fmtarg.indentation`
424/// // by `const_panic::fmt::INDENTATION_STEP` spaces.
425/// // The indentation field is used by these constants when the
426/// // `fmtarg.is_alternate` flag is enabled.
427/// open: fmt::OpenBrace,
428/// // fmt::COMMA_SEP must only be used between fields
429/// "x: ", x, fmt::COMMA_SEP,
430/// "y: ", y, fmt::COMMA_SEP,
431/// // fmt::COMMA_TERM must only be used after the last field
432/// "z: ", z, fmt::COMMA_TERM,
433/// close: fmt::CloseBrace,
434/// },
435/// Self::Left(x) => flatten_panicvals! {fmtarg, <Qux<$T>>::PV_COUNT;
436/// "Left",
437/// open: fmt::OpenParen,
438/// x, fmt::COMMA_TERM,
439/// close: fmt::CloseParen,
440/// },
441/// }
442/// }
443/// }
444/// }
445/// ```
446///
447///
448/// [`PanicVal`]: crate::PanicVal
449///
450#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
451#[macro_export]
452macro_rules! flatten_panicvals {
453 ($fmtargs:expr $(, $length:expr)?; $($args:tt)* ) => {{
454 let mut fmtargs: $crate::FmtArg = $fmtargs;
455 $crate::__to_pvf_inner!(fmtargs [$(length($length))?][$($args)* ,])
456 }};
457}
458
459#[doc(hidden)]
460#[macro_export]
461macro_rules! __to_pvf_inner {
462 (
463 $fmtargs:ident
464 [
465 $(length($expected_length:expr))?
466 $((($len:expr, $kind:ident $args:tt), $fmt_override:tt, $reff:expr))*
467 ]
468 [$(,)*]
469 ) => ({
470 const __ADDED_UP_LEN_SDOFKE09F__: $crate::__::usize = 0 $( + $len )*;
471 const __LEN_SDOFKE09F__: $crate::__::usize =
472 $crate::__to_pvf_used_length!(__ADDED_UP_LEN_SDOFKE09F__, $($expected_length)?);
473
474 $(
475 const _: () =
476 $crate::__::assert_flatten_panicvals_length(
477 $expected_length,
478 __ADDED_UP_LEN_SDOFKE09F__,
479 );
480 )?
481 $crate::__::flatten_panicvals::<__LEN_SDOFKE09F__>(&[
482 $(
483 $crate::__to_pvf_kind!($fmtargs $kind $args, $fmt_override, $reff)
484 ),*
485 ])
486 });
487
488 // Had to add these workarounds
489 // to avoid getting stuck being parsed as a type in the `$ty:ty =>` branch.
490 ($fmtargs:ident $prev:tt [$tt:tt, $($rem:tt)*]) => {
491 $crate::__to_pvf_expr!{
492 $fmtargs
493 $prev
494 (1, single())
495 [$tt, $($rem)*]
496 }
497 };
498 ($fmtargs:ident $prev:tt [$tt0:tt $tt1:tt, $($rem:tt)*]) => {
499 $crate::__to_pvf_expr!{
500 $fmtargs
501 $prev
502 (1, single())
503 [$tt0 $tt1, $($rem)*]
504 }
505 };
506
507 ($fmtargs:ident $prev:tt [$ty:ty => $($rem:tt)*]) => {
508 $crate::__to_pvf_expr!{
509 $fmtargs
510 $prev
511 (<$ty as $crate::__::PanicFmt>::PV_COUNT, many($ty))
512 [$($rem)*]
513 }
514 };
515 ($fmtargs:ident $prev:tt [$($rem:tt)*]) => {
516 $crate::__to_pvf_expr!{
517 $fmtargs
518 $prev
519 (1, single())
520 [$($rem)*]
521 }
522 };
523}
524
525#[doc(hidden)]
526#[macro_export]
527macro_rules! __to_pvf_expr {
528 ($fmtargs:ident [$($prev:tt)*] $other:tt [$kw:tt: $reff:expr, $($rem:tt)*]) => {
529 $crate::__to_pvf_inner!{
530 $fmtargs
531
532 [$($prev)* ($other, $kw, $reff)]
533
534 [$($rem)*]
535 }
536 };
537 ($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:literal, $($rem:tt)*])=>{
538 $crate::__to_pvf_inner!{
539 $fmtargs
540
541 [$($prev)* ($other, display, $reff)]
542
543 [$($rem)*]
544 }
545 };
546 ($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:expr, $($rem:tt)*]) => {
547 $crate::__to_pvf_inner!{
548 $fmtargs
549
550 [$($prev)* ($other, _, $reff)]
551
552 [$($rem)*]
553 }
554 };
555 ($fmtargs:ident [$($prev:tt)*] $other:tt [$($rem:tt)*]) => {
556 $crate::__::compile_error!(concat!(
557 "expected expression, found:",
558 stringify!($($rem)*)
559 ))
560 };
561}
562
563#[doc(hidden)]
564#[macro_export]
565macro_rules! __to_pvf_kind {
566 ($fmtargs:ident single (), $fmt_override:tt, $reff:tt) => {
567 &match &$reff {
568 reff => [$crate::__::PanicFmt::PROOF
569 .infer(reff)
570 .coerce(reff)
571 .to_panicval($crate::__set_fmt_from_kw!($fmt_override, $fmtargs))],
572 }
573 };
574 ($fmtargs:ident many ($ty:ty), $fmt_override:tt, $reff:tt) => {
575 $crate::__::panicvals_id::<{ <$ty as $crate::__::PanicFmt>::PV_COUNT }>(&match &$reff {
576 reff => <$ty as $crate::__::PanicFmt>::PROOF
577 .coerce(reff)
578 .to_panicvals($crate::__set_fmt_from_kw!($fmt_override, $fmtargs)),
579 })
580 };
581}
582
583#[doc(hidden)]
584#[macro_export]
585macro_rules! __to_pvf_used_length {
586 ( $added_up:expr, $expected_length:expr ) => {
587 $crate::utils::max_usize($added_up, $expected_length)
588 };
589 ( $added_up:expr, ) => {
590 $added_up
591 };
592}
593
594/// Helper macro for defining and using a `macro_rules!` macro inline.
595///
596/// The reason this was defined is to work around a limitation in stable const-eval,
597/// where you can't call methods on generic types.
598///
599/// # Example
600///
601/// Implementing panic formatting for a generic struct with [`flatten_panicvals`].
602///
603/// ```rust
604/// use const_panic::{
605/// fmt::{self, ComputePvCount, FmtArg},
606/// ArrayString, PanicFmt, PanicVal, flatten_panicvals, inline_macro,
607/// };
608///
609/// // Debug formatting
610/// assert_eq!(
611/// const_panic::concat_!(FmtArg::DEBUG; Foo(10, 20)),
612/// "Foo(10, 20)"
613/// );
614///
615/// // Alternate-Debug formatting
616/// assert_eq!(
617/// const_panic::concat_!(FmtArg::ALT_DEBUG; Foo(false, true)),
618/// concat!(
619/// "Foo(\n",
620/// " false,\n",
621/// " true,\n",
622/// ")",
623/// ),
624/// );
625///
626/// // Display formatting
627/// assert_eq!(
628/// const_panic::concat_!(FmtArg::DISPLAY; Foo("hmm", "what?")),
629/// "Foo(hmm, what?)"
630/// );
631///
632///
633///
634///
635/// struct Foo<T>(T, T);
636///
637/// impl<T> PanicFmt for Foo<T>
638/// where
639/// T: PanicFmt,
640/// {
641/// type This = Self;
642/// type Kind = const_panic::IsCustomType;
643///
644/// const PV_COUNT: usize = ComputePvCount{
645/// field_amount: 2,
646/// summed_pv_count: T::PV_COUNT * 2,
647/// delimiter: fmt::TypeDelim::Tupled,
648/// }.call();
649/// }
650///
651/// // Because of limitations of stable const evaluation,
652/// // this macro only implements panic formatting for
653/// // - `Foo<bool>`
654/// // - `Foo<u8>`
655/// // - `Foo<&str>`
656/// inline_macro!{
657/// (bool), (u8), (&str);
658/// ($T:ty)=>
659/// impl Foo<$T> {
660/// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::<$T>::PV_COUNT] {
661/// flatten_panicvals! {fmtarg;
662/// "Foo",
663/// open: fmt::OpenParen,
664/// $T => self.0, fmt::COMMA_SEP,
665/// $T => self.1, fmt::COMMA_TERM,
666/// close: fmt::CloseParen,
667/// }
668/// }
669/// }
670/// }
671///
672/// ```
673#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
674#[macro_export]
675macro_rules! inline_macro{
676 (
677 $(($($args:tt)*)),* $(,)?;
678 ($($params:tt)*)
679 =>
680 $($code:tt)*
681 ) => {
682 macro_rules! __dummy__ {
683 ($($params)*) => {$($code)*}
684 }
685 $(
686 __dummy__!{ $($args)* }
687 )*
688 }
689}