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}