Macro const_panic::flatten_panicvals [−][src]
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 potentially 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;
$(
$($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.
$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. -
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!{
impl Foo;
struct Foo(u32, &'static str)
}
struct Bar {
x: &'static str,
}
const_panic::impl_panicfmt!{
impl Bar;
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,
},
}
}
}
}