/// Formats multiple values into an array of `PanicVal`s.
///
/// The `flatten`ing part comes from the fact that each argument
/// is converted to an array of `PanicVal`,
/// which are then concatenated into a single array.
///
/// # Arguments
///
/// The syntax for this macro is
/// ```text
/// flatten_panicvals!(
/// $fmtarg:expr $(, $pv_count:expr)?;
/// $(
/// $($Type:ty => )? $($format_override:tt :)? $arg_to_fmt:expr
/// ),*
/// $()?
/// )
/// ```
///
/// `$fmtarg` is a [`FmtArg`](crate::FmtArg) argument
/// which determines how non-literal `$arg_to_fmt` arguments are formatted.
///
/// `$pv_count` is an optional argument which overrides the length of the array
/// that this returns.
///
If this argument is smaller than the flattened arrays would create,
/// it produces a compile-time error.
/// If this is larger, this fills the trailing elements with `PanicVal::EMPTY`.
///
/// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument,
/// changing how that `$arg_to_fmt` argument is formatted.
///
/// `$arg_to_fmt` are the formatted arguments,
/// which must implement the [`PanicFmt`](crate::fmt::PanicFmt) trait.
///
///
/// If the `$Type =>` syntax is used, this calls the `to_panicvals`
/// method on `$arg_to_fmt`.
/// If the `$Type =>` syntax is *not* used, this calls the `to_panicval`
/// method on `$arg_to_fmt`.
///
/// These are the signatures of those methods:
/// ```rust
/// # use const_panic::{FmtArg, PanicFmt, PanicVal};
/// # struct Foo;
/// #
/// # impl PanicFmt for Foo {
/// # type This = Self;
/// # type Kind = const_panic::IsCustomType;
/// # const PV_COUNT: usize = 1;
/// # }
/// # impl Foo {
/// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; ::PV_COUNT]
/// # { loop{} }
/// # }
/// ```
/// ```rust
/// # use const_panic::{FmtArg, PanicVal};
/// # struct Bar;
/// #
/// # impl Bar {
/// const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_>
/// # { loop{} }
/// # }
/// ```
///
/// ### Parsing limitation
///
/// Because of limitations of `macro_rules!` macros,
/// you'll sometimes need to wrap `$arg_to_fmt` arguments in parentheses to fix
/// this error:
/// ```text
/// error: expected type, found `foo`
/// ```
///
///
#[doc = formatting_docs!("
- `open`: increments `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`]
before formatting the argument, and uses Display formatting for that argument.
- `close`: decrements `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`]
before formatting the argument, and uses Display formatting for that argument.
[`fmt::INDENTATION_STEP`]: crate::fmt::INDENTATION_STEP
")]
///
///
/// # Examples
///
/// [Struct](#struct-formatting) and [Enum](#enum-formatting) Formatting examples below.
///
/// ### Basic
///
/// ```rust
/// use const_panic::{ArrayString, FmtArg, flatten_panicvals};
///
///
/// assert_eq!(
/// ArrayString::<999>::from_panicvals(
/// // Formatting literals
/// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello")
/// ).unwrap(),
/// "100hello"
/// );
///
/// assert_eq!(
/// ArrayString::<999>::from_panicvals(
/// // Formatting non-literals.
/// // `"foo"` is considered a non-literal, because it's inside other tokens.
/// &flatten_panicvals!(FmtArg::ALT_DEBUG; ("foo"), [100u8, 200])
/// ).unwrap(),
/// concat!(
/// "\"foo\"[\n",
/// " 100,\n",
/// " 200,\n",
/// "]",
/// )
/// );
///
/// assert_eq!(
/// ArrayString::<999>::from_panicvals(&
/// // Alternate-Debug Formatting composite types.
/// flatten_panicvals!(
/// FmtArg::ALT_DEBUG;
/// Foo => Foo(3, "5"),
/// ", ",
/// Bar => Bar{x: "hello"}
/// )
/// ).unwrap(),
/// concat!(
/// "Foo(\n",
/// " 3,\n",
/// " \"5\",\n",
/// "), Bar {\n",
/// " x: \"hello\",\n",
/// "}",
/// )
/// );
///
/// assert_eq!(
/// ArrayString::<999>::from_panicvals(&
/// // Overriding the formatting of arguments.
/// //
/// // The `open` and `close` overrides are demonstrated in the
/// // struct and enum examples.
/// flatten_panicvals!(
/// FmtArg::DEBUG;
/// Foo => display: Foo(3, "5"),
/// debug: ", ",
/// Bar => Bar{x: "hello"}
/// )
/// ).unwrap(),
/// r#"Foo(3, 5)", "Bar { x: "hello" }"#
/// );
///
///
///
/// struct Foo(u32, &'static str);
///
/// const_panic::impl_panicfmt!{
/// struct Foo(u32, &'static str);
/// }
///
/// struct Bar {
/// x: &'static str,
/// }
///
/// const_panic::impl_panicfmt!{
/// struct Bar {
/// x: &'static str,
/// }
/// }
///
/// ```
///
/// ### Struct formatting
///
/// Implementing panic formatting for braced and tuple structs.
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg, PanicFmt, ComputePvCount},
/// ArrayString, PanicVal,
/// flatten_panicvals,
/// };
///
/// fn main(){
/// let foo = Foo {
/// x: &[3, 5, 8, 13],
/// y: 21,
/// z: Bar(false, true),
/// };
///
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&foo.to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }",
/// );
/// assert_eq!(
/// ArrayString::<200>::from_panicvals(&foo.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
/// concat!(
/// "Foo {\n",
/// " x: [\n",
/// " 3,\n",
/// " 5,\n",
/// " 8,\n",
/// " 13,\n",
/// " ],\n",
/// " y: 21,\n",
/// " z: Bar(\n",
/// " false,\n",
/// " true,\n",
/// " ),\n",
/// "}",
/// ),
/// );
/// }
///
///
///
/// struct Foo<'a> {
/// x: &'a [u8],
/// y: u8,
/// z: Bar,
/// }
///
/// struct Bar(bool, bool);
///
///
/// impl PanicFmt for Foo<'_> {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// // `ComputePvCount` allows computing the length of the array of `PanicVal`s
/// // returned by `Foo::to_panicvals` below.
/// //
/// // Note that ComputePvCount only calculates the correct number if you
/// // follow the pattern in this example.
/// const PV_COUNT: usize = ComputePvCount{
/// field_amount: 3,
/// summed_pv_count: <&[u8]>::PV_COUNT
/// + ::PV_COUNT
/// + ::PV_COUNT,
/// delimiter: fmt::TypeDelim::Braced,
/// }.call();
/// }
///
/// impl<'a> Foo<'a> {
/// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'a>; Foo::PV_COUNT] {
/// // These constants from `fmt` add newlines and padding
/// // when the `fmtarg.is_alternate` flag is enabled,
/// // to match the standard behavior for `Debug` formatting.
/// flatten_panicvals! {fmtarg;
/// "Foo",
/// // the `open:` format override increments `fmtarg.indentation`
/// // by `const_panic::fmt::INDENTATION_STEP` spaces.
/// // The indentation field is used by these constants when the
/// // `fmtarg.is_alternate` flag is enabled.
/// open: fmt::OpenBrace,
/// // fmt::COMMA_SEP must only be used between fields
/// "x: ", &[u8] => self.x, fmt::COMMA_SEP,
/// "y: ", u8 => self.y, fmt::COMMA_SEP,
/// // fmt::COMMA_TERM must only be used after the last field
/// "z: ", Bar => self.z, fmt::COMMA_TERM,
/// // the `close:` format override decrements the indentation.
/// close: fmt::CloseBrace,
/// }
/// }
/// }
///
/// impl PanicFmt for Bar {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// const PV_COUNT: usize = ComputePvCount{
/// field_amount: 2,
/// summed_pv_count: ::PV_COUNT * 2,
/// delimiter: fmt::TypeDelim::Tupled,
/// }.call();
/// }
///
/// impl Bar {
/// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'static>; Bar::PV_COUNT] {
/// flatten_panicvals! {f;
/// "Bar",
/// open: fmt::OpenParen,
/// // fmt::COMMA_SEP must only be used between fields
/// self.0, fmt::COMMA_SEP,
/// // fmt::COMMA_TERM must only be used after the last field
/// self.1, fmt::COMMA_TERM,
/// close: fmt::CloseParen,
/// }
/// }
/// }
/// ```
///
/// ### Enum Formatting
///
/// This example demonstrates formatting of generic enum types.
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg, PanicFmt, ComputePvCount},
/// ArrayString, PanicVal,
/// flatten_panicvals,
/// };
///
/// fn main() {
/// let up: Qux = Qux::Up;
/// // Debug formatting the Up variant
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&up.to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "Up",
/// );
///
///
/// let down: Qux = Qux::Down { x: 21, y: 34, z: 55 };
/// // Debug formatting the Down variant
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "Down { x: 21, y: 34, z: 55 }",
/// );
/// // Alternate-Debug formatting the Down variant
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
/// concat!(
/// "Down {\n",
/// " x: 21,\n",
/// " y: 34,\n",
/// " z: 55,\n",
/// "}",
/// )
/// );
///
///
/// let left: Qux = Qux::Left(89);
/// // Debug formatting the Left variant
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "Left(89)",
/// );
/// // Alternate-Debug formatting the Left variant
/// assert_eq!(
/// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
/// concat!(
/// "Left(\n",
/// " 89,\n",
/// ")",
/// )
/// );
/// }
///
/// enum Qux {
/// Up,
/// Down { x: T, y: T, z: T },
/// Left(u64),
/// }
///
///
/// impl PanicFmt for Qux {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// const PV_COUNT: usize = {
/// // `ComputePvCount` computes the length of the array of `PanicVal`s
/// // produced by each variant.
/// //
/// // `slice_max_usize` returns the maximum usize in a slice.
/// // In this case, to return the longest array produced by the variants.
/// const_panic::utils::slice_max_usize(&[
/// ComputePvCount{
/// field_amount: 0,
/// summed_pv_count: 0,
/// delimiter: fmt::TypeDelim::Braced,
/// }.call(),
/// ComputePvCount{
/// field_amount: 3,
/// summed_pv_count: ::PV_COUNT * 3,
/// delimiter: fmt::TypeDelim::Braced,
/// }.call(),
/// ComputePvCount{
/// field_amount: 1,
/// summed_pv_count: ::PV_COUNT,
/// delimiter: fmt::TypeDelim::Tupled,
/// }.call(),
/// ])
/// };
/// }
///
/// // Because of limitations of stable const evaluation,
/// // you have to use macros to implement the `to_panicvals` method
/// // for more than one concrete type (ignoring lifetimes).
/// //
/// // This macro implements panic formatting for
/// // - `Qux`
/// // - `Qux`
/// // - `Qux`
/// const_panic::inline_macro! {
/// (u8),
/// (u16),
/// (u32);
///
/// ($T:ty) =>
///
/// impl Qux<$T> {
/// pub const fn to_panicvals(
/// &self,
/// fmtarg: FmtArg,
/// ) -> [PanicVal<'static>; >::PV_COUNT] {
/// match self {
/// Self::Up =>
/// // The `>::PV_COUNT` argument tells `flatten_panicvals` to
/// // create an array of that length.
/// // Variants that would otherwise produce shorter arrays
/// // pad that array with trailing `PanicVal::EMPTY`.
/// flatten_panicvals! {fmtarg, >::PV_COUNT;
/// "Up"
/// },
/// Self::Down{x, y, z} =>
/// // These constants from `fmt` add newlines and padding
/// // when the `fmtarg.is_alternate` flag is enabled,
/// // to match the standard behavior for `Debug` formatting.
/// flatten_panicvals! {fmtarg, >::PV_COUNT;
/// "Down",
/// // the `open:` format override increments `fmtarg.indentation`
/// // by `const_panic::fmt::INDENTATION_STEP` spaces.
/// // The indentation field is used by these constants when the
/// // `fmtarg.is_alternate` flag is enabled.
/// open: fmt::OpenBrace,
/// // fmt::COMMA_SEP must only be used between fields
/// "x: ", x, fmt::COMMA_SEP,
/// "y: ", y, fmt::COMMA_SEP,
/// // fmt::COMMA_TERM must only be used after the last field
/// "z: ", z, fmt::COMMA_TERM,
/// close: fmt::CloseBrace,
/// },
/// Self::Left(x) => flatten_panicvals! {fmtarg, >::PV_COUNT;
/// "Left",
/// open: fmt::OpenParen,
/// x, fmt::COMMA_TERM,
/// close: fmt::CloseParen,
/// },
/// }
/// }
/// }
/// }
/// ```
///
///
/// [`PanicVal`]: crate::PanicVal
///
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[macro_export]
macro_rules! flatten_panicvals {
($fmtargs:expr $(, $length:expr)?; $($args:tt)* ) => {{
let mut fmtargs: $crate::FmtArg = $fmtargs;
$crate::__to_pvf_inner!(fmtargs [$(length($length))?][$($args)* ,])
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __to_pvf_inner {
(
$fmtargs:ident
[
$(length($expected_length:expr))?
$((($len:expr, $kind:ident $args:tt), $fmt_override:tt, $reff:expr))*
]
[$(,)*]
) => ({
const __ADDED_UP_LEN_SDOFKE09F__: $crate::__::usize = 0 $( + $len )*;
const __LEN_SDOFKE09F__: $crate::__::usize =
$crate::__to_pvf_used_length!(__ADDED_UP_LEN_SDOFKE09F__, $($expected_length)?);
$(
const _: () =
$crate::__::assert_flatten_panicvals_length(
$expected_length,
__ADDED_UP_LEN_SDOFKE09F__,
);
)?
$crate::__::flatten_panicvals::<__LEN_SDOFKE09F__>(&[
$(
$crate::__to_pvf_kind!($fmtargs $kind $args, $fmt_override, $reff)
),*
])
});
// Had to add these workarounds
// to avoid getting stuck being parsed as a type in the `$ty:ty =>` branch.
($fmtargs:ident $prev:tt [$tt:tt, $($rem:tt)*]) => {
$crate::__to_pvf_expr!{
$fmtargs
$prev
(1, single())
[$tt, $($rem)*]
}
};
($fmtargs:ident $prev:tt [$tt0:tt $tt1:tt, $($rem:tt)*]) => {
$crate::__to_pvf_expr!{
$fmtargs
$prev
(1, single())
[$tt0 $tt1, $($rem)*]
}
};
($fmtargs:ident $prev:tt [$ty:ty => $($rem:tt)*]) => {
$crate::__to_pvf_expr!{
$fmtargs
$prev
(<$ty as $crate::__::PanicFmt>::PV_COUNT, many($ty))
[$($rem)*]
}
};
($fmtargs:ident $prev:tt [$($rem:tt)*]) => {
$crate::__to_pvf_expr!{
$fmtargs
$prev
(1, single())
[$($rem)*]
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __to_pvf_expr {
($fmtargs:ident [$($prev:tt)*] $other:tt [$kw:tt: $reff:expr, $($rem:tt)*]) => {
$crate::__to_pvf_inner!{
$fmtargs
[$($prev)* ($other, $kw, $reff)]
[$($rem)*]
}
};
($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:literal, $($rem:tt)*])=>{
$crate::__to_pvf_inner!{
$fmtargs
[$($prev)* ($other, display, $reff)]
[$($rem)*]
}
};
($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:expr, $($rem:tt)*]) => {
$crate::__to_pvf_inner!{
$fmtargs
[$($prev)* ($other, _, $reff)]
[$($rem)*]
}
};
($fmtargs:ident [$($prev:tt)*] $other:tt [$($rem:tt)*]) => {
$crate::__::compile_error!(concat!(
"expected expression, found:",
stringify!($($rem)*)
))
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __to_pvf_kind {
($fmtargs:ident single (), $fmt_override:tt, $reff:tt) => {
&match &$reff {
reff => [$crate::__::PanicFmt::PROOF
.infer(reff)
.coerce(reff)
.to_panicval($crate::__set_fmt_from_kw!($fmt_override, $fmtargs))],
}
};
($fmtargs:ident many ($ty:ty), $fmt_override:tt, $reff:tt) => {
$crate::__::panicvals_id::<{ <$ty as $crate::__::PanicFmt>::PV_COUNT }>(&match &$reff {
reff => <$ty as $crate::__::PanicFmt>::PROOF
.coerce(reff)
.to_panicvals($crate::__set_fmt_from_kw!($fmt_override, $fmtargs)),
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __to_pvf_used_length {
( $added_up:expr, $expected_length:expr ) => {
$crate::utils::max_usize($added_up, $expected_length)
};
( $added_up:expr, ) => {
$added_up
};
}
/// Helper macro for defining and using a `macro_rules!` macro inline.
///
/// The reason this was defined is to work around a limitation in stable const-eval,
/// where you can't call methods on generic types.
///
/// # Example
///
/// Implementing panic formatting for a generic struct with [`flatten_panicvals`].
///
/// ```rust
/// use const_panic::{
/// fmt::{self, ComputePvCount, FmtArg},
/// ArrayString, PanicFmt, PanicVal, flatten_panicvals, inline_macro,
/// };
///
/// // Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG; Foo(10, 20)),
/// "Foo(10, 20)"
/// );
///
/// // Alternate-Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DEBUG; Foo(false, true)),
/// concat!(
/// "Foo(\n",
/// " false,\n",
/// " true,\n",
/// ")",
/// ),
/// );
///
/// // Display formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DISPLAY; Foo("hmm", "what?")),
/// "Foo(hmm, what?)"
/// );
///
///
///
///
/// struct Foo(T, T);
///
/// impl PanicFmt for Foo
/// where
/// T: PanicFmt,
/// {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// const PV_COUNT: usize = ComputePvCount{
/// field_amount: 2,
/// summed_pv_count: T::PV_COUNT * 2,
/// delimiter: fmt::TypeDelim::Tupled,
/// }.call();
/// }
///
/// // Because of limitations of stable const evaluation,
/// // this macro only implements panic formatting for
/// // - `Foo`
/// // - `Foo`
/// // - `Foo<&str>`
/// inline_macro!{
/// (bool), (u8), (&str);
/// ($T:ty)=>
/// impl Foo<$T> {
/// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::<$T>::PV_COUNT] {
/// flatten_panicvals! {fmtarg;
/// "Foo",
/// open: fmt::OpenParen,
/// $T => self.0, fmt::COMMA_SEP,
/// $T => self.1, fmt::COMMA_TERM,
/// close: fmt::CloseParen,
/// }
/// }
/// }
/// }
///
/// ```
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[macro_export]
macro_rules! inline_macro{
(
$(($($args:tt)*)),* $(,)?;
($($params:tt)*)
=>
$($code:tt)*
) => {
macro_rules! __dummy__ {
($($params)*) => {$($code)*}
}
$(
__dummy__!{ $($args)* }
)*
}
}