Macro const_panic::flatten_panicvals
source · [−]macro_rules! flatten_panicvals {
($fmtargs:expr $(, $length:expr)?; $($args:tt)* ) => { ... };
}
non_basic
only.Expand description
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
flatten_panicvals!(
$fmtarg:expr $(, $pv_count:expr)?;
$(
$($Type:ty => )? $($format_override:tt :)? $arg_to_fmt:expr
),*
$()?
)
$fmtarg
is a 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
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
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:
const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; <Foo as PanicFmt>::PV_COUNT]
const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_>
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:
error: expected type, found `foo`
Formatting
Literals are Display formatted by default, so that you can pass string literals without worrying about what the current formatting settings are.
Expressions are formatted as determined by the $fmtarg
argument.
Note that literals inside parentheses (eg: (100)
) are considered expressions
by this macro.
Formatting overrides
You can override how an argument is formatted by prefixing the argument expression with any of the options below:
-
debug:
or{?}:
:Debug
formats the argument. -
alt_debug:
or{#?}:
: alternate-Debug
formats the argument. -
display:
or{}:
:Display
formats the argument. -
alt_display:
or{#}:
: alternate-Display
formats the argument. -
bin:
or{b}:
:Debug
formats the argument, with binary-formatted numbers. -
alt_bin:
or{#b}:
: alternate-Debug
formats the argument, with binary-formatted numbers. -
hex:
or{X}:
:Debug
formats the argument, with hexadecimal-formatted numbers. -
alt_hex:
or{#X}:
: alternate-Debug
formats the argument, with hexadecimal-formatted numbers. -
open
: increments$fmtarg
’s indentation byfmt::INDENTATION_STEP
before formatting the argument, and uses Display formatting for that argument. -
close
: decrements$fmtarg
’s indentation byfmt::INDENTATION_STEP
before formatting the argument, and uses Display formatting for that argument.
String formatting
String expressions are debug-formatted like this:
- Prepending and appending the double quote character (
"
). - Escaping the
'\t'
,'\n'
,'\r'
,'\\'
,'\''
, and'\"'
characters. - Escaping control characters with
\xYY
, whereYY
is the hexadecimal value of the control character.
Examples
Struct and Enum Formatting examples below.
Basic
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.
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
+ <u8>::PV_COUNT
+ <Bar>::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: <bool>::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.
use const_panic::{
fmt::{self, FmtArg, PanicFmt, ComputePvCount},
ArrayString, PanicVal,
flatten_panicvals,
};
fn main() {
let up: Qux<u8> = Qux::Up;
// Debug formatting the Up variant
assert_eq!(
ArrayString::<100>::from_panicvals(&up.to_panicvals(FmtArg::DEBUG)).unwrap(),
"Up",
);
let down: Qux<u16> = 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<u32> = 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<T> {
Up,
Down { x: T, y: T, z: T },
Left(u64),
}
impl<T: PanicFmt> PanicFmt for Qux<T> {
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: <T>::PV_COUNT * 3,
delimiter: fmt::TypeDelim::Braced,
}.call(),
ComputePvCount{
field_amount: 1,
summed_pv_count: <u64>::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<u8>`
// - `Qux<u16>`
// - `Qux<u32>`
const_panic::inline_macro! {
(u8),
(u16),
(u32);
($T:ty) =>
impl Qux<$T> {
pub const fn to_panicvals(
&self,
fmtarg: FmtArg,
) -> [PanicVal<'static>; <Qux<$T>>::PV_COUNT] {
match self {
Self::Up =>
// The `<Qux<$T>>::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, <Qux<$T>>::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, <Qux<$T>>::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, <Qux<$T>>::PV_COUNT;
"Left",
open: fmt::OpenParen,
x, fmt::COMMA_TERM,
close: fmt::CloseParen,
},
}
}
}
}