Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

293
vendor/const_panic/src/array_string.rs vendored Normal file
View File

@@ -0,0 +1,293 @@
use crate::{
utils::{bytes_up_to, RangedBytes},
FmtArg, PanicFmt, PanicVal,
};
use core::{
cmp::PartialEq,
fmt::{self, Debug},
};
/// For precomputing a panic message.
///
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[derive(Copy, Clone)]
pub struct ArrayString<const CAP: usize> {
pub(crate) len: u32,
pub(crate) buffer: [u8; CAP],
}
/// Equivalent of `ArrayString` which can only be up to 255 bytes long.
///
/// This stores the length as a `u8`, while `ArrayString` stores it as a `u32`,
/// making this 3 bytes smaller and 1-aligned (while ArrayString is aligned to a `u32`).
#[derive(Copy, Clone)]
pub(crate) struct TinyString<const CAP: usize> {
len: u8,
buffer: [u8; CAP],
}
const fn add_up_lengths(mut strings: &[&str]) -> usize {
let mut len = 0;
while let [x, rem @ ..] = strings {
len += x.len();
strings = rem;
}
len
}
impl<const CAP: usize> ArrayString<CAP> {
/// Constructs an `ArrayString` from a `&str`
///
/// # Panics
///
/// Panics if `string` is larger than `CAP`.
///
/// # Example
///
/// ```rust
/// use const_panic::ArrayString;
///
/// assert_eq!(ArrayString::<16>::new("Hello, world!"), "Hello, world!");
/// ```
pub const fn new(string: &str) -> Self {
Self::concat(&[string])
}
/// Constructs an `ArrayString` by concatenating zero or more `&str`s
///
/// # Panics
///
/// Panics if the concatenated string would be longer than `CAP`.
///
/// # Example
///
/// ```rust
/// use const_panic::ArrayString;
///
/// assert_eq!(
/// ArrayString::<99>::concat(&["This ", "is ", "a string"]),
/// "This is a string"
/// );
/// ```
pub const fn concat(strings: &[&str]) -> Self {
let mut len = 0u32;
let mut buffer = [0u8; CAP];
let mut mstrings = strings;
while let [string, ref rem @ ..] = *mstrings {
mstrings = rem;
let mut bytes = string.as_bytes();
while let [x, ref rem @ ..] = *bytes {
if len == u32::MAX || len as usize >= CAP {
crate::concat_panic(&[&[
PanicVal::write_str("The input strings were longer than "),
PanicVal::from_usize(CAP, FmtArg::DISPLAY),
PanicVal::write_str(", concatenated length: "),
PanicVal::from_usize(add_up_lengths(strings), FmtArg::DISPLAY),
PanicVal::write_str(", strings: "),
PanicVal::from_slice_str(strings, FmtArg::DEBUG),
]])
}
bytes = rem;
buffer[len as usize] = x;
len += 1;
}
}
Self { len, buffer }
}
/// Constructs this string from a `&[&[PanicVal<'_>]]`.
///
/// Returns `None` if the formatted args would be larger than `CAP`.
///
/// # Example
///
/// ```rust
/// use const_panic::{ArrayString, FmtArg, flatten_panicvals};
///
/// assert_eq!(
/// ArrayString::<17>::concat_panicvals(&[
/// &flatten_panicvals!(FmtArg::DEBUG; 1u8, ("hello")),
/// &flatten_panicvals!(FmtArg::DEBUG; &[3u8, 5, 8]),
/// ]).unwrap(),
/// "1\"hello\"[3, 5, 8]",
/// );
///
/// assert!(
/// ArrayString::<16>::concat_panicvals(&[
/// &flatten_panicvals!(FmtArg::DEBUG; 1u8, ("hello")),
/// &flatten_panicvals!(FmtArg::DEBUG; &[3u8, 5, 8]),
/// ]).is_none(),
/// );
///
/// ```
///
pub const fn concat_panicvals(args: &[&[PanicVal<'_>]]) -> Option<Self> {
match crate::concat_panic_::make_panic_string::<CAP>(args) {
Ok(x) => Some(x),
Err(_) => None,
}
}
/// Constructs this string from a `&[PanicVal<'_>]`.
///
/// Returns `None` if the formatted args would be larger than `CAP`.
///
/// # Example
///
/// ```rust
/// use const_panic::{ArrayString, FmtArg, flatten_panicvals};
///
/// assert_eq!(
/// ArrayString::<8>::from_panicvals(
/// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello")
/// ).unwrap(),
/// "100hello",
/// );
///
/// // trying to format panicvals into too small an ArrayString
/// assert!(
/// ArrayString::<7>::from_panicvals(
/// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello")
/// ).is_none(),
/// );
///
/// ```
pub const fn from_panicvals(args: &[PanicVal<'_>]) -> Option<Self> {
Self::concat_panicvals(&[args])
}
/// How long the string is in bytes.
///
/// # Example
///
/// ```rust
/// use const_panic::ArrayString;
///
/// assert_eq!(ArrayString::<16>::new("foo").len(), 3);
/// assert_eq!(ArrayString::<16>::new("foo bar").len(), 7);
/// assert_eq!(ArrayString::<16>::new("Hello, world!").len(), 13);
/// ```
pub const fn len(&self) -> usize {
self.len as usize
}
/// Accesses the string as a byte slice.
///
/// # Performance
///
/// When the "rust_1_64" feature is disabled,
/// this takes a linear amount of time to run, proportional to `CAP - self.len()`.
///
/// When the "rust_1_64" feature is enabled,
/// this takes a constant amount of time to run.
///
/// # Example
///
/// ```rust
/// use const_panic::ArrayString;
///
/// assert_eq!(ArrayString::<16>::new("foo").as_bytes(), b"foo");
/// assert_eq!(ArrayString::<16>::new("foo bar").as_bytes(), b"foo bar");
/// assert_eq!(ArrayString::<16>::new("Hello, world!").as_bytes(), b"Hello, world!");
/// ```
pub const fn as_bytes(&self) -> &[u8] {
bytes_up_to(&self.buffer, self.len())
}
/// Gets the string.
///
/// # Performance
///
/// This takes a linear amount of time to run.
///
/// # Example
///
/// ```rust
/// use const_panic::ArrayString;
///
/// assert_eq!(ArrayString::<16>::new("foo").to_str(), "foo");
/// assert_eq!(ArrayString::<16>::new("foo bar").to_str(), "foo bar");
/// assert_eq!(ArrayString::<16>::new("Hello, world!").to_str(), "Hello, world!");
/// ```
pub const fn to_str(&self) -> &str {
#[cfg(not(feature = "rust_1_64"))]
{
// safety: make_panic_string delegates formatting to the `write_to_buffer` macro,
// which is tested as producing valid utf8.
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
#[cfg(feature = "rust_1_64")]
match core::str::from_utf8(self.as_bytes()) {
Ok(x) => x,
Err(_) => panic!("INTERNAL BUG: the string isn't valid utf-8"),
}
}
/// Creates a single element `PanicVal` borrowing from this string.
pub const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; 1] {
[PanicVal::from_str(self.to_str(), f)]
}
/// Creates a `PanicVal` borrowing from this string.
pub const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_> {
PanicVal::from_str(self.to_str(), f)
}
}
impl<const CAP: usize> ArrayString<CAP> {
pub(crate) const fn to_compact(self) -> TinyString<CAP> {
if self.len() <= 255 {
TinyString {
len: self.len as u8,
buffer: self.buffer,
}
} else {
crate::concat_panic(&[&[
PanicVal::write_str(
"The input string is too long, expected `length <= 255`, found length: ",
),
PanicVal::from_usize(self.len(), FmtArg::DISPLAY),
]])
}
}
}
impl<const CAP: usize> Debug for ArrayString<CAP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self.to_str(), f)
}
}
impl<const CAP: usize> PartialEq<str> for ArrayString<CAP> {
fn eq(&self, str: &str) -> bool {
self.to_str() == str
}
}
impl<const CAP: usize> PartialEq<&str> for ArrayString<CAP> {
fn eq(&self, str: &&str) -> bool {
self == *str
}
}
impl<const CAP: usize> PanicFmt for ArrayString<CAP> {
type This = Self;
type Kind = crate::fmt::IsCustomType;
const PV_COUNT: usize = 1;
}
////////////////////////////////////////////////////////////////////////////////
impl<const CAP: usize> TinyString<CAP> {
pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> {
RangedBytes {
start: 0,
end: self.len as usize,
bytes: &self.buffer,
}
}
}

360
vendor/const_panic/src/concat_panic_.rs vendored Normal file
View File

@@ -0,0 +1,360 @@
use crate::{
fmt::FmtKind,
panic_val::{PanicClass, PanicVal, StrFmt},
utils::{bytes_up_to, string_cap, WasTruncated},
};
/// Panics by concatenating the argument slice.
///
/// This is the function that the [`concat_panic`](macro@concat_panic) macro calls to panic.
///
/// # Example
///
/// Here's how to panic with formatting without using any macros:
///
/// ```compile_fail
/// use const_panic::{FmtArg, PanicVal, concat_panic};
///
/// const _: () = concat_panic(&[&[
/// PanicVal::write_str("\nthe error was "),
/// PanicVal::from_u8(100, FmtArg::DISPLAY),
/// PanicVal::write_str(" and "),
/// PanicVal::from_str("\nHello\tworld", FmtArg::DEBUG),
/// ]]);
///
///
/// ```
/// That fails to compile with this error message:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/concat_panic_.rs:13:15
/// |
/// 6 | const _: () = concat_panic(&[&[
/// | _______________^
/// 7 | | PanicVal::write_str("\nthe error was "),
/// 8 | | PanicVal::from_u8(100, FmtArg::DISPLAY),
/// 9 | | PanicVal::write_str(" and "),
/// 10 | | PanicVal::from_str("\nHello\tworld", FmtArg::DEBUG),
/// 11 | | ]]);
/// | |___^ the evaluated program panicked at '
/// the error was 100 and "\nHello\tworld"', src/concat_panic_.rs:6:15
/// ```
///
#[cold]
#[inline(never)]
#[track_caller]
pub const fn concat_panic(args: &[&[PanicVal<'_>]]) -> ! {
// The panic message capacity starts small and gets larger each time,
// so that platforms with smaller stacks can call this at runtime.
//
// Also, given that most(?) panic messages are smaller than 1024 bytes long,
// it's not going to be any less efficient in the common case.
if let Err(_) = panic_inner::<(), 1024>(args) {}
if let Err(_) = panic_inner::<(), { 1024 * 6 }>(args) {}
match panic_inner::<_, MAX_PANIC_MSG_LEN>(args) {
Ok(x) => x,
Err(_) => panic!(
"\
unreachable:\n\
the `write_panicval_to_buffer` macro must not return Err when \
$capacity == $max_capacity\
"
),
}
}
/// The maximum length of panic messages (in bytes),
/// after which the message is truncated.
// this should probably be smaller on platforms where this
// const fn is called at runtime, and the stack is finy.
pub const MAX_PANIC_MSG_LEN: usize = 32768;
// writes a single PanicVal to an array
macro_rules! write_panicval {
(
$outer_label:lifetime,
$mout:ident, $lout:ident, $tct:expr,
(
$len:expr,
$capacity:expr,
$max_capacity:expr,
$not_enough_space:expr,
$write_buffer:ident,
$write_buffer_checked:ident,
)
) => {
let rem_space = $capacity - $len;
let (strfmt, class, was_truncated) = $tct;
let StrFmt {
leftpad: mut lpad,
rightpad: mut rpad,
fmt_kind,
} = strfmt;
let ranged = match class {
PanicClass::PreFmt(str) => str,
PanicClass::Int(int) => {
if int.len() <= string_cap::MEDIUM {
$mout = int.fmt::<{ string_cap::MEDIUM }>();
$mout.ranged()
} else {
$lout = int.fmt::<{ string_cap::LARGE }>();
$lout.ranged()
}
}
#[cfg(feature = "non_basic")]
PanicClass::Slice(_) => unreachable!(),
};
let trunc_end = ranged.start + was_truncated.get_length(ranged.len());
while lpad != 0 {
$write_buffer! {b' '}
lpad -= 1;
}
if let FmtKind::Display = fmt_kind {
let mut i = ranged.start;
while i < trunc_end {
$write_buffer! {ranged.bytes[i]}
i += 1;
}
} else if rem_space != 0 {
$write_buffer! {b'"'}
let mut i = 0;
while i < trunc_end {
use crate::debug_str_fmt::{hex_as_ascii, ForEscaping};
let c = ranged.bytes[i];
let mut written_c = c;
if ForEscaping::is_escaped(c) {
$write_buffer! {b'\\'}
if ForEscaping::is_backslash_escaped(c) {
written_c = ForEscaping::get_backslash_escape(c);
} else {
$write_buffer! {b'x'}
$write_buffer! {hex_as_ascii(c >> 4)}
written_c = hex_as_ascii(c & 0b1111);
};
}
$write_buffer! {written_c}
i += 1;
}
if let WasTruncated::No = was_truncated {
$write_buffer_checked! {b'"'}
}
}
while rpad != 0 {
$write_buffer! {b' '}
rpad -= 1;
}
if let WasTruncated::Yes(_) = was_truncated {
if $capacity < $max_capacity {
return $not_enough_space;
} else {
break $outer_label;
}
}
};
}
macro_rules! write_to_buffer_inner {
(
$args:ident
(
$len:expr,
$capacity:expr,
$($_rem:tt)*
)
$wptb_args:tt
) => {
let mut args = $args;
let mut mout;
let mut lout;
'outer: while let [mut outer, ref nargs @ ..] = args {
while let [arg, nouter @ ..] = outer {
let tct = arg.to_class_truncated($capacity - $len);
match tct.1 {
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
PanicClass::Slice(slice) => {
let mut iter = slice.iter();
'iter: loop {
let (two_args, niter) = iter.next();
let mut two_args: &[_] = &two_args;
while let [arg, ntwo_args @ ..] = two_args {
let tct = arg.to_class_truncated($capacity - $len);
write_panicval! {'outer, mout, lout, tct, $wptb_args}
two_args = ntwo_args;
}
match niter {
Some(x) => iter = x,
None => break 'iter,
}
}
}
_ => {
write_panicval! {'outer, mout, lout, tct, $wptb_args}
}
}
outer = nouter;
}
args = nargs;
}
};
}
macro_rules! write_to_buffer {
($args:ident $wptb_args:tt) => {
write_to_buffer_inner! {
$args
$wptb_args
$wptb_args
}
};
}
macro_rules! make_buffer_writer_macros {
($buffer:ident, $len:ident) => {
macro_rules! write_buffer {
($value:expr) => {
__write_array! {$buffer, $len, $value}
};
}
macro_rules! write_buffer_checked {
($value:expr) => {
__write_array_checked! {$buffer, $len, $value}
};
}
};
}
#[cold]
#[inline(never)]
#[track_caller]
const fn panic_inner<T, const LEN: usize>(args: &[&[PanicVal<'_>]]) -> Result<T, NotEnoughSpace> {
let mut buffer = [0u8; LEN];
let mut len = 0usize;
make_buffer_writer_macros! {buffer, len}
write_to_buffer! {
args
(
len, LEN, MAX_PANIC_MSG_LEN, Err(NotEnoughSpace),
write_buffer, write_buffer_checked,
)
}
unsafe {
let buffer = bytes_up_to(&buffer, len);
let str = core::str::from_utf8_unchecked(buffer);
panic!("{}", str)
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct NotEnoughSpace;
#[cfg(feature = "test")]
use crate::test_utils::TestString;
#[doc(hidden)]
#[cfg(feature = "test")]
pub fn format_panic_message<const LEN: usize>(
args: &[&[PanicVal<'_>]],
capacity: usize,
max_capacity: usize,
) -> Result<TestString<LEN>, NotEnoughSpace> {
let mut buffer = [0u8; LEN];
let mut len = 0usize;
{
// intentionally shadowed
let buffer = &mut buffer[..capacity];
make_buffer_writer_macros! {buffer, len}
write_to_buffer! {
args
(
len, capacity, max_capacity, Err(NotEnoughSpace),
write_buffer, write_buffer_checked,
)
}
}
Ok(TestString { buffer, len })
}
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[doc(hidden)]
pub(crate) const fn make_panic_string<const LEN: usize>(
args: &[&[PanicVal<'_>]],
) -> Result<crate::ArrayString<LEN>, NotEnoughSpace> {
let mut buffer = [0u8; LEN];
let mut len = 0usize;
make_buffer_writer_macros! {buffer, len}
write_to_buffer! {
args
(len, LEN, LEN + 1, Err(NotEnoughSpace), write_buffer, write_buffer_checked,)
}
assert!(len as u32 as usize == len, "the panic message is too large");
Ok(crate::ArrayString {
buffer,
len: len as u32,
})
}
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[doc(hidden)]
#[track_caller]
pub const fn make_panic_string_unwrapped<const LEN: usize>(
args: &[&[PanicVal<'_>]],
) -> crate::ArrayString<LEN> {
match make_panic_string(args) {
Ok(x) => x,
Err(_) => panic!("arguments are too large to fit in LEN"),
}
}
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[doc(hidden)]
pub const fn compute_length(args: &[&[PanicVal<'_>]]) -> usize {
let mut len = 0usize;
macro_rules! add_to_len {
($value:expr) => {{
let _: u8 = $value;
len += 1;
}};
}
write_to_buffer! {
args
(
len, usize::MAX - 1, usize::MAX, usize::MAX,
add_to_len, add_to_len,
)
}
len
}

42
vendor/const_panic/src/const_default.rs vendored Normal file
View File

@@ -0,0 +1,42 @@
/// Default values for const parameters
///
/// This trait is sealed so that it can be potentially replaced with
/// an external `ConstDefault` trait when a potential future feature is enabled.
#[doc(hidden)]
pub trait ConstDefault: sealed::Sealed {
const DEFAULT: Self;
}
mod sealed {
pub trait Sealed {}
}
macro_rules! impl_constdefault {
($($ty:ty = $val:expr),* $(,)?) => (
$(
impl sealed::Sealed for $ty {}
impl ConstDefault for $ty {
const DEFAULT: Self = $val;
}
)*
)
}
impl_constdefault! {
u8 = 0,
u16 = 0,
u32 = 0,
u64 = 0,
u128 = 0,
usize = 0,
i8 = 0,
i16 = 0,
i32 = 0,
i64 = 0,
i128 = 0,
isize = 0,
bool = false,
char = '\0',
&str = "",
}

95
vendor/const_panic/src/debug_str_fmt.rs vendored Normal file
View File

@@ -0,0 +1,95 @@
// most of this copied from const_format, which is mine, but still..
#[doc(hidden)]
pub(crate) struct ForEscaping {
pub(crate) is_escaped: u128,
pub(crate) is_backslash_escaped: u128,
pub(crate) escape_char: [u8; 16],
}
impl ForEscaping {
/// Gets the backslash escape for a character that is kwown to be escaped with a backslash.
#[inline(always)]
pub(crate) const fn get_backslash_escape(b: u8) -> u8 {
FOR_ESCAPING.escape_char[(b & 0b1111) as usize]
}
// how long this byte inside a utf8 string takes to represent in debug formatting.
pub(crate) const fn byte_len(c: u8) -> usize {
if c < 128 {
let shifted = 1 << c;
if (FOR_ESCAPING.is_escaped & shifted) != 0 {
if (FOR_ESCAPING.is_backslash_escaped & shifted) != 0 {
2
} else {
4
}
} else {
1
}
} else {
1
}
}
pub(crate) const fn is_escaped(c: u8) -> bool {
(c < 128) && ((FOR_ESCAPING.is_escaped & (1 << c)) != 0)
}
pub(crate) const fn is_backslash_escaped(c: u8) -> bool {
(c < 128) && ((FOR_ESCAPING.is_backslash_escaped & (1 << c)) != 0)
}
}
#[doc(hidden)]
/// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F'
#[inline(always)]
pub(crate) const fn hex_as_ascii(n: u8) -> u8 {
if n < 10 {
n + b'0'
} else {
n - 10 + b'A'
}
}
#[doc(hidden)]
pub(crate) const FOR_ESCAPING: &ForEscaping = {
let mut is_backslash_escaped = 0;
let escaped = [
(b'\t', b't'),
(b'\n', b'n'),
(b'\r', b'r'),
(b'\'', b'\''),
(b'"', b'"'),
(b'\\', b'\\'),
];
// Using the fact that the characters above all have different bit patterns for
// the lowest 4 bits.
let mut escape_char = [0u8; 16];
let escaped_len = escaped.len();
let mut i = 0;
while i < escaped_len {
let (code, escape) = escaped[i];
is_backslash_escaped |= 1 << code;
let ei = (code & 0b1111) as usize;
if escape_char[ei] != 0 {
panic!("Oh no, some escaped character uses the same 4 lower bits as another")
}
escape_char[ei] = escape;
i += 1;
}
// Setting all the control characters as being escaped.
let is_escaped = is_backslash_escaped | 0xFFFF_FFFF;
&ForEscaping {
escape_char,
is_backslash_escaped,
is_escaped,
}
};

51
vendor/const_panic/src/doc_macros.rs vendored Normal file
View File

@@ -0,0 +1,51 @@
macro_rules! formatting_docs {($($additional_fmt_overrides:expr)?) => {
concat!(r##"
# 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.
"##,
$($additional_fmt_overrides,)?
r##"
### 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`,
where `YY` is the hexadecimal value of the control character.
"##
)}}
macro_rules! limitation_docs {() => {
"
Arguments to the formatting/panicking macros must have a fully inferred concrete type,
because `const_panic` macros use duck typing to call methods on those arguments.
One effect of that limitation is that you will have to pass suffixed
integer literals (eg: `100u8`) when those integers aren't inferred to be a concrete type.
"
}}
pub(crate) use limitation_docs;

22
vendor/const_panic/src/doctests.rs vendored Normal file
View File

@@ -0,0 +1,22 @@
/// ###################################################
///
/// Ensure that phantom type parameters must be ignored
///
/// ```compile_fail
/// struct Foo<T>(std::marker::PhantomData<T>);
///
/// const_panic::impl_panicfmt!{
/// struct Foo<T>(std::marker::PhantomData<T>);
/// }
/// ```
///
/// ```rust
/// struct Foo<T>(std::marker::PhantomData<T>);
///
/// const_panic::impl_panicfmt!{
/// struct Foo<ignore T>(std::marker::PhantomData<T>);
/// }
/// ```
///
///
pub struct ImplPanicFmt;

417
vendor/const_panic/src/fmt.rs vendored Normal file
View File

@@ -0,0 +1,417 @@
//! Formatting-related items
//!
//! Panic formatting for custom types can be done in these ways
//! (in increasing order of verbosity):
//! - Using the [`PanicFmt` derive] macro
//! (requires the opt-in `"derive"` feature)
//! - Using the [`impl_panicfmt`] macro
//! (requires the default-enabled `"non_basic"` feature)
//! - Using the [`flatten_panicvals`] macro
//! (requires the default-enabled `"non_basic"` feature)
//! - Manually implementing the [`PanicFmt`] trait as described in its docs.
//!
//! [`PanicFmt` derive]: derive@crate::PanicFmt
//! [`PanicFmt`]: trait@crate::fmt::PanicFmt
//! [`impl_panicfmt`]: crate::impl_panicfmt
//! [`flatten_panicvals`]: crate::flatten_panicvals
#[cfg(feature = "non_basic")]
mod non_basic_fmt;
#[cfg(feature = "non_basic")]
mod fmt_compressed;
pub mod char_formatting;
#[cfg(feature = "non_basic")]
pub use self::{fmt_compressed::PackedFmtArg, non_basic_fmt::*};
use crate::wrapper::StdWrapper;
use core::marker::PhantomData;
use typewit::{Identity, TypeEq};
/// Trait for types that can be formatted by const panics.
///
/// # Implementor
///
/// Implementors are expected to also define this inherent method to format the type:
/// ```rust
/// # use const_panic::fmt::{FmtArg, IsCustomType, PanicFmt};
/// # use const_panic::PanicVal;
/// # struct Foo;
/// # impl Foo {
/// const fn to_panicvals<'a>(&'a self, f: FmtArg) -> [PanicVal<'a>; <Self as PanicFmt>::PV_COUNT]
/// # { loop{} }
/// # }
/// # impl PanicFmt for Foo {
/// # type This = Self;
/// # type Kind = IsCustomType;
/// # const PV_COUNT: usize = 1;
/// # }
/// ```
/// The returned [`PanicVal`](crate::PanicVal) can also be `PanicVal<'static>`.
///
/// # Implementation examples
///
/// This trait can be implemented in these ways (in increasing order of verbosity):
/// - Using the [`PanicFmt` derive] macro
/// (requires the opt-in `"derive"` feature)
/// - Using the [`impl_panicfmt`](impl_panicfmt#examples) macro
/// (requires the default-enabled `"non_basic"` feature)
/// - Using the [`flatten_panicvals`](flatten_panicvals#examples) macro
/// (requires the default-enabled `"non_basic"` feature)
/// - Using no macros at all
///
/// ### Macro-less impl
///
/// Implementing this trait for a simple enum without using macros.
///
#[cfg_attr(feature = "non_basic", doc = "```rust")]
#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
/// use const_panic::{ArrayString, FmtArg, PanicFmt, PanicVal};
///
/// // `ArrayString` requires the "non_basic" crate feature (enabled by default),
/// // everything else in this example works with no enabled crate features.
/// assert_eq!(
/// ArrayString::<99>::concat_panicvals(&[
/// &Foo::Bar.to_panicvals(FmtArg::DEBUG),
/// &[PanicVal::write_str(",")],
/// &Foo::Baz.to_panicvals(FmtArg::DEBUG),
/// &[PanicVal::write_str(",")],
/// &Foo::Qux.to_panicvals(FmtArg::DEBUG),
/// ]).unwrap(),
/// "Bar,Baz,Qux",
/// );
///
///
/// enum Foo {
/// Bar,
/// Baz,
/// Qux,
/// }
///
/// impl PanicFmt for Foo {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
/// const PV_COUNT: usize = 1;
/// }
///
/// impl Foo {
/// pub const fn to_panicvals(self, _: FmtArg) -> [PanicVal<'static>; Foo::PV_COUNT] {
/// match self {
/// Self::Bar => [PanicVal::write_str("Bar")],
/// Self::Baz => [PanicVal::write_str("Baz")],
/// Self::Qux => [PanicVal::write_str("Qux")],
/// }
/// }
/// }
///
/// ```
/// [`PanicFmt` derive]: derive@crate::PanicFmt
/// [`PanicFmt`]: trait@crate::fmt::PanicFmt
/// [`impl_panicfmt`]: crate::impl_panicfmt
/// [`flatten_panicvals`]: crate::flatten_panicvals
pub trait PanicFmt {
/// The type after dereferencing all references.
///
/// User-defined types should generally set this to `Self`.
type This: ?Sized;
/// Whether this is a user-defined type or standard library type.
///
/// User-defined types should generally set this to [`IsCustomType`].
type Kind;
/// The length of the array returned in `Self::to_panicvals`
/// (an inherent method that formats the type for panic messages).
const PV_COUNT: usize;
/// A marker type that proves that `Self` implements `PanicFmt`.
///
/// Used by const_panic macros to coerce both standard library and
/// user-defined types into some type that has a `to_panicvals` method.
const PROOF: IsPanicFmt<Self, Self::This, Self::Kind> = IsPanicFmt::NEW;
}
impl<'a, T: PanicFmt + ?Sized> PanicFmt for &'a T {
type This = T::This;
type Kind = T::Kind;
const PV_COUNT: usize = T::PV_COUNT;
}
/// Marker type used as the [`PanicFmt::Kind`] associated type for std types.
pub struct IsStdType;
/// Marker type used as the [`PanicFmt::Kind`] for user-defined types.
pub struct IsCustomType;
/// A marker type that proves that `S` implements
/// [`PanicFmt<This = T, Kind = K>`](PanicFmt).
///
/// Used by const_panic macros to coerce both standard library and
/// user-defined types into some type that has a `to_panicvals` method.
///
pub struct IsPanicFmt<S: ?Sized, T: ?Sized, K> {
self_: PhantomData<fn() -> S>,
this: PhantomData<fn() -> T>,
kind: PhantomData<fn() -> K>,
_priv: (),
}
impl<T: PanicFmt + ?Sized> IsPanicFmt<T, T::This, T::Kind> {
/// Constucts an `IsPanicFmt`
pub const NEW: Self = Self {
self_: PhantomData,
this: PhantomData,
kind: PhantomData,
_priv: (),
};
}
impl<S: ?Sized, T: ?Sized, K> IsPanicFmt<S, T, K> {
/// Infers the `S` type parameter with the argument.
///
/// Because the only ways to construct `IsPanicFmt`
/// use `IsPanicFmt<S, S::This, S::Kind>`,
/// the other type parameters are inferred along with `S`.
pub const fn infer(self, _: &S) -> Self {
self
}
/// For coercing `&T` to `StdWrapper<&T>`.
pub const fn coerce<'a>(self, x: &'a T) -> CoerceReturnOutput<&'a T, K>
where
// hack to make this bound work in 1.57.0:
// K: CoerceReturn<&'a T>,
// (before trait bounds were officially supported)
<K as Identity>::Type: CoerceReturn<&'a T>,
{
match <K as CoerceReturn<&'a T>>::__COERCE_TO_WITNESS {
__CoerceToWitness::IsStdType(te) => te.to_left(StdWrapper(x)),
__CoerceToWitness::IsCustomType(te) => te.to_left(x),
}
}
}
impl<S: ?Sized, T: ?Sized, K> Copy for IsPanicFmt<S, T, K> {}
impl<S: ?Sized, T: ?Sized, K> Clone for IsPanicFmt<S, T, K> {
fn clone(&self) -> Self {
*self
}
}
/////////////////////////////////////////////////////////////////////
/// Computes the type that the `T` argument is converted into by [`IsPanicFmt::coerce`].
pub type CoerceReturnOutput<T, K> = <K as CoerceReturn<T>>::CoerceTo;
/// Computes the type that the `T` argument is converted into by [`IsPanicFmt::coerce`].
///
/// This trait is sealed, it's implemented by [`IsStdType`] and [`IsCustomType`],
/// and cannot be implemented by any other type.
pub trait CoerceReturn<T>: Sized {
/// The type that the `T` argument is converted into.
type CoerceTo;
#[doc(hidden)]
const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self>;
}
impl<T> CoerceReturn<T> for IsStdType {
type CoerceTo = StdWrapper<T>;
#[doc(hidden)]
const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self> =
__CoerceToWitness::IsStdType(TypeEq::NEW);
}
impl<T> CoerceReturn<T> for IsCustomType {
type CoerceTo = T;
#[doc(hidden)]
const __COERCE_TO_WITNESS: __CoerceToWitness<T, Self> =
__CoerceToWitness::IsCustomType(TypeEq::NEW);
}
#[doc(hidden)]
pub enum __CoerceToWitness<T, K: CoerceReturn<T>> {
#[non_exhaustive]
IsStdType(TypeEq<<K as CoerceReturn<T>>::CoerceTo, StdWrapper<T>>),
#[non_exhaustive]
IsCustomType(TypeEq<<K as CoerceReturn<T>>::CoerceTo, T>),
}
/////////////////////////////////////////////////////////////////////
/// Carries all of the configuration for formatting functions.
///
/// # Example
///
#[cfg_attr(feature = "non_basic", doc = "```rust")]
#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
/// use const_panic::{ArrayString, FmtArg, StdWrapper};
///
/// // `StdWrapper` wraps references to std types to provide their `to_panicvals` methods
/// const ARRAY: &[&str] = &["3", "foo\nbar", "\0qux"];
///
/// // Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG; ARRAY),
/// r#"["3", "foo\nbar", "\x00qux"]"#
/// );
///
/// // Alternate-Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DEBUG; ARRAY),
/// concat!(
/// "[\n",
/// " \"3\",\n",
/// " \"foo\\nbar\",\n",
/// " \"\\x00qux\",\n",
/// "]",
/// )
/// );
///
/// // Display formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DISPLAY; ARRAY),
/// "[3, foo\nbar, \x00qux]"
/// );
///
/// // Alternate-Display formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DISPLAY; ARRAY),
/// concat!(
/// "[\n",
/// " 3,\n",
/// " foo\n",
/// "bar,\n",
/// " \x00qux,\n",
/// "]",
/// )
/// );
///
/// ```
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct FmtArg {
/// How much indentation is needed for a field/array element.
///
/// Indentation is used by [`fmt::Delimiter`](crate::fmt::Delimiter)
/// and by [`fmt::Separator`](crate::fmt::Separator),
/// when the [`is_alternate` field](#structfield.is_alternate) flag is enabled.
pub indentation: u8,
/// Whether alternate formatting is being used.
pub is_alternate: bool,
/// Whether this is intended to be `Display` or `Debug` formatted.
pub fmt_kind: FmtKind,
/// What integers are formatted as: decimal, hexadecimal, or binary.
pub number_fmt: NumberFmt,
}
impl FmtArg {
/// A `FmtArg` with no indentation and `Display` formatting.
pub const DISPLAY: Self = Self {
indentation: 0,
fmt_kind: FmtKind::Display,
is_alternate: false,
number_fmt: NumberFmt::Decimal,
};
/// A `FmtArg` with alternate `Display` formatting, starting with no indentation.
pub const ALT_DISPLAY: Self = Self::DISPLAY.set_alternate(true);
/// A `FmtArg` with `Debug` formatting and no indentation.
pub const DEBUG: Self = Self::DISPLAY.set_debug();
/// A `FmtArg` with alternate `Debug` formatting, starting with no indentation.
pub const ALT_DEBUG: Self = Self::DEBUG.set_alternate(true);
/// A `FmtArg` with `Debug` and `Binary` formatting and no indentation.
pub const BIN: Self = Self::DISPLAY.set_bin();
/// A `FmtArg` with alternate `Debug` and `Binary` formatting,
/// starting with no indentation.
pub const ALT_BIN: Self = Self::BIN.set_alternate(true);
/// A `FmtArg` with `Debug` and `Hexadecimal` formatting and no indentation.
pub const HEX: Self = Self::DISPLAY.set_hex();
/// A `FmtArg` with alternate `Debug` and `Hexadecimal` formatting,
/// starting with no indentation.
pub const ALT_HEX: Self = Self::HEX.set_alternate(true);
/// Sets whether alternate formatting is enabled
pub const fn set_alternate(mut self, is_alternate: bool) -> Self {
self.is_alternate = is_alternate;
self
}
/// Changes the formatting to `Display`.
pub const fn set_display(mut self) -> Self {
self.fmt_kind = FmtKind::Display;
self
}
/// Changes the formatting to `Debug`.
pub const fn set_debug(mut self) -> Self {
self.fmt_kind = FmtKind::Debug;
self
}
/// Changes the formatting to `Debug`, and number formatting to `Hexadecimal`.
pub const fn set_hex(mut self) -> Self {
self.fmt_kind = FmtKind::Debug;
self.number_fmt = NumberFmt::Hexadecimal;
self
}
/// Changes the formatting to `Debug`, and number formatting to `Binary`.
pub const fn set_bin(mut self) -> Self {
self.fmt_kind = FmtKind::Debug;
self.number_fmt = NumberFmt::Binary;
self
}
}
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
impl FmtArg {
/// Increments the indentation by [`INDENTATION_STEP`] spaces.
pub const fn indent(mut self) -> Self {
self.indentation += INDENTATION_STEP;
self
}
/// Decrement the indentation by [`INDENTATION_STEP`] spaces.
pub const fn unindent(mut self) -> Self {
self.indentation = self.indentation.saturating_sub(INDENTATION_STEP);
self
}
}
////////////////////////////////////////////////////////////////////////////////
/// What kind of formatting to do, either `Display` or `Debug`.
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FmtKind {
/// `Debug` formatting
Debug = 0,
/// `Display` formatting
Display = 1,
}
////////////////////////////////////////////////////////////////////////////////
/// What integers are formatted as.
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum NumberFmt {
/// Formatted as decimal.
Decimal = 0,
/// Formatted as binary, eg: `101`, `0b110`.
Binary = 1,
/// Formatted as hexadecimal, eg: `FAD`, `0xDE`.
Hexadecimal = 2,
}

View File

@@ -0,0 +1,146 @@
//! `char`-formatted related items
use crate::{
fmt::{FmtArg, FmtKind},
fmt_impls::basic_fmt_impls::primitive_static_panicfmt,
panic_val::{PanicVal, PanicVariant},
utils::{string_cap, PreFmtString, StartAndBytes},
};
#[cfg(all(test, not(miri)))]
mod tests;
impl PanicVal<'_> {
/// Constructs a `PanicVal` from a `char`.
pub const fn from_char(c: char, fmtarg: FmtArg) -> Self {
let StartAndBytes { start, bytes } = match fmtarg.fmt_kind {
FmtKind::Display => {
let (arr, len) = char_to_utf8(c);
crate::utils::tail_byte_array::<{ string_cap::PREFMT }>(len, &arr)
}
FmtKind::Debug => {
let fmtchar = char_to_debug(c);
crate::utils::tail_byte_array(fmtchar.len(), &fmtchar.encoded)
}
};
// SAFETY:
// char_to_utf8 is exhaustively tested in the tests module.
// char_to_debug is exhaustively tested in the tests module.
// tail_byte_array is also tested for smaller/equal/larger input arrays.
let prefmt = unsafe { PreFmtString::new(start, bytes) };
PanicVal {
var: PanicVariant::PreFmt(prefmt),
}
}
}
primitive_static_panicfmt! {
fn[](&self: char, fmtarg) {
PanicVal::from_char(*self.0, fmtarg)
}
}
/// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F'
#[inline]
const fn hex_as_ascii(n: u8) -> u8 {
if n < 10 {
n + b'0'
} else {
n - 10 + b'A'
}
}
#[cfg(test)]
pub(crate) const fn char_debug_len(c: char) -> usize {
let inner = match c {
'\t' | '\r' | '\n' | '\\' | '\'' => 2,
'\x00'..='\x1F' => 4,
_ => c.len_utf8(),
};
inner + 2
}
const fn char_to_utf8(char: char) -> ([u8; 4], usize) {
let u32 = char as u32;
match u32 {
0..=127 => ([u32 as u8, 0, 0, 0], 1),
0x80..=0x7FF => {
let b0 = 0b1100_0000 | (u32 >> 6) as u8;
let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
([b0, b1, 0, 0], 2)
}
0x800..=0xFFFF => {
let b0 = 0b1110_0000 | (u32 >> 12) as u8;
let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
([b0, b1, b2, 0], 3)
}
0x10000..=u32::MAX => {
let b0 = 0b1111_0000 | (u32 >> 18) as u8;
let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8;
let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8;
let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8;
([b0, b1, b2, b3], 4)
}
}
}
/// Display formats a `char`
pub const fn char_to_display(char: char) -> FmtChar {
let ([b0, b1, b2, b3], len) = char_to_utf8(char);
FmtChar {
encoded: [b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0, 0],
len: len as u8,
}
}
/// Debug formats a `char`
pub const fn char_to_debug(c: char) -> FmtChar {
let ([b0, b1, b2, b3], len) = match c {
'\t' => (*br#"\t "#, 2),
'\r' => (*br#"\r "#, 2),
'\n' => (*br#"\n "#, 2),
'\\' => (*br#"\\ "#, 2),
'\'' => (*br#"\' "#, 2),
'\"' => (*br#"" "#, 1),
'\x00'..='\x1F' => {
let n = c as u8;
(
[b'\\', b'x', hex_as_ascii(n >> 4), hex_as_ascii(n & 0b1111)],
4,
)
}
_ => char_to_utf8(c),
};
let mut encoded = [b'\'', b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0];
encoded[len + 1] = b'\'';
FmtChar {
encoded,
len: (len as u8) + 2,
}
}
/// An byte slice with a display/debug formatted `char`.
///
/// To get the encoded character, you need to do
/// `&fmt_char.encoded()[..fmt_char.len()]`.
#[derive(Copy, Clone)]
pub struct FmtChar {
encoded: [u8; 12],
len: u8,
}
impl FmtChar {
/// Array which contains the display/debug-formatted `char`,
/// and trailing `0` padding.
pub const fn encoded(&self) -> &[u8; 12] {
&self.encoded
}
/// The length of the subslice that contains the formatted character.
pub const fn len(&self) -> usize {
self.len as usize
}
}

View File

@@ -0,0 +1,93 @@
use super::{char_debug_len, char_to_debug, char_to_display, FmtChar};
fn as_bytes(fmt: &FmtChar) -> &[u8] {
&fmt.encoded()[..fmt.len()]
}
#[test]
fn char_to_utf8_encoding_test() {
for c in '\0'..=core::char::MAX {
let mut utf8_std = [0u8; 4];
let utf8_std = c.encode_utf8(&mut utf8_std);
let utf8_here = char_to_display(c);
assert_eq!(utf8_std.as_bytes(), as_bytes(&utf8_here));
}
}
#[test]
fn char_to_utf8_display_test() {
for c in '\0'..=core::char::MAX {
let mut utf8_std = [0u8; 4];
let utf8_std = c.encode_utf8(&mut utf8_std);
let utf8_here = char_to_display(c);
assert_eq!(utf8_std.as_bytes(), as_bytes(&utf8_here));
}
}
#[test]
fn char_to_utf8_debug_test() {
let first_escapes = [
('\x00', r#"'\x00'"#),
('\x01', r#"'\x01'"#),
('\x02', r#"'\x02'"#),
('\x03', r#"'\x03'"#),
('\x04', r#"'\x04'"#),
('\x05', r#"'\x05'"#),
('\x06', r#"'\x06'"#),
('\x07', r#"'\x07'"#),
('\x08', r#"'\x08'"#),
('\t', r#"'\t'"#),
('\n', r#"'\n'"#),
('\x0B', r#"'\x0B'"#),
('\x0C', r#"'\x0C'"#),
('\r', r#"'\r'"#),
('\x0E', r#"'\x0E'"#),
('\x0F', r#"'\x0F'"#),
('\x10', r#"'\x10'"#),
('\x11', r#"'\x11'"#),
('\x12', r#"'\x12'"#),
('\x13', r#"'\x13'"#),
('\x14', r#"'\x14'"#),
('\x15', r#"'\x15'"#),
('\x16', r#"'\x16'"#),
('\x17', r#"'\x17'"#),
('\x18', r#"'\x18'"#),
('\x19', r#"'\x19'"#),
('\x1A', r#"'\x1A'"#),
('\x1B', r#"'\x1B'"#),
('\x1C', r#"'\x1C'"#),
('\x1D', r#"'\x1D'"#),
('\x1E', r#"'\x1E'"#),
('\x1F', r#"'\x1F'"#),
];
for (c, expected) in first_escapes.iter().copied() {
let utf8_here = char_to_debug(c);
assert_eq!(expected.as_bytes(), as_bytes(&utf8_here), "{:?}", c);
assert_eq!(expected.len(), char_debug_len(c), "{:?}", c);
}
let other_escapes = [('\'', r#"'\''"#), ('\"', r#"'"'"#), ('\\', r#"'\\'"#)];
let mut buffer = arrayvec::ArrayString::<12>::new();
for c in '\x20'..=core::char::MAX {
let utf8_here = char_to_debug(c);
if let Some((_, expected)) = Some(c)
.filter(|c| *c <= '\x7F')
.and_then(|c| other_escapes.iter().copied().find(|x| x.0 == c))
{
assert_eq!(expected.as_bytes(), as_bytes(&utf8_here), "{:?}", c);
assert_eq!(expected.len(), char_debug_len(c), "{:?}", c);
} else {
buffer.clear();
buffer.push('\'');
buffer.push(c);
buffer.push('\'');
assert_eq!(buffer.as_bytes(), as_bytes(&utf8_here), "{:?}", c);
assert_eq!(buffer.len(), char_debug_len(c), "{:?}", c);
}
}
}

View File

@@ -0,0 +1,96 @@
use crate::fmt::{FmtArg, FmtKind, NumberFmt};
/// A version of FmtArg which occupies less space, but needs to be unpacked to be used.
#[derive(Copy, Clone)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub struct PackedFmtArg {
indentation: u8,
bitfields: u8,
}
const FMT_KIND_OFFSET: u8 = 1;
const NUMBER_FMT_OFFSET: u8 = FMT_KIND_OFFSET + FmtKind::BITS;
impl FmtArg {
/// Converts this `FmtArg` into a `PackedFmtArg`,
/// which is smaller but can only be converted back into a `FmtArg`.
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub const fn pack(self) -> PackedFmtArg {
let Self {
indentation,
is_alternate,
fmt_kind,
number_fmt,
} = self;
PackedFmtArg {
indentation,
bitfields: is_alternate as u8
| ((fmt_kind as u8) << FMT_KIND_OFFSET)
| ((number_fmt as u8) << NUMBER_FMT_OFFSET),
}
}
}
impl PackedFmtArg {
/// Converts this `PackedFmtArg` back into a `FmtArg`.
pub const fn unpack(self) -> FmtArg {
let indentation = self.indentation;
let is_alternate = (self.bitfields & 1) != 0;
let fmt_kind = FmtKind::from_prim(self.bitfields >> FMT_KIND_OFFSET);
let number_fmt = NumberFmt::from_prim(self.bitfields >> NUMBER_FMT_OFFSET);
FmtArg {
indentation,
is_alternate,
fmt_kind,
number_fmt,
}
}
}
macro_rules! enum_prim {
(
$type:ident, $bits:expr;
default $default:ident,
$($variant:ident),*
$(,)?
) => (
#[allow(non_upper_case_globals)]
const _: () = {
$(const $variant: u8 = $type::$variant as u8;)*
const __MASK: u8 = 2u8.pow($bits) - 1;
match $type::$default {
$($type::$variant => (),)*
$type::$default => (),
}
impl $type {
#[allow(dead_code)]
const BITS: u8 = $bits;
const fn from_prim(n: u8) -> Self {
match n & __MASK {
$($variant => Self::$variant,)*
_ => Self::$default,
}
}
}
};
)
}
use enum_prim;
enum_prim! {
FmtKind, 2;
default Debug,
Display,
}
enum_prim! {
NumberFmt, 2;
default Decimal,
Binary,
Hexadecimal,
}

View File

@@ -0,0 +1,551 @@
use crate::{ArrayString, PanicVal};
use super::{FmtArg, IsCustomType, PanicFmt};
/// For outputting an [alternate flag]-aware delimiter.
///
/// This was created for formatting structs and enum variants,
/// so these delimiters have spaces around them to follow the <br>
/// `Foo { bar: baz }`, `Foo(bar)`, and `[foo, bar]` style.
///
/// # Example
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg},
/// ArrayString,
/// flatten_panicvals,
/// };
///
/// // Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG;
/// open: fmt::EmptyDelimiter,
/// 100u8, fmt::COMMA_SEP,
/// false, fmt::COMMA_SEP,
/// [0u16; 0], fmt::COMMA_SEP,
/// // parenthesizing to pass this as a non-literal
/// // otherwise the string is Display formatted
/// ("really"), fmt::COMMA_TERM,
/// close: "",
/// ),
/// " 100, false, [], \"really\""
/// );
///
///
/// // Alternate-Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DEBUG;
/// open: fmt::EmptyDelimiter,
/// 100u8, fmt::COMMA_SEP,
/// false, fmt::COMMA_SEP,
/// [0u16; 0], fmt::COMMA_SEP,
/// ("really"), fmt::COMMA_TERM,
/// close: "",
/// ),
/// concat!(
/// "\n",
/// " 100,\n",
/// " false,\n",
/// " [],\n",
/// " \"really\",\n",
/// )
/// );
///
/// ```
///
/// [alternate flag]: crate::FmtArg#structfield.is_alternate
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub enum Delimiter {
/// `(`
OpenParen,
/// `)`
CloseParen,
/// `[`
OpenBracket,
/// `]`
CloseBracket,
/// ` { `
OpenBrace,
/// ` }`
CloseBrace,
/// An empty delimiter,
/// exists only to add whitespace on the next line when
/// the alternate flag is enabled.
Empty,
}
pub use self::Delimiter::{
CloseBrace, CloseBracket, CloseParen, Empty as EmptyDelimiter, OpenBrace, OpenBracket,
OpenParen,
};
impl Delimiter {
/// Converts this `Delimiter` into an array of one `PanicVal`
///
/// When the [alternate flag] is enabled, this and the `to_panicval` method output:
/// - the delimiter
/// - a newline
/// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces
///
/// When the [alternate flag] is disabled,
/// these methods output braces with spaces around them,
/// the empty delimiter as one space,
/// and the remaining delimiters with no spaces around them.
///
/// [alternate flag]: crate::FmtArg#structfield.is_alternate
///
pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] {
[self.to_panicval(f)]
}
/// Converts this `Delimiter` into a `PanicVal`
pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> {
match (self, f.is_alternate) {
(Self::OpenParen, false) => PanicVal::write_str("("),
(Self::CloseParen, false) => PanicVal::write_str(")"),
(Self::OpenBracket, false) => PanicVal::write_str("["),
(Self::CloseBracket, false) => PanicVal::write_str("]"),
(Self::OpenBrace, false) => PanicVal::write_str(" { "),
(Self::CloseBrace, false) => PanicVal::write_str(" }"),
(Self::Empty, false) => PanicVal::write_str(" "),
(Self::OpenParen, true) => PanicVal::write_str("(\n").with_rightpad(f),
(Self::CloseParen, true) => PanicVal::write_str(")").with_leftpad(f),
(Self::OpenBracket, true) => PanicVal::write_str("[\n").with_rightpad(f),
(Self::CloseBracket, true) => PanicVal::write_str("]").with_leftpad(f),
(Self::OpenBrace, true) => PanicVal::write_str(" {\n").with_rightpad(f),
(Self::CloseBrace, true) => PanicVal::write_str("}").with_leftpad(f),
(Self::Empty, true) => PanicVal::write_str("\n").with_rightpad(f),
}
}
}
impl PanicFmt for Delimiter {
type This = Self;
type Kind = IsCustomType;
const PV_COUNT: usize = 1;
}
////////////////////////////////////////////////////////////////////////////////
/// How much indentation (in spaces) is added with [`FmtArg::indent`],
/// and removed with [`FmtArg::unindent`].
///
/// [The FmtArg.indentation field](crate::FmtArg#structfield.indentation)
/// is used by [`fmt::Delimiter`](crate::fmt::Delimiter)
/// and by [`fmt::Separator`](crate::fmt::Separator),
/// when the [`is_alternate`](crate::FmtArg#structfield.is_alternate) flag is enabled.
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub const INDENTATION_STEP: u8 = 4;
////////////////////////////////////////////////////////////////////////////////
/// A stack allocated string type that's convertible into
/// [`PanicVal<'static>`](PanicVal),
/// with [`SHORT_STRING_CAP`] capacity.
///
/// # Example
///
/// ```rust
/// use const_panic::{
/// fmt::ShortString,
/// ArrayString, FmtArg, PanicVal,
/// };
///
/// let pv: PanicVal<'static> =
/// PanicVal::write_short_str(ShortString::new("3,13,21,34,55,89"));
///
/// assert_eq!(ArrayString::<20>::from_panicvals(&[pv]).unwrap(), "3,13,21,34,55,89");
///
///
/// let pv_debug: PanicVal<'static> =
/// PanicVal::from_short_str(ShortString::new("foo\n\0bar"), FmtArg::DEBUG);
///
/// assert_eq!(
/// ArrayString::<20>::from_panicvals(&[pv_debug]).unwrap(),
/// "\"foo\\n\\x00bar\"",
/// );
///
/// ```
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub type ShortString = ArrayString<SHORT_STRING_CAP>;
pub use crate::utils::string_cap::TINY as SHORT_STRING_CAP;
impl<'a> PanicVal<'a> {
/// Constructs a `PanicVal` from a `ShortString`.
pub const fn from_short_str(this: ShortString, f: FmtArg) -> PanicVal<'a> {
use crate::panic_val::{PanicVariant, StrFmt};
PanicVal::__new(PanicVariant::ShortString(StrFmt::new(f), this.to_compact()))
}
}
////////////////////////////////////////////////////////////////////////////////
/// For computing the [`PanicFmt::PV_COUNT`] of a struct or enum variant,
/// with the [`call`](ComputePvCount::call) method.
///
/// This assumes that you write the `to_panicvals` method like in the
/// [`flatten_panicvals` examples](crate::flatten_panicvals#examples)
///
/// # Examples
///
/// These examples demonstrates how to use this type by itself,
/// for a more complete example you can look at the
/// [`flatten_panicvals` examples](crate::flatten_panicvals#examples)
///
/// ### Struct
///
/// ```rust
/// use const_panic::{ComputePvCount, PanicFmt};
///
/// struct Foo<'a> {
/// x: u32,
/// y: &'a [&'a str],
/// z: Bar,
/// }
/// # type Bar = u8;
///
/// impl PanicFmt for Foo<'_> {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// const PV_COUNT: usize = ComputePvCount{
/// field_amount: 3,
/// summed_pv_count:
/// u32::PV_COUNT +
/// <&[&str]>::PV_COUNT +
/// <Bar>::PV_COUNT,
/// delimiter: const_panic::TypeDelim::Braced,
/// }.call();
/// }
///
/// assert_eq!(Foo::PV_COUNT, 12);
///
/// ```
///
/// ### Enum
///
/// ```rust
/// use const_panic::{ComputePvCount, PanicFmt};
///
/// enum Foo {
/// Bar,
/// Baz(u32, u64),
/// }
/// # type Bar = u8;
///
/// impl PanicFmt for Foo {
/// type This = Self;
/// type Kind = const_panic::IsCustomType;
///
/// const PV_COUNT: usize = const_panic::utils::slice_max_usize(&[
/// ComputePvCount{
/// field_amount: 0,
/// summed_pv_count: 0,
/// delimiter: const_panic::TypeDelim::Braced,
/// }.call(),
/// ComputePvCount{
/// field_amount: 2,
/// summed_pv_count: <u32>::PV_COUNT + <u64>::PV_COUNT,
/// delimiter: const_panic::TypeDelim::Tupled,
/// }.call(),
/// ]);
/// }
///
/// assert_eq!(Foo::PV_COUNT, 7);
///
/// ```
///
///
///
///
///
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub struct ComputePvCount {
/// The amount of fields in the struct
pub field_amount: usize,
/// The summed up amount of `PanicVal`s that all the fields produce.
///
/// Eg: for a struct with `Bar` and `Qux` fields, this would be
/// `<Bar as PanicFmt>::PV_COUNT + <Qux as PanicFmt>::PV_COUNT`,
///
pub summed_pv_count: usize,
/// Whether it's a tupled or braced struct/variant.
pub delimiter: TypeDelim,
}
impl ComputePvCount {
/// Does the computation.
pub const fn call(&self) -> usize {
// field-less structs and variants don't output the empty delimiter
if self.field_amount == 0 {
return 1;
}
const TYPE_NAME: usize = 1;
const DELIM_TOKENS: usize = 2;
let field_tokens = match self.delimiter {
TypeDelim::Tupled => self.field_amount,
TypeDelim::Braced => 2 * self.field_amount,
};
TYPE_NAME + DELIM_TOKENS + field_tokens + self.summed_pv_count
}
}
/// Whether a struct or variant is Tupled or Braced.
///
/// Unit structs/variants are considered braced.
///
/// # Example
///
/// ### Formatting
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg, TypeDelim},
/// ArrayString,
/// flatten_panicvals,
/// };
///
/// {
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG;
/// "Foo",
/// open: TypeDelim::Tupled.open(),
/// 10u8, fmt::COMMA_SEP,
/// false, fmt::COMMA_TERM,
/// close: TypeDelim::Tupled.close(),
/// ),
/// "Foo(10, false)"
/// );
/// }
///
/// {
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG;
/// "Bar",
/// open: TypeDelim::Braced.open(),
/// "x: ", debug: "hello", fmt::COMMA_SEP,
/// "y: ", true, fmt::COMMA_TERM,
/// close: TypeDelim::Braced.close(),
/// ),
/// "Bar { x: \"hello\", y: true }"
/// );
/// }
///
/// ```
///
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub enum TypeDelim {
/// A `Foo(Bar)` type or variant
Tupled,
/// A `Foo{bar: Baz}` type or variant
Braced,
}
impl TypeDelim {
/// Gets the delimiters that surround the fields of a struct or variant.
pub const fn get_open_and_close(self) -> (Delimiter, Delimiter) {
(self.open(), self.close())
}
/// Gets the group delimiter that precedes the fields of a struct or variant.
pub const fn open(self) -> Delimiter {
match self {
Self::Tupled => Delimiter::OpenParen,
Self::Braced => Delimiter::OpenBrace,
}
}
/// Gets the group delimiter that follows the fields of a struct or variant.
pub const fn close(self) -> Delimiter {
match self {
Self::Tupled => Delimiter::CloseParen,
Self::Braced => Delimiter::CloseBrace,
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// An [alternate-flag-aware] comma separator for use between fields or elements.
///
/// When the alternate flag is enabled, this puts each field/element on its own line.
///
/// # Example
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg},
/// ArrayString,
/// flatten_panicvals,
/// };
///
/// // Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG;
/// open: fmt::OpenBracket,
/// 100u8, fmt::COMMA_SEP,
/// false, fmt::COMMA_SEP,
/// [0u16; 0], fmt::COMMA_SEP,
/// // fmt::COMMA_TERM always goes after the last field
/// debug: "really", fmt::COMMA_TERM,
/// close: fmt::CloseBracket,
/// ),
/// "[100, false, [], \"really\"]"
/// );
///
///
/// // Alternate-Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DEBUG;
/// open: fmt::OpenBracket,
/// 100u8, fmt::COMMA_SEP,
/// false, fmt::COMMA_SEP,
/// [0u16; 0], fmt::COMMA_SEP,
/// // fmt::COMMA_TERM always goes after the last field
/// debug: "really", fmt::COMMA_TERM,
/// close: fmt::CloseBracket,
/// ),
/// concat!(
/// "[\n",
/// " 100,\n",
/// " false,\n",
/// " [],\n",
/// " \"really\",\n",
/// "]",
/// )
/// );
///
/// ```
///
/// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub const COMMA_SEP: Separator<'_> = Separator::new(",", IsLast::No);
/// An [alternate-flag-aware] comma for use after the last field or element.
///
/// For an example, you can look at [the one for `COMMA_SEP`](COMMA_SEP#example)
///
/// When the alternate flag is enabled, this puts each field/element on its own line.
///
/// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub const COMMA_TERM: Separator<'_> = Separator::new(",", IsLast::Yes);
/// For telling [`Separator`] whether it comes after the last field or not.
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub enum IsLast {
///
Yes,
///
No,
}
/// For [alternate flag]-aware separation of fields, collection elements, etc.
///
/// # Example
///
/// ```rust
/// use const_panic::{
/// fmt::{self, FmtArg, IsLast, Separator},
/// ArrayString,
/// flatten_panicvals,
/// };
///
/// const SEMICOLON_SEP: Separator = Separator::new(";", IsLast::No);
/// const SEMICOLON_TERM: Separator = Separator::new(";", IsLast::Yes);
///
/// // Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::DEBUG;
/// open: fmt::OpenBrace,
/// debug: "foo", SEMICOLON_SEP,
/// [3u8, 5, 8], SEMICOLON_SEP,
/// false, SEMICOLON_TERM,
/// close: fmt::CloseBrace,
/// ),
/// // the space before the brace is because Delimiter is intended for
/// // formatting structs and enum variants.
/// " { \"foo\"; [3, 5, 8]; false }"
/// );
///
///
/// // Alternate-Debug formatting
/// assert_eq!(
/// const_panic::concat_!(FmtArg::ALT_DEBUG;
/// open: fmt::OpenBrace,
/// debug: "foo", SEMICOLON_SEP,
/// debug: [3u8, 5, 8], SEMICOLON_SEP,
/// false, SEMICOLON_TERM,
/// close: fmt::CloseBrace,
/// ),
/// concat!(
/// " {\n",
/// " \"foo\";\n",
/// " [3, 5, 8];\n",
/// " false;\n",
/// "}",
/// )
/// );
/// ```
///
/// [alternate flag]: crate::FmtArg#structfield.is_alternate
#[derive(Copy, Clone)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub struct Separator<'a>(&'a str, IsLast);
impl<'a> Separator<'a> {
/// Constructs a `Separator` from a custom separator.
///
/// # Panics
///
/// Panics if `string` is longer than 12 bytes.
///
pub const fn new(string: &'a str, is_last_field: IsLast) -> Self {
if string.len() > 12 {
crate::concat_panic(&[&[
PanicVal::write_str("expected a string shorter than 12 bytes,"),
PanicVal::write_str("actual length: "),
PanicVal::from_usize(string.len(), FmtArg::DISPLAY),
PanicVal::write_str(", string: "),
PanicVal::from_str(string, FmtArg::DEBUG),
]])
}
Separator(string, is_last_field)
}
/// Converts this `Separator` into an array of one `PanicVal`.
/// Otherwise does the same as [`to_panicval`](Self::to_panicval)
pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] {
[PanicVal::from_element_separator(self.0, self.1, f)]
}
/// Converts this `Separator` into a `PanicVal`.
///
/// When the [alternate flag] is enabled, this and the `to_panicvals` method output:
/// - the separator
/// - a newline
/// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces
/// if constructed with [`IsLast::No`]
///
/// When the [alternate flag] is disabled,
/// these methods output the separator and a single space
/// if constructed with [`IsLast::No`],
/// otherwise output nothing.
///
/// [alternate flag]: crate::FmtArg#structfield.is_alternate
pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> {
PanicVal::from_element_separator(self.0, self.1, f)
}
}
impl PanicFmt for Separator<'_> {
type This = Self;
type Kind = IsCustomType;
const PV_COUNT: usize = 1;
}

View File

@@ -0,0 +1,195 @@
use crate::{
panic_val::{IntVal, PanicVal, PanicVariant, StrFmt},
utils::Packed,
FmtArg, PanicFmt, StdWrapper,
};
macro_rules! primitive_static_panicfmt {
(
fn[$($impl:tt)*](&$self:ident: $ty:ty, $f:ident) {
$($content:tt)*
}
) => {
impl<$($impl)*> crate::PanicFmt for $ty {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
impl<$($impl)*> crate::StdWrapper<&$ty> {
#[doc = concat!(
"Converts this `", stringify!($ty), "` to a single-element `PanicVal` array."
)]
pub const fn to_panicvals($self, $f: crate::FmtArg) -> [PanicVal<'static>; 1] {
[{
$($content)*
}]
}
#[doc = concat!(
"Converts this `", stringify!($ty), "` to a `PanicVal`."
)]
pub const fn to_panicval($self, $f: crate::FmtArg) -> PanicVal<'static> {
$($content)*
}
}
}
}
pub(crate) use primitive_static_panicfmt;
macro_rules! impl_panicfmt_panicval_array {
(
PV_COUNT = $pv_len:expr;
fn[$($impl:tt)*](&$self:ident: $ty:ty) -> $ret:ty {
$($content:tt)*
}
) => (
impl<$($impl)*> PanicFmt for $ty {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = $pv_len;
}
impl<'s, $($impl)*> StdWrapper<&'s $ty> {
///
pub const fn to_panicvals($self: Self, _: FmtArg) -> $ret {
$($content)*
}
}
)
}
macro_rules! impl_panicfmt_panicarg {
(
fn $panic_arg_ctor:ident[$($impl:tt)*](
$this:ident:
$ty:ty,
$f:ident
) -> PanicVal<$pa_lt:lifetime>
$panic_args:block
)=>{
impl PanicVal<'_> {
#[doc = concat!("Constructs a `PanicVal` from a `", stringify!($ty), "` .")]
pub const fn $panic_arg_ctor<$($impl)*>($this: $ty, $f: FmtArg) -> PanicVal<$pa_lt>
$panic_args
}
impl<$($impl)*> PanicFmt for $ty {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
impl<'s, $($impl)*> StdWrapper<&'s $ty> {
#[doc = concat!(
"Formats this `", stringify!($ty),
"` into a single-`PanicVal` array",
)]
pub const fn to_panicvals(self: Self, f: FmtArg) -> [PanicVal<$pa_lt>;1] {
[PanicVal::$panic_arg_ctor(*self.0, f)]
}
#[doc = concat!("Formats this `", stringify!($ty), "` into a `PanicVal`")]
pub const fn to_panicval(self: Self, f: FmtArg) -> PanicVal<$pa_lt> {
PanicVal::$panic_arg_ctor(*self.0, f)
}
}
}
}
macro_rules! impl_panicfmt_int {
($panic_arg_ctor:ident, $intarg_contructor:ident, $ty:ty) => {
impl PanicVal<'_> {
/// Constructs this `PanicVal` from an integer.
pub const fn $panic_arg_ctor(this: $ty, f: FmtArg) -> PanicVal<'static> {
const BITS: u8 = core::mem::size_of::<$ty>() as u8 * 8;
IntVal::$intarg_contructor(this as _, BITS, f)
}
}
primitive_static_panicfmt! {
fn[](&self: $ty, f) {
PanicVal::$panic_arg_ctor(*self.0, f)
}
}
};
}
impl_panicfmt_int! {from_u8, from_u128, u8}
impl_panicfmt_int! {from_u16, from_u128, u16}
impl_panicfmt_int! {from_u32, from_u128, u32}
impl_panicfmt_int! {from_u64, from_u128, u64}
impl_panicfmt_int! {from_u128, from_u128, u128}
impl_panicfmt_int! {from_usize, from_u128, usize}
impl_panicfmt_int! {from_i8, from_i128, i8}
impl_panicfmt_int! {from_i16, from_i128, i16}
impl_panicfmt_int! {from_i32, from_i128, i32}
impl_panicfmt_int! {from_i64, from_i128, i64}
impl_panicfmt_int! {from_i128, from_i128, i128}
impl_panicfmt_int! {from_isize, from_i128, isize}
impl_panicfmt_panicarg! {
fn from_bool[](this: bool, _f) -> PanicVal<'static> {
PanicVal::write_str(if this { "true" } else { "false" })
}
}
impl<'a> PanicVal<'a> {
/// Constructs a `PanicVal` from a `&str`
pub const fn from_str(this: &'a str, f: FmtArg) -> PanicVal<'a> {
PanicVal::__new(PanicVariant::Str(StrFmt::new(f), Packed(this)))
}
}
impl PanicFmt for str {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
impl<'a> StdWrapper<&'a str> {
/// Formats this `&str` into a single-`PanicVal` array
pub const fn to_panicvals(self: Self, f: FmtArg) -> [PanicVal<'a>; 1] {
[PanicVal::from_str(self.0, f)]
}
/// Formats this `&str` into a `PanicVal`
pub const fn to_panicval(self: Self, f: FmtArg) -> PanicVal<'a> {
PanicVal::from_str(self.0, f)
}
}
impl_panicfmt_panicval_array! {
PV_COUNT = N;
fn['a, const N: usize](&self: [PanicVal<'a>; N]) -> &'s [PanicVal<'a>; N] {
self.0
}
}
impl_panicfmt_panicval_array! {
PV_COUNT = usize::MAX;
fn['a](&self: [PanicVal<'a>]) -> &'s [PanicVal<'a>] {
self.0
}
}
impl<'a, 'b> StdWrapper<&'a &'b [PanicVal<'b>]> {
/// Coerces a `&&[PanicVal<'_>]` into a `&[PanicVal<'_>]`
pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] {
*self.0
}
}
impl<'a, 'b, const N: usize> StdWrapper<&'a &'b [PanicVal<'b>; N]> {
/// Coerces a `&&[PanicVal<'_>; N]` into a `&[PanicVal<'_>]`
pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] {
*self.0
}
}
impl<'b, const N: usize> StdWrapper<&'b [PanicVal<'b>; N]> {
/// Coerces a `&[PanicVal<'_>; N]` into a `&[PanicVal<'_>]`
pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] {
self.0
}
}

View File

@@ -0,0 +1,87 @@
use crate::{FmtArg, PanicFmt, PanicVal, StdWrapper};
use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
macro_rules! impl_range_panicfmt_one {
(
fn(&$self:ident: $ty:ty, $f:ident) -> $pv_count:literal {
$($content:tt)*
}
) => {
impl PanicFmt for $ty {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = $pv_count;
}
impl crate::StdWrapper<&$ty> {
#[doc = concat!(
"Converts this `", stringify!($ty), "` to a single-element `PanicVal` array."
)]
pub const fn to_panicvals($self, $f: FmtArg) -> [PanicVal<'static>; $pv_count] {
$($content)*
}
}
}
}
macro_rules! impl_range_panicfmt {
($elem_ty:ty) => {
impl_range_panicfmt_one! {
fn(&self: Range<$elem_ty>, f) -> 3 {
[
StdWrapper(&self.0.start).to_panicval(f),
PanicVal::write_str(".."),
StdWrapper(&self.0.end).to_panicval(f),
]
}
}
impl_range_panicfmt_one! {
fn(&self: RangeFrom<$elem_ty>, f) -> 2 {
[
StdWrapper(&self.0.start).to_panicval(f),
PanicVal::write_str(".."),
]
}
}
impl_range_panicfmt_one! {
fn(&self: RangeTo<$elem_ty>, f) -> 2 {
[
PanicVal::write_str(".."),
StdWrapper(&self.0.end).to_panicval(f),
]
}
}
impl_range_panicfmt_one! {
fn(&self: RangeToInclusive<$elem_ty>, f) -> 2 {
[
PanicVal::write_str("..="),
StdWrapper(&self.0.end).to_panicval(f),
]
}
}
impl_range_panicfmt_one! {
fn(&self: RangeInclusive<$elem_ty>, f) -> 3 {
[
StdWrapper(self.0.start()).to_panicval(f),
PanicVal::write_str("..="),
StdWrapper(self.0.end()).to_panicval(f),
]
}
}
};
}
impl_range_panicfmt! {usize}
////////////////////////////////////////////////////////////////////////////////
impl_range_panicfmt_one! {
fn(&self: RangeFull, _f) -> 1 {
[PanicVal::write_str("..")]
}
}

View File

@@ -0,0 +1,37 @@
use crate::{FmtArg, PanicVal};
use core::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
macro_rules! nonzero_impls {
($(($int_ctor:ident, $ty:ty))*) => (
$(
primitive_static_panicfmt!{
fn[](&self: $ty, fmtarg) {
PanicVal::$int_ctor(self.0.get(), fmtarg)
}
}
)*
impl_for_option!{
$((for[], 'static, $ty, $ty))*
}
)
}
nonzero_impls! {
(from_u8, NonZeroU8)
(from_i8, NonZeroI8)
(from_u16, NonZeroU16)
(from_i16, NonZeroI16)
(from_u32, NonZeroU32)
(from_i32, NonZeroI32)
(from_u64, NonZeroU64)
(from_i64, NonZeroI64)
(from_u128, NonZeroU128)
(from_i128, NonZeroI128)
(from_usize, NonZeroUsize)
(from_isize, NonZeroIsize)
}

View File

@@ -0,0 +1,78 @@
use crate::{FmtArg, PanicFmt, PanicVal};
/// Note: there is only `to_panicvals` methods for `Option`s of standard library types
/// for now.
///
impl<T> PanicFmt for Option<T>
where
T: PanicFmt,
{
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 4 + T::PV_COUNT;
}
macro_rules! impl_for_option {
(
$((for[$($generics:tt)*],$lt:lifetime, $ty:ty, $unref:ty))*
) => (
$(
impl<'s, $($generics)*> crate::StdWrapper<&'s Option<$ty>> {
#[doc = concat!(
"Converts this `Option<",
stringify!($ty),
">` to a `PanicVal` array."
)]
pub const fn to_panicvals(
self: Self,
mut fmtarg: FmtArg,
) -> [PanicVal<$lt>; 5] {
use crate::{PanicVal, StdWrapper, __::EPV, fmt};
match self.0 {
Some(x) => [
PanicVal::write_str("Some"),
{fmtarg = fmtarg.indent(); fmt::OpenParen.to_panicval(fmtarg)},
StdWrapper::<&$unref>(x).to_panicval(fmtarg),
fmt::COMMA_TERM.to_panicval(fmtarg),
{fmtarg = fmtarg.unindent(); fmt::CloseParen.to_panicval(fmtarg)},
],
None => [PanicVal::write_str("None"), EPV, EPV, EPV, EPV],
}
}
}
)*
)
}
macro_rules! impl_for_option_outer {
(
$(($lt:lifetime, $ty:ty, $unref:ty))*
) => (
impl_for_option!{
$(
(for[], $lt, $ty, $unref)
(for[const N: usize], 's, [$ty; N], [$ty; N])
(for[const N: usize], 's, &'s [$ty; N], [$ty; N])
(for[], 's, &'s [$ty], [$ty])
)*
}
)
}
impl_for_option_outer! {
('static, bool, bool)
('static, u8, u8)
('static, u16, u16)
('static, u32, u32)
('static, u64, u64)
('static, u128, u128)
('static, i8, i8)
('static, i16, i16)
('static, i32, i32)
('static, i64, i64)
('static, i128, i128)
('static, isize, isize)
('static, usize, usize)
('s, &'s str, str)
}

View File

@@ -0,0 +1,84 @@
use crate::{FmtArg, PanicVal};
use core::{
marker::{PhantomData, PhantomPinned},
ptr::NonNull,
};
use core as std;
macro_rules! ptr_impls {
($ty:ty) => {
primitive_static_panicfmt! {
fn[T: ?Sized](&self: $ty, _f) {
PanicVal::write_str("<pointer>")
}
}
};
}
ptr_impls! {*const T}
ptr_impls! {*mut T}
ptr_impls! {NonNull<T>}
impl_for_option! {
(for[T], 'static, NonNull<T>, NonNull<T>)
}
primitive_static_panicfmt! {
fn[T: ?Sized](&self: PhantomData<T>, _f) {
PanicVal::write_str("PhantomData")
}
}
primitive_static_panicfmt! {
fn[](&self: PhantomPinned, _f) {
PanicVal::write_str("PhantomPinned")
}
}
primitive_static_panicfmt! {
fn[](&self: (), _f) {
PanicVal::write_str("()")
}
}
impl_for_option! {
(for[], 'static, core::cmp::Ordering, core::cmp::Ordering)
}
primitive_static_panicfmt! {
fn[](&self: std::cmp::Ordering, _f) {
let v = match self.0 {
std::cmp::Ordering::Less => "Less",
std::cmp::Ordering::Equal => "Equal",
std::cmp::Ordering::Greater => "Greater",
};
PanicVal::write_str(v)
}
}
primitive_static_panicfmt! {
fn[](&self: std::sync::atomic::Ordering, _f) {
use std::sync::atomic::Ordering;
let v = match self.0 {
Ordering::Relaxed => "Relaxed",
Ordering::Release => "Release",
Ordering::Acquire => "Acquire",
Ordering::AcqRel => "AcqRel",
Ordering::SeqCst => "SeqCst",
_ => "<std::sync::atomic::Ordering>",
};
PanicVal::write_str(v)
}
}
#[allow(unreachable_code)]
const _: () = {
primitive_static_panicfmt! {
fn[](&self: std::convert::Infallible, _f) {
match *self.0 {}
}
}
};

View File

@@ -0,0 +1,67 @@
use crate::{
fmt::{FmtArg, FmtKind, PanicFmt},
PanicVal, StdWrapper,
};
use core::str::Utf8Error;
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
impl PanicFmt for Utf8Error {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 5;
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
impl StdWrapper<&Utf8Error> {
/// Formats a `Utf8Error` (supports both Debug and Display formatting).
pub const fn to_panicvals(self, fmtarg: FmtArg) -> [PanicVal<'static>; Utf8Error::PV_COUNT] {
let this = *self.0;
match fmtarg.fmt_kind {
FmtKind::Display => {
let [pv0, pv1, pv2] = match this.error_len() {
Some(x) => [
PanicVal::write_str("invalid utf-8 sequence of "),
PanicVal::from_usize(x, fmtarg),
PanicVal::write_str(" bytes "),
],
None => [
PanicVal::write_str("incomplete utf-8 byte sequence "),
PanicVal::EMPTY,
PanicVal::EMPTY,
],
};
[
pv0,
pv1,
pv2,
PanicVal::write_str("from index "),
PanicVal::from_usize(this.valid_up_to(), fmtarg),
]
}
FmtKind::Debug => {
let [pv0, pv1, pv2] = match this.error_len() {
Some(x) => [
PanicVal::write_str(", error_len: Some("),
PanicVal::from_usize(x, fmtarg),
PanicVal::write_str(") }"),
],
None => [
PanicVal::write_str(", error_len: None }"),
PanicVal::EMPTY,
PanicVal::EMPTY,
],
};
[
PanicVal::write_str("Utf8Error { valid_up_to: "),
PanicVal::from_usize(this.valid_up_to(), fmtarg),
pv0,
pv1,
pv2,
]
}
}
}
}

View File

@@ -0,0 +1,80 @@
use crate::{
fmt::{FmtArg, FmtKind, PanicFmt},
PanicVal, StdWrapper,
};
use core::num::{IntErrorKind, ParseIntError};
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))]
impl PanicFmt for ParseIntError {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))]
impl StdWrapper<&ParseIntError> {
/// Formats a `ParseIntError` (supports both Debug and Display formatting).
pub const fn to_panicvals(
self,
fmtarg: FmtArg,
) -> [PanicVal<'static>; ParseIntError::PV_COUNT] {
[self.to_panicval(fmtarg)]
}
/// Formats a `ParseIntError` (supports both Debug and Display formatting).
pub const fn to_panicval(self, fmtarg: FmtArg) -> PanicVal<'static> {
macro_rules! debug_fmt {
($variant:ident) => {
concat!("ParseIntError { kind: ", stringify!($variant), " }")
};
}
let this = self.0;
match fmtarg.fmt_kind {
FmtKind::Display => PanicVal::write_str(match this.kind() {
IntErrorKind::Empty => "cannot parse integer from empty string",
IntErrorKind::InvalidDigit => "invalid digit found in string",
IntErrorKind::PosOverflow => "number too large to fit in target type",
IntErrorKind::NegOverflow => "number too small to fit in target type",
IntErrorKind::Zero => "number would be zero for non-zero type",
_ => "<ParseIntError>",
}),
FmtKind::Debug => PanicVal::write_str(match this.kind() {
IntErrorKind::Empty => debug_fmt!(Empty),
IntErrorKind::InvalidDigit => debug_fmt!(InvalidDigit),
IntErrorKind::PosOverflow => debug_fmt!(PosOverflow),
IntErrorKind::NegOverflow => debug_fmt!(NegOverflow),
IntErrorKind::Zero => debug_fmt!(Zero),
_ => "<IntErrorKind>",
}),
}
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))]
impl PanicFmt for IntErrorKind {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))]
impl StdWrapper<&IntErrorKind> {
/// Formats a `IntErrorKind` (supports only Debug formatting).
pub const fn to_panicvals(self, fmtarg: FmtArg) -> [PanicVal<'static>; IntErrorKind::PV_COUNT] {
[self.to_panicval(fmtarg)]
}
/// Formats a `IntErrorKind` (supports only Debug formatting).
pub const fn to_panicval(self, _: FmtArg) -> PanicVal<'static> {
PanicVal::write_str(match *self.0 {
IntErrorKind::Empty => "Empty",
IntErrorKind::InvalidDigit => "InvalidDigit",
IntErrorKind::PosOverflow => "PosOverflow",
IntErrorKind::NegOverflow => "NegOverflow",
IntErrorKind::Zero => "Zero",
_ => "<IntErrorKind>",
})
}
}

View File

@@ -0,0 +1,97 @@
use crate::{
fmt::{self as cfmt, ComputePvCount, FmtArg, FmtKind, PanicFmt},
PanicVal, StdWrapper,
};
use core::ffi::{FromBytesUntilNulError, FromBytesWithNulError};
#[cfg_attr(
feature = "docsrs",
doc(cfg(all(feature = "non_basic", feature = "rust_1_88")))
)]
impl PanicFmt for FromBytesWithNulError {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = ComputePvCount {
field_amount: 1,
summed_pv_count: <usize>::PV_COUNT,
delimiter: cfmt::TypeDelim::Braced,
}
.call();
}
#[cfg_attr(
feature = "docsrs",
doc(cfg(all(feature = "non_basic", feature = "rust_1_88")))
)]
impl StdWrapper<&FromBytesWithNulError> {
/// Formats a `FromBytesWithNulError`.
pub const fn to_panicvals(
self,
fmtarg: FmtArg,
) -> [PanicVal<'static>; FromBytesWithNulError::PV_COUNT] {
match (fmtarg.fmt_kind, *self.0) {
(FmtKind::Display, FromBytesWithNulError::InteriorNul { position }) => {
crate::utils::flatten_panicvals(&[&[
PanicVal::write_str("data provided contains an interior nul byte at byte pos "),
PanicVal::from_usize(position, fmtarg),
]])
}
(FmtKind::Display, FromBytesWithNulError::NotNulTerminated) => {
crate::utils::flatten_panicvals(&[&[PanicVal::write_str(
"data provided is not nul terminated",
)]])
}
(FmtKind::Debug, FromBytesWithNulError::InteriorNul { position }) => {
flatten_panicvals! {fmtarg;
"InteriorNul",
open: cfmt::OpenBrace,
"position: ", usize => position, cfmt::COMMA_TERM,
close: cfmt::CloseBrace,
}
}
(FmtKind::Debug, FromBytesWithNulError::NotNulTerminated) => {
crate::utils::flatten_panicvals(&[&[PanicVal::write_str("NotNulTerminated")]])
}
}
}
}
#[cfg_attr(
feature = "docsrs",
doc(cfg(all(feature = "non_basic", feature = "rust_1_88")))
)]
impl PanicFmt for FromBytesUntilNulError {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = ComputePvCount {
field_amount: 1,
summed_pv_count: <()>::PV_COUNT,
delimiter: cfmt::TypeDelim::Tupled,
}
.call();
}
#[cfg_attr(
feature = "docsrs",
doc(cfg(all(feature = "non_basic", feature = "rust_1_88")))
)]
impl StdWrapper<&FromBytesUntilNulError> {
/// Formats a `FromBytesUntilNulError`.
pub const fn to_panicvals(
self,
fmtarg: FmtArg,
) -> [PanicVal<'static>; FromBytesUntilNulError::PV_COUNT] {
match fmtarg.fmt_kind {
FmtKind::Display => crate::utils::flatten_panicvals(&[&[PanicVal::write_str(
"data provided does not contain a nul",
)]]),
FmtKind::Debug => flatten_panicvals! {fmtarg;
"FromBytesUntilNulError",
open: cfmt::OpenParen,
() => (), cfmt::COMMA_TERM,
close: cfmt::CloseParen,
},
}
}
}

141
vendor/const_panic/src/int_formatting.rs vendored Normal file
View File

@@ -0,0 +1,141 @@
use crate::{
fmt::{FmtArg, NumberFmt},
utils::{Sign, TailShortString},
};
pub(crate) const fn fmt_decimal<const N: usize>(sign: Sign, mut n: u128) -> TailShortString<N> {
let mut start = N;
let mut buffer = [0u8; N];
loop {
start -= 1;
let digit = (n % 10) as u8;
buffer[start] = b'0' + digit;
n /= 10;
if n == 0 {
break;
}
}
if let Sign::Negative = sign {
start -= 1;
buffer[start] = b'-';
}
// safety: buffer is only ever written ascii, so its automatically valid utf8.
unsafe { TailShortString::new(start as u8, buffer) }
}
pub(crate) const fn fmt_binary<const N: usize>(
mut n: u128,
is_alternate: bool,
) -> TailShortString<N> {
let mut start = N;
let mut buffer = [0u8; N];
loop {
start -= 1;
let digit = (n & 1) as u8;
buffer[start] = b'0' + digit;
n >>= 1;
if n == 0 {
break;
}
}
if is_alternate {
start -= 1;
buffer[start] = b'b';
start -= 1;
buffer[start] = b'0';
}
// safety: buffer is only ever written ascii, so its automatically valid utf8.
unsafe { TailShortString::new(start as u8, buffer) }
}
pub(crate) const fn fmt_hexadecimal<const N: usize>(
mut n: u128,
is_alternate: bool,
) -> TailShortString<N> {
let mut start = N;
let mut buffer = [0u8; N];
loop {
start -= 1;
let digit = (n & 0xF) as u8;
buffer[start] = match digit {
0..=9 => b'0' + digit,
_ => b'A' - 10 + digit,
};
n >>= 4;
if n == 0 {
break;
}
}
if is_alternate {
start -= 1;
buffer[start] = b'x';
start -= 1;
buffer[start] = b'0';
}
// safety: buffer is only ever written ascii, so its automatically valid utf8.
unsafe { TailShortString::new(start as u8, buffer) }
}
pub(crate) const fn compute_len(sign: Sign, int: u128, bits: u8, fmt: FmtArg) -> u8 {
match fmt.number_fmt {
NumberFmt::Decimal => compute_decimal_len(sign, int),
NumberFmt::Hexadecimal => {
let with_0x = (fmt.is_alternate as u8) * 2;
let i = match sign {
Sign::Negative => bits,
Sign::Positive => (128 - int.leading_zeros()) as u8,
};
let tmp = if i == 0 {
1
} else {
i / 4 + (i % 4 != 0) as u8
};
tmp + with_0x
}
NumberFmt::Binary => {
let with_0b = (fmt.is_alternate as u8) * 2;
let i = match sign {
Sign::Negative => bits,
Sign::Positive => (128 - int.leading_zeros()) as u8,
};
(if i == 0 { 1 } else { i }) + with_0b
}
}
}
const fn compute_decimal_len(sign: Sign, mut n: u128) -> u8 {
let mut len = matches!(sign, Sign::Negative) as u8 + 1;
if n >= 1_0000_0000_0000_0000 {
n /= 1_0000_0000_0000_0000;
len += 16;
}
if n >= 1_0000_0000_0000 {
n /= 1_0000_0000_0000;
len += 12;
}
if n >= 1_0000_0000 {
n /= 100_000_000;
len += 8;
}
if n >= 1_0000 {
n /= 1_0000;
len += 4;
}
if n >= 100 {
n /= 100;
len += 2;
}
if n >= 10 {
len += 1;
}
len
}

345
vendor/const_panic/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,345 @@
//! For panicking with formatting in const contexts.
//!
//! This library exists because the panic macro was stabilized for use in const contexts
//! in Rust 1.57.0, without formatting support.
//!
//! All of the types that implement the [`PanicFmt`] trait can be formatted in panics.
//!
//! # Examples
//!
//! - [Basic](#basic)
//! - [Custom Types](#custom-types)
//!
//! ### Basic
//!
//! ```compile_fail
//! use const_panic::concat_assert;
//!
//! const FOO: u32 = 10;
//! const BAR: u32 = 0;
//! const _: () = assert_non_zero(FOO, BAR);
//!
//! #[track_caller]
//! const fn assert_non_zero(foo: u32, bar: u32) {
//! concat_assert!{
//! foo != 0 && bar != 0,
//! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
//! }
//! }
//! ```
//! The above code fails to compile with this error:
//! ```text
//! error[E0080]: evaluation of constant value failed
//! --> src/lib.rs:20:15
//! |
//! 8 | const _: () = assert_non_zero(FOO, BAR);
//! | ^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
//! neither foo nor bar can be zero!
//! foo: 10
//! bar: 0', src/lib.rs:8:15
//! ```
//!
//! When called at runtime
//! ```should_panic
//! use const_panic::concat_assert;
//!
//! assert_non_zero(10, 0);
//!
//! #[track_caller]
//! const fn assert_non_zero(foo: u32, bar: u32) {
//! concat_assert!{
//! foo != 0 && bar != 0,
//! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
//! }
//! }
//! ```
//! it prints this:
//! ```text
//! thread 'main' panicked at '
//! neither foo nor bar can be zero!
//! foo: 10
//! bar: 0', src/lib.rs:6:1
//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
//!
//! ```
//!
//! ### Custom types
//!
//! Panic formatting for custom types can be done in these ways
//! (in increasing order of verbosity):
//! - Using the [`PanicFmt` derive] macro
//! (requires the opt-in `"derive"` feature)
//! - Using the [`impl_panicfmt`] macro
//! (requires the default-enabled `"non_basic"` feature)
//! - Using the [`flatten_panicvals`] macro
//! (requires the default-enabled `"non_basic"` feature)
//! - Manually implementing the [`PanicFmt`] trait as described in its docs.
//!
//! This example uses the [`PanicFmt` derive] approach.
//!
//! ```compile_fail
//! use const_panic::{PanicFmt, concat_panic};
//!
//! const LAST: u8 = {
//! Foo{
//! x: &[],
//! y: Bar(false, true),
//! z: Qux::Left(23),
//! }.pop().1
//! };
//!
//! impl Foo<'_> {
//! /// Pops the last element
//! ///
//! /// # Panics
//! ///
//! /// Panics if `self.x` is empty
//! #[track_caller]
//! const fn pop(mut self) -> (Self, u8) {
//! if let [rem @ .., last] = self.x {
//! self.x = rem;
//! (self, *last)
//! } else {
//! concat_panic!(
//! "\nexpected a non-empty Foo, found: \n",
//! // uses alternative Debug formatting for `self`,
//! // otherwise this would use regular Debug formatting.
//! alt_debug: self
//! )
//! }
//! }
//! }
//!
//! #[derive(PanicFmt)]
//! struct Foo<'a> {
//! x: &'a [u8],
//! y: Bar,
//! z: Qux,
//! }
//!
//! #[derive(PanicFmt)]
//! struct Bar(bool, bool);
//!
//! #[derive(PanicFmt)]
//! enum Qux {
//! Up,
//! Down { x: u32, y: u32 },
//! Left(u64),
//! }
//!
//! ```
//! The above code fails to compile with this error:
//! ```text
//! error[E0080]: evaluation of constant value failed
//! --> src/lib.rs:57:5
//! |
//! 7 | / Foo{
//! 8 | | x: &[],
//! 9 | | y: Bar(false, true),
//! 10 | | z: Qux::Left(23),
//! 11 | | }.pop().1
//! | |___________^ the evaluated program panicked at '
//! expected a non-empty Foo, found:
//! Foo {
//! x: [],
//! y: Bar(
//! false,
//! true,
//! ),
//! z: Left(
//! 23,
//! ),
//! }', src/lib.rs:11:7
//!
//!
//! ```
//!
//! # Limitations
#![doc = crate::doc_macros::limitation_docs!()]
//!
//! ### Panic message length
//!
//! The panic message can only be up to [`MAX_PANIC_MSG_LEN`] long,
//! after which it is truncated.
//!
//! # Cargo features
//!
//! - `"non_basic"`(enabled by default):
//! Enables support for formatting structs, enums, and arrays.
//! <br>
//! Without this feature, you can effectively only format primitive types
//! (custom types can manually implement formatting with more difficulty).
//!
//! - `"rust_latest_stable"`(disabled by default):
//! Enables all the `"rust_1_*"` features.
//!
//! - `"rust_1_64"`(disabled by default):
//! Enables formatting of additional items that require Rust 1.64.0 to do so.
//!
//! - `"rust_1_82"`(disabled by default):
//! Enables formatting of additional items that require Rust 1.82.0 to do so.
//!
//! - `"rust_1_88"`(disabled by default):
//! Enables formatting of additional items that require Rust 1.88.0 to do so.
//!
//! - `"derive"`(disabled by default):
//! Enables the [`PanicFmt` derive] macro.
//!
//! # Plans
//!
//! None for now
//!
//! # No-std support
//!
//! `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used.
//!
//! # Minimum Supported Rust Version
//!
//! This requires Rust 1.57.0, because it uses the `panic` macro in a const context.
//!
//!
//! [`PanicFmt` derive]: derive@crate::PanicFmt
//! [`PanicFmt`]: trait@crate::PanicFmt
//! [`impl_panicfmt`]: crate::impl_panicfmt
//! [`flatten_panicvals`]: crate::flatten_panicvals
//! [`MAX_PANIC_MSG_LEN`]: crate::MAX_PANIC_MSG_LEN
#![no_std]
#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
#![warn(missing_docs)]
#![deny(clippy::missing_safety_doc)]
#![deny(clippy::shadow_unrelated)]
#![deny(clippy::wildcard_imports)]
extern crate self as const_panic;
#[macro_use]
mod doc_macros;
#[macro_use]
mod macros;
mod concat_panic_;
mod debug_str_fmt;
mod int_formatting;
pub mod fmt;
#[cfg(all(doctest, feature = "non_basic"))]
pub mod doctests;
mod panic_val;
#[cfg(feature = "non_basic")]
mod const_default;
pub mod utils;
/// tests doc lints with `cargo doc --features="test rust_latest_stable derive"`
#[cfg(all(feature = "test", feature = "non_basic"))]
pub mod test_doc_lints;
#[cfg(all(test, not(feature = "test")))]
compile_error! {r##"please use cargo test --features "test""##}
#[cfg(feature = "non_basic")]
mod slice_stuff;
#[cfg(feature = "non_basic")]
mod array_string;
#[cfg(feature = "non_basic")]
pub use crate::array_string::ArrayString;
mod wrapper;
mod fmt_impls {
#[macro_use]
pub(crate) mod basic_fmt_impls;
#[cfg(feature = "rust_1_64")]
mod rust_1_64_fmt_impls;
#[cfg(feature = "rust_1_82")]
mod rust_1_82_fmt_impls;
#[macro_use]
#[cfg(feature = "non_basic")]
mod option_fmt_impls;
#[cfg(feature = "non_basic")]
mod nonzero_impls;
#[cfg(feature = "non_basic")]
mod other_impls;
#[cfg(feature = "non_basic")]
mod fmt_range;
#[cfg(all(feature = "non_basic", feature = "rust_1_88"))]
mod rust_1_88_nonbasic_fmt_impls;
}
pub use crate::{
concat_panic_::{concat_panic, MAX_PANIC_MSG_LEN},
panic_val::PanicVal,
wrapper::StdWrapper,
};
#[doc(no_inline)]
pub use crate::fmt::{FmtArg, IsCustomType, PanicFmt};
#[cfg(feature = "non_basic")]
#[doc(no_inline)]
pub use crate::fmt::{ComputePvCount, TypeDelim};
#[doc(hidden)]
pub mod __ {
pub use core::{
assert, compile_error, concat,
option::Option::{None, Some},
primitive::usize,
result::Result::{Err, Ok},
stringify,
};
pub use crate::*;
#[cfg(feature = "non_basic")]
pub use crate::reexported_non_basic::*;
}
#[cfg(feature = "non_basic")]
#[doc(hidden)]
mod reexported_non_basic {
pub use core::{mem::forget, option::Option, primitive::str, unreachable};
pub use typewit::MakeTypeWitness;
pub use crate::{
concat_panic_::{compute_length, make_panic_string_unwrapped},
const_default::ConstDefault,
macros::concat_macro::ConcatCmd,
utils::{assert_flatten_panicvals_length, flatten_panicvals, panicvals_id},
};
pub const EPV: crate::PanicVal<'_> = crate::PanicVal::EMPTY;
}
#[cfg(feature = "derive")]
include! {"./proc_macro_reexports/panicfmt_derive.rs"}
#[doc(hidden)]
#[cfg(feature = "test")]
pub mod test_utils;
#[doc(hidden)]
#[cfg(feature = "test")]
pub mod for_tests {
pub use crate::concat_panic_::{format_panic_message, NotEnoughSpace};
}
#[cfg(all(doctest))]
#[doc = include_str!("../README.md")]
pub struct ReadmeTest;

506
vendor/const_panic/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,506 @@
#[macro_use]
mod concat_assert;
#[cfg(feature = "non_basic")]
#[macro_use]
pub(crate) mod concat_macro;
#[cfg(feature = "non_basic")]
#[macro_use]
mod non_basic_macros;
#[cfg(feature = "non_basic")]
#[macro_use]
mod macro_utils;
#[cfg(feature = "non_basic")]
#[macro_use]
mod impl_panicfmt;
#[macro_use]
mod unwrapping;
#[doc(hidden)]
#[macro_export]
macro_rules! __write_array {
($array:expr, $len:expr, $value:expr) => {
$array[$len] = $value;
$len += 1;
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __write_array_checked {
($array:expr, $len:expr, $value:expr) => {
if $array.len() > $len {
$array[$len] = $value;
$len += 1;
}
};
}
/// Coerces `$reff` to a type that has a `to_panicvals` method,
/// which is expected to return a `[PanicVal<'_>; LEN]`.
///
/// # Limitations
///
#[doc = crate::doc_macros::limitation_docs!()]
///
/// # Example
///
/// This example uses [`const_panic::ArrayString`](crate::ArrayString)
/// to show what the values format into,
/// which requires the `"non_basic"` crate feature (enabled by default).
///
#[cfg_attr(feature = "non_basic", doc = "```rust")]
#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
/// use const_panic::{ArrayString, FmtArg, IsCustomType, PanicFmt, PanicVal, coerce_fmt};
///
/// type AS = ArrayString<100>;
///
/// assert_eq!(
/// AS::from_panicvals(&coerce_fmt!(100u8).to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "100",
/// );
///
/// assert_eq!(
/// AS::from_panicvals(&coerce_fmt!("hello\n").to_panicvals(FmtArg::DEBUG)).unwrap(),
/// r#""hello\n""#,
/// );
///
/// assert_eq!(
/// AS::from_panicvals(&coerce_fmt!(IsReal::No).to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "No",
/// );
///
/// assert_eq!(
/// AS::from_panicvals(&coerce_fmt!(IsReal::Yes).to_panicvals(FmtArg::DEBUG)).unwrap(),
/// "Yes",
/// );
///
///
///
/// enum IsReal{Yes, No}
///
/// // All the code below manually implements panic formatting for a field-less enum.
/// // This can be written concisely with the `PanicFmt` derive or `impl_panicfmt` macro.
/// impl PanicFmt for IsReal {
/// type This = Self;
/// type Kind = IsCustomType;
/// const PV_COUNT: usize = 1;
/// }
///
/// impl IsReal {
/// pub const fn to_panicvals(&self, _f: FmtArg) -> [PanicVal<'_>; IsReal::PV_COUNT] {
/// let x = match self {
/// Self::Yes => "Yes",
/// Self::No => "No",
/// };
/// [PanicVal::write_str(x)]
/// }
/// }
///
/// ```
#[macro_export]
macro_rules! coerce_fmt {
($reff:expr) => {
match &$reff {
reff => $crate::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}
};
}
/// Panics with the concanenation of the arguments.
///
/// [**Examples below**](#examples)
///
/// # Syntax
///
/// This macro uses this syntax:
/// ```text
/// concat_panic!(
/// $($fmtarg:expr;)?
/// $(
/// $( $format_override:tt: )? $arg_to_fmt:expr
/// ),*
/// $(,)?
/// )
/// ```
///
/// `$fmtarg` is an optional [`FmtArg`](crate::FmtArg) argument
/// which defaults to `FmtArg::DEBUG`,
/// determining how non-literal `$arg_to_fmt` arguments are formatted.
///
/// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument,
/// changing how that `$arg_to_fmt` argument is formatted.
///
#[doc = formatting_docs!()]
///
/// # Limitations
///
#[doc = crate::doc_macros::limitation_docs!()]
///
/// # Examples
///
/// ### `Odd`-type
///
/// ```rust, compile_fail
/// use const_panic::concat_panic;
///
/// use odd::Odd;
///
/// # fn main(){
/// const _: Odd = match Odd::new(3 * 4) {
/// Ok(x) => x,
/// Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
/// };
/// # }
///
/// mod odd {
/// pub struct Odd(u32);
///
/// impl Odd {
/// pub const fn new(n: u32) -> Result<Odd, Even> {
/// if n % 2 == 1 {
/// Ok(Odd(n))
/// } else {
/// Err(Even(n))
/// }
/// }
/// }
///
/// # /*
/// #[derive(const_panic::PanicFmt))]
/// # */
/// pub struct Even(u32);
/// #
/// # impl const_panic::PanicFmt for Even {
/// # type This = Self;
/// # type Kind = const_panic::IsCustomType;
/// # const PV_COUNT: usize = 1;
/// # }
/// # impl Even {
/// # pub const fn to_panicvals(
/// # &self,
/// # f: const_panic::FmtArg,
/// # ) -> [const_panic::PanicVal<'static>; 1] {
/// # const_panic::StdWrapper(&self.0).to_panicvals(f)
/// # }
/// # }
/// }
///
/// ```
/// produces this compile-time error:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros.rs:188:15
/// |
/// 10 | Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
/// expected odd number, got `12`', src/macros.rs:10:15
/// |
/// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
///
/// ```
///
///
/// ### All the syntax
///
/// This example demonstrates using all of the syntax of this macro.
///
/// ```compile_fail
/// use const_panic::{FmtArg, concat_panic, fmt};
///
/// const _: () = concat_panic!{
/// // the optional `$fmtarg` parameter.
/// // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
/// FmtArg::ALT_DEBUG;
///
/// "\n\nshowing off literals:\n",
/// 100u8,
/// "hello",
///
/// "\n\nnon-literals with formatting determined by the $fmtarg parameter:\n",
/// // this is considered a non-literal, because it's inside other tokens.
/// ("a non-literal"),
/// [100u8, 200],
///
/// "\n\nexplicitly debug formatted:\n",
/// debug: "foo",
/// // `{?}:` is The same as `debug:`
/// {?}: "bar",
///
/// "\n\nalternate debug formatted:\n",
/// alt_debug: ["foo"],
/// // `{#?}:` is The same as `alt_debug:`
/// {#?}: "bar",
///
/// "\n\ndisplay formatted:\n",
/// display: "baz",
/// // `{}:` is The same as `display:`
/// {}: ["qux", "aaa"],
///
/// "\n\nalternate display formatted:",
/// alt_display: ["bbb", "ccc"],
/// // `{#}:` is The same as `alt_display:`
/// {#}: ["bbb", "ccc"],
///
/// "\n\nbinary formatted:\n",
/// bin: [3u8, 5, 8, 13],
/// // `{b}:` is The same as `bin:`
/// {b}: [3u8, 5, 8, 13],
///
/// "\n\nalternate-binary formatted:\n",
/// alt_bin: [21u8, 34, 55, 89],
/// // `{#b}:` is The same as `alt_bin:`
/// {#b}: [21u8, 34, 55, 89],
///
/// "\n\nhexadecimal formatted:\n",
/// hex: [3u8, 5, 8, 13],
/// // `{X}:` is The same as `hex:`
/// {X}: [3u8, 5, 8, 13],
///
/// "\n\nalternate-hexadecimal formatted:\n",
/// alt_hex: [21u8, 34, 55, 89],
/// // `{#X}:` is The same as `alt_hex:`
/// {#X}: [21u8, 34, 55, 89],
///
/// "\n\n",
/// };
///
/// ```
/// The above code produces this compile-time error:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros.rs:186:15
/// |
/// 6 | const _: () = concat_panic!{
/// | _______________^
/// 7 | | // the optional `$fmtarg` parameter.
/// 8 | | // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
/// 9 | | FmtArg::ALT_DEBUG;
/// ... |
/// 60 | | "\n\n",
/// 61 | | };
/// | |_^ the evaluated program panicked at '
///
/// showing off literals:
/// 100hello
///
/// non-literals with formatting determined by the $fmtarg parameter:
/// "a non-literal"[
/// 100,
/// 200,
/// ]
///
/// explicitly debug formatted:
/// "foo""bar"
///
/// alternate debug formatted:
/// [
/// "foo",
/// ]"bar"
///
/// display formatted:
/// baz[qux, aaa]
///
/// alternate display formatted:[
/// bbb,
/// ccc,
/// ][
/// bbb,
/// ccc,
/// ]
///
/// binary formatted:
/// [11, 101, 1000, 1101][11, 101, 1000, 1101]
///
/// alternate-binary formatted:
/// [
/// 0b10101,
/// 0b100010,
/// 0b110111,
/// 0b1011001,
/// ][
/// 0b10101,
/// 0b100010,
/// 0b110111,
/// 0b1011001,
/// ]
///
/// hexadecimal formatted:
/// [3, 5, 8, D][3, 5, 8, D]
///
/// alternate-hexadecimal formatted:
/// [
/// 0x15,
/// 0x22,
/// 0x37,
/// 0x59,
/// ][
/// 0x15,
/// 0x22,
/// 0x37,
/// 0x59,
/// ]
///
/// ', src/macros.rs:6:15
/// |
/// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
///
/// error: aborting due to previous error
///
/// ```
///
#[macro_export]
macro_rules! concat_panic {
($($args:tt)*) => (
$crate::__concat_func_setup!{
(|args| $crate::concat_panic(args))
[]
[$($args)*,]
}
)
}
// This macro takes the optional `$fmt:expr;` argument before everything else.
// But I had to parse the argument manually,
// because `$fmt:expr;` fails compilation instead of trying the following branches
// when the argument isn't valid expression syntax.
#[doc(hidden)]
#[macro_export]
macro_rules! __concat_func_setup {
($args:tt $prev:tt [$($fmt:tt).*; $($rem:tt)* ]) => ({
let mut fmt: $crate::FmtArg = $($fmt).*;
$crate::__concat_func!{fmt $args $prev [$($rem)*]}
});
($args:tt $prev:tt [$(:: $(@$_dummy:tt@)?)? $($fmt:ident)::* ; $($rem:tt)* ]) => ({
let mut fmt: $crate::FmtArg = $(:: $($_dummy)?)? $($fmt)::*;
$crate::__concat_func!{fmt $args $prev [$($rem)*]}
});
($args:tt $prev:tt $rem:tt) => ({
let mut fmt: $crate::FmtArg = $crate::FmtArg::DEBUG;
$crate::__concat_func!{fmt $args $prev $rem}
});
}
#[doc(hidden)]
#[macro_export]
macro_rules! __concat_func {
($fmt:ident $args:tt [$($prev:tt)*] [$keyword:tt: $expr:expr, $($rem:tt)* ]) => {
$crate::__concat_func!{
$fmt
$args
[$($prev)* ($crate::__set_fmt_from_kw!($keyword, $fmt), $expr)]
[$($rem)*]
}
};
($fmt:ident $args:tt [$($prev:tt)*] [$expr:literal, $($rem:tt)* ]) => {
$crate::__concat_func!{
$fmt
$args
[$($prev)* ($crate::__set_fmt_from_kw!(display, $fmt), $expr)]
[$($rem)*]
}
};
($fmt:ident $args:tt [$($prev:tt)*] [$expr:expr, $($rem:tt)* ]) => {
$crate::__concat_func!{
$fmt
$args
[$($prev)* ($fmt, $expr)]
[$($rem)*]
}
};
($fmt:ident (|$args:ident| $function_call:expr) [$(($fmt_arg:expr, $reff:expr))*] [$(,)*]) => {
match &[
$(
$crate::StdWrapper(
&$crate::coerce_fmt!($reff)
.to_panicvals($fmt_arg)
).deref_panic_vals(),
)*
] {
$args => $function_call,
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __set_fmt_from_kw {
(open, $fmtarg:ident) => {{
$fmtarg = $fmtarg.indent();
$fmtarg.set_display()
}};
(close, $fmtarg:ident) => {{
$fmtarg = $fmtarg.unindent();
$fmtarg.set_display()
}};
(display, $fmtarg:ident) => {
$fmtarg.set_display().set_alternate(false)
};
({}, $fmtarg:ident) => {
$fmtarg.set_display().set_alternate(false)
};
(alt_display, $fmtarg:ident) => {
$fmtarg.set_display().set_alternate(true)
};
({#}, $fmtarg:ident) => {
$fmtarg.set_display().set_alternate(true)
};
(debug, $fmtarg:ident) => {
$fmtarg.set_debug().set_alternate(false)
};
({?}, $fmtarg:ident) => {
$fmtarg.set_debug().set_alternate(false)
};
(alt_debug, $fmtarg:ident) => {
$fmtarg.set_debug().set_alternate(true)
};
({#?}, $fmtarg:ident) => {
$fmtarg.set_debug().set_alternate(true)
};
(hex, $fmtarg:ident) => {
$fmtarg.set_hex().set_alternate(false)
};
({X}, $fmtarg:ident) => {
$fmtarg.set_hex().set_alternate(false)
};
(alt_hex, $fmtarg:ident) => {
$fmtarg.set_hex().set_alternate(true)
};
({#X}, $fmtarg:ident) => {
$fmtarg.set_hex().set_alternate(true)
};
(bin, $fmtarg:ident) => {
$fmtarg.set_bin().set_alternate(false)
};
({b}, $fmtarg:ident) => {
$fmtarg.set_bin().set_alternate(false)
};
(alt_bin, $fmtarg:ident) => {
$fmtarg.set_bin().set_alternate(true)
};
({#b}, $fmtarg:ident) => {
$fmtarg.set_bin().set_alternate(true)
};
(_, $fmtarg:ident) => {
$fmtarg
};
($kw:tt, $fmtarg:ident) => {
compile_error!(concat!(
"unrecognized formatting specifier: ",
stringify!($kw),
"\n",
"expected one of:\n",
"- display/{}\n",
"- alt_display/{#}\n",
"- debug/{?}\n",
"- alt_debug/{#?}\n",
"- hex/{X}\n",
"- alt_hex/{#X}\n",
"- bin/{b}\n",
"- alt_bin/{#b}\n",
))
};
}

View File

@@ -0,0 +1,124 @@
/// Asserts that `$condition` is true.
///
/// When only the `$condition` argument is passed,
/// this delegates to the [`core::assert`] macro.
///
/// When two or more arguments are passed,
/// this panics with formatting by delegating the second and remaining arguments
/// to the [`concat_panic`](macro@crate::concat_panic) macro.
///
/// ### Examples
///
/// ### Formatted assertion
///
/// ```compile_fail
/// use const_panic::concat_assert;
///
/// const ONE: Even = Even::new(1);
///
/// struct Even(u32);
///
/// impl Even {
/// #[track_caller]
/// const fn new(n: u32) -> Self {
/// concat_assert!(n % 2 == 0, "\nexpected the argument to be even, found: ", n);
///
/// Even(n)
/// }
/// }
/// ```
/// the above code errors with this message:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/concat_assert.rs:16:19
/// |
/// 4 | const ONE: Even = Even::new(1);
/// | ^^^^^^^^^^^^ the evaluated program panicked at '
/// expected the argument to be even, found: 1', src/macros/concat_assert.rs:4:19
///
/// ```
///
/// ### More formatting
///
/// This example demonstrates what error non-`#[track_caller]` functions produce,
/// and uses the `"non_basic"` feature(enabled by default).
///
/// ```compile_fail
/// use const_panic::concat_assert;
///
/// const SUM: u64 = sum(&[3, 5, 8], 1..40);
///
/// const fn sum(mut slice: &[u32], range: std::ops::Range<usize>) -> u64 {
/// concat_assert!(
/// range.start <= range.end && range.end <= slice.len(),
/// "\ncannot index slice of length `", slice.len(),
/// "` with `", range, "` range"
/// );
///
/// let mut sum = 0u64;
///
/// while let [curr, ref rem @ ..] = *slice {
/// sum += curr as u64;
///
/// slice = rem;
/// }
///
/// sum
/// }
/// ```
/// the above code errors with this message:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/concat_assert.rs:52:5
/// |
/// 6 | const SUM: u64 = sum(&[3, 5, 8], 1..40);
/// | ---------------------- inside `SUM` at src/macros/concat_assert.rs:6:18
/// ...
/// 9 | / concat_assert!(
/// 10 | | range.start <= range.end && range.end <= slice.len(),
/// 11 | | "\ncannot index slice of length `", slice.len(),
/// 12 | | "` with `", range, "` range"
/// 13 | | );
/// | | ^
/// | | |
/// | |_____the evaluated program panicked at '
/// cannot index slice of length `3` with `1..40` range', src/macros/concat_assert.rs:9:5
/// | inside `_doctest_main_src_macros_concat_assert_rs_46_0::sum` at /home/matias/Documents/proyectos programacion/const_panic/src/macros.rs:240:21
/// |
/// = note: this error originates in the macro `$crate::concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
/// ```
///
/// ### Unformatted assertion
///
/// When only the `$condition` argument is passed,
/// this delegates to the [`core::assert`] macro.
///
/// ```compile_fail
/// use const_panic::concat_assert;
///
/// const _: () = concat_assert!(cfg!(any(feature = "foo", feature = "bar")) );
/// ```
/// the above code errors with this message:
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/concat_assert.rs:48:15
/// |
/// 6 | const _: () = concat_assert!(cfg!(any(feature = "foo", feature = "bar")) );
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: cfg!(any(feature = \"foo\", feature = \"bar\"))', src/macros/concat_assert.rs:6:15
/// |
/// = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
/// ```
///
///
#[macro_export]
macro_rules! concat_assert {
($condition:expr $(,)?) => {
$crate::__::assert!($condition);
};
($condition:expr, $($fmt:tt)*) => {{
#[allow(clippy::equatable_if_let)]
if let false = $condition {
$crate::concat_panic!{$($fmt)*}
}
}};
}

View File

@@ -0,0 +1,86 @@
use crate::ArrayString;
use typewit::{MakeTypeWitness, TypeEq, TypeWitnessTypeArg};
/// Concatenates [`PanicFmt`] constants into a `&'static str`
///
/// This formats arguments the same as the [`concat_panic`] macro,
/// also requiring the arguments to be constant expressions.
///
/// # Example
///
/// ### Basic
///
/// ```rust
/// use const_panic::concat_;
///
/// assert_eq!(concat_!("array: ", &[3u8, 5, 8, 13]), "array: [3, 5, 8, 13]");
///
/// ```
///
/// ### Formatted
///
/// ```rust
/// use const_panic::concat_;
///
/// assert_eq!(concat_!({?}: get_message(), {}: get_message()), r#""hello"hello"#);
///
/// const fn get_message() -> &'static str {
/// "hello"
/// }
///
/// ```
///
/// [`PanicFmt`]: crate::fmt::PanicFmt
/// [`concat_panic`]: macro@crate::concat_panic
///
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
#[macro_export]
macro_rules! concat_ {
() => ("");
($($args:tt)*) => ({
const fn __func_zxe7hgbnjs<Ret_ZXE7HGBNJS, const CAP_ZXE7HGBNJS: $crate::__::usize>(
cmd: $crate::__::ConcatCmd<Ret_ZXE7HGBNJS, CAP_ZXE7HGBNJS>
) -> Ret_ZXE7HGBNJS {
$crate::__concat_func_setup!{
(|args| {
match cmd {
$crate::__::ConcatCmd::ComputeLength(te) =>
te.to_left($crate::__::compute_length(args)),
$crate::__::ConcatCmd::BuildArray(te) =>
te.to_left($crate::__::make_panic_string_unwrapped(args)),
}
})
[]
[$($args)*,]
}
}
const LEN_ZXE7HGBNJS: $crate::__::usize = __func_zxe7hgbnjs($crate::__::MakeTypeWitness::MAKE);
const AS_ZXE7HGBNJS: $crate::ArrayString<LEN_ZXE7HGBNJS> =
__func_zxe7hgbnjs($crate::__::MakeTypeWitness::MAKE);
const S_ZXE7HGBNJS: &$crate::__::str = AS_ZXE7HGBNJS.to_str();
S_ZXE7HGBNJS
})
}
#[doc(hidden)]
pub enum ConcatCmd<Ret, const CAP: usize> {
ComputeLength(TypeEq<Ret, usize>),
BuildArray(TypeEq<Ret, ArrayString<CAP>>),
}
impl<Ret, const CAP: usize> TypeWitnessTypeArg for ConcatCmd<Ret, CAP> {
type Arg = Ret;
}
impl MakeTypeWitness for ConcatCmd<usize, 0> {
const MAKE: Self = Self::ComputeLength(TypeEq::NEW);
}
impl<const CAP: usize> MakeTypeWitness for ConcatCmd<ArrayString<CAP>, CAP> {
const MAKE: Self = Self::BuildArray(TypeEq::NEW);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
#[doc(hidden)]
#[macro_export]
macro_rules! zip_counter_and_last {
(
$(:: $(@$dummy:tt@)?)? $($macro:ident)::* ! $prev_args:tt
($($iter:tt)*)
($($counter:tt)*)
) => {
$crate::__zip_counter_and_last_inner!{
(($(:: $($dummy)?)? $($macro)::*) $prev_args)
[]
($($iter)*)
($($counter)*)
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __zip_counter_and_last_inner {
(
$macro:tt
[$($prev:tt)*]
($elem:tt $($iter:tt)+)
($counter:tt $($rem_counter:tt)+)
) => {
$crate::__zip_counter_and_last_inner!{
$macro
[$($prev)* prefix($elem $counter)]
($($iter)*)
($($rem_counter)*)
}
};
(
( ($($macro:tt)*) { $($prev_args:tt)* } )
[$($prev:tt)*]
($elem:tt)
($counter:tt $($rem_counter:tt)*)
) => {
$($macro)* !{
$($prev_args)*
$($prev)*
last($elem $counter)
}
};
(
( ($($macro:tt)*) { $($prev_args:tt)* } )
[$($prev:tt)*]
()
($($rem_counter:tt)*)
) => {
$($macro)* !{
$($prev_args)*
$($prev)*
}
};
}

View File

@@ -0,0 +1,689 @@
/// 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.
/// <br>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`.<br>
/// 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<'_>; <Foo as PanicFmt>::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
/// + <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.
///
/// ```rust
/// 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,
/// },
/// }
/// }
/// }
/// }
/// ```
///
///
/// [`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, T);
///
/// impl<T> PanicFmt for Foo<T>
/// 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<bool>`
/// // - `Foo<u8>`
/// // - `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)* }
)*
}
}

View File

@@ -0,0 +1,187 @@
/// Gets the value in the `Some` variant.
///
/// # Panics
///
/// Panics if `$opt` is a None.
///
/// # Example
///
/// ```rust
/// use const_panic::unwrap_some;
///
/// const SUM: u8 = unwrap_some!(add_up(&[3, 5, 8, 13]));
///
/// assert_eq!(SUM, 29);
///
///
/// const fn add_up(mut slice: &[u8]) -> Option<u8> {
/// let mut sum = 0u8;
///
/// while let [x, ref rem @ ..] = *slice {
/// match sum.checked_add(x) {
/// Some(x) => sum = x,
/// None => return None,
/// }
/// slice = rem;
/// }
///
/// Some(sum)
/// }
///
/// ```
///
///
/// ### Error
///
/// This is what the compile-time error looks like when attempting to unwrap a `None`:
///
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/unwrapping.rs:10:17
/// |
/// 6 | const SUM: u8 = unwrap_some!(add_up(&[3, 5, 8, 13, 250]));
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
/// invoked `unwrap_some` macro with a `None` value', src/macros/unwrapping.rs:6:17
/// |
/// = note: this error originates in the macro `unwrap_some` (in Nightly builds, run with -Z macro-backtrace for more info)
///
/// ```
///
#[macro_export]
macro_rules! unwrap_some {
($opt:expr) => {
match $opt {
$crate::__::Some(x) => x,
$crate::__::None => $crate::concat_panic(&[&[$crate::PanicVal::write_str(
"\ninvoked `unwrap_some` macro with a `None` value",
)]]),
}
};
}
/// Gets the value in the `Ok` variant.
///
/// # Panics
///
/// This panics if `$res` is an `Err`, including the debug-formatted error in the panic message.
///
/// # Example
///
/// The struct formatting below requires the `"non_basic"` feature (enabled by default)
///
#[cfg_attr(feature = "non_basic", doc = "```rust")]
#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
/// use const_panic::unwrap_ok;
///
/// const SUM: u64 = unwrap_ok!(add_up_evens(&[2, 4, 8, 16]));
///
/// assert_eq!(SUM, 30);
///
/// const fn add_up_evens(slice: &[u8]) -> Result<u64, OddError> {
/// let mut sum = 0u64;
/// let mut i = 0;
///
/// while i < slice.len() {
/// let x = slice[i];
///
/// if x % 2 == 1 {
/// return Err(OddError{at: i, number: x});
/// }
///
/// sum += x as u64;
/// i += 1;
/// }
///
/// Ok(sum)
/// }
///
///
/// struct OddError {
/// at: usize,
/// number: u8,
/// }
///
/// // You can also use `#[derive(PanicFmt))]` with the "derive" feature
/// const_panic::impl_panicfmt!{
/// struct OddError {
/// at: usize,
/// number: u8,
/// }
/// }
///
/// ```
///
/// ### Error
///
/// This is what the compile-time error looks like when attempting to unwrap an `Err`:
///
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/unwrapping.rs:51:18
/// |
/// 6 | const SUM: u64 = unwrap_ok!(add_up_evens(&[3, 5, 8, 13]));
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
/// invoked `unwrap_ok` macro with an `Err` value: OddError { at: 0, number: 3 }', src/macros/unwrapping.rs:6:18
/// |
/// ```
#[macro_export]
macro_rules! unwrap_ok {
($res:expr) => {
match $res {
$crate::__::Ok(x) => x,
$crate::__::Err(e) => $crate::concat_panic(&[
&[$crate::PanicVal::write_str(
"\ninvoked `unwrap_ok` macro with an `Err` value: ",
)],
&$crate::coerce_fmt!(e).to_panicvals($crate::FmtArg::DEBUG),
]),
}
};
}
/// Gets the value in the `Err` variant.
///
/// # Panics
///
/// This panics if `$res` is an `Ok`, including the debug-formatted value in the panic message.
///
/// # Example
///
/// ```rust
/// use const_panic::unwrap_err;
///
/// type Res = Result<u32, &'static str>;
///
/// const ERR: &str = unwrap_err!(Res::Err("this is an error"));
///
/// assert_eq!(ERR, "this is an error");
///
/// ```
///
/// ### Error
///
/// This is what the compile-time error looks like when attempting to unwrap an `Ok`:
///
/// ```text
/// error[E0080]: evaluation of constant value failed
/// --> src/macros/unwrapping.rs:174:19
/// |
/// 8 | const ERR: &str = unwrap_err!(Res::Ok(1234));
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
/// invoked `unwrap_err` macro with an `Ok` value: 1234', src/macros/unwrapping.rs:8:19
/// |
/// ```
#[macro_export]
macro_rules! unwrap_err {
($res:expr) => {
match $res {
$crate::__::Ok(x) => $crate::concat_panic(&[
&[$crate::PanicVal::write_str(
"\ninvoked `unwrap_err` macro with an `Ok` value: ",
)],
&$crate::coerce_fmt!(x).to_panicvals($crate::FmtArg::DEBUG),
]),
$crate::__::Err(e) => e,
}
};
}

379
vendor/const_panic/src/panic_val.rs vendored Normal file
View File

@@ -0,0 +1,379 @@
use crate::{
fmt::{FmtArg, FmtKind, NumberFmt},
utils::{string_cap, Packed, PreFmtString, RangedBytes, Sign, TailShortString, WasTruncated},
};
#[cfg(feature = "non_basic")]
use crate::{
array_string::TinyString,
fmt::{IsLast, ShortString},
};
/// An opaque enum of the values that this crate knows how to format,
/// along with some formatting metadata.
///
/// This has constructor functions to make a `PanicVal` from:
/// - `bool`
/// - Integers
/// - `&str`
/// - Arrays/Slices of primitives (with the "non_basic" feature, enabled by default)
/// - [`ShortString`](crate::fmt::ShortString)
/// (with the "non_basic" feature, enabled by default)
///
#[derive(Copy, Clone)]
pub struct PanicVal<'a> {
pub(crate) var: PanicVariant<'a>,
}
#[derive(Copy, Clone)]
pub(crate) enum PanicVariant<'a> {
Str(StrFmt, Packed<&'a str>),
#[cfg(feature = "non_basic")]
ShortString(StrFmt, TinyString<{ string_cap::TINY }>),
PreFmt(PreFmtString),
Int(IntVal),
#[cfg(feature = "non_basic")]
Slice(crate::slice_stuff::Slice<'a>),
}
pub(crate) enum PanicClass<'a> {
PreFmt(RangedBytes<&'a [u8]>),
Int(IntVal),
#[cfg(feature = "non_basic")]
Slice(crate::slice_stuff::Slice<'a>),
}
#[derive(Copy, Clone)]
pub(crate) struct StrFmt {
pub(crate) leftpad: u8,
pub(crate) rightpad: u8,
pub(crate) fmt_kind: FmtKind,
}
impl StrFmt {
const DISPLAY: Self = Self {
leftpad: 0,
rightpad: 0,
fmt_kind: FmtKind::Display,
};
pub const fn new(fmtarg: FmtArg) -> Self {
Self {
leftpad: 0,
rightpad: 0,
fmt_kind: fmtarg.fmt_kind,
}
}
}
impl<'a> PanicVal<'a> {
/// A `PanicVal` that formats to nothing.
pub const EMPTY: Self = PanicVal::write_str("");
/// How many spaces are printed before this
pub const fn leftpad(&self) -> u8 {
use self::PanicVariant as PV;
match self.var {
PV::Str(strfmt, ..) => strfmt.leftpad,
#[cfg(feature = "non_basic")]
PV::ShortString(strfmt, ..) => strfmt.leftpad,
_ => 0,
}
}
/// How many spaces are printed after this
pub const fn rightpad(&self) -> u8 {
use self::PanicVariant as PV;
match self.var {
PV::Str(strfmt, ..) => strfmt.rightpad,
#[cfg(feature = "non_basic")]
PV::ShortString(strfmt, ..) => strfmt.rightpad,
_ => 0,
}
}
}
macro_rules! mutate_strfmt {
($self:ident, |$strfmt:ident| $mutator:expr) => {
match $self.var {
PanicVariant::Str(mut $strfmt, str) => {
$mutator;
PanicVal {
var: PanicVariant::Str($strfmt, str),
}
}
#[cfg(feature = "non_basic")]
PanicVariant::ShortString(mut $strfmt, str) => {
$mutator;
PanicVal {
var: PanicVariant::ShortString($strfmt, str),
}
}
var => PanicVal { var },
}
};
}
impl<'a> PanicVal<'a> {
/// Sets the amount of spaces printed before this to `fmtarg.indentation`.
///
/// Note that only strings can be padded.
pub const fn with_leftpad(self, fmtarg: FmtArg) -> Self {
mutate_strfmt! {self, |strfmt| strfmt.leftpad = fmtarg.indentation}
}
/// Sets the amount of spaces printed after this to `fmtarg.indentation`.
///
/// Note that only strings can be padded.
pub const fn with_rightpad(self, fmtarg: FmtArg) -> Self {
mutate_strfmt! {self, |strfmt| strfmt.rightpad = fmtarg.indentation}
}
/// Constructs a PanicVal which outputs the contents of `string` verbatim.
///
/// Equivalent to `PanicVal::from_str(string, FmtArg::DISPLAY)`
pub const fn write_str(string: &'a str) -> Self {
PanicVal {
var: PanicVariant::Str(StrFmt::DISPLAY, Packed(string)),
}
}
/// Constructs a PanicVal from a [`ShortString`], which outputs the string verbatim.
#[cfg(feature = "non_basic")]
pub const fn write_short_str(string: ShortString) -> Self {
Self {
var: PanicVariant::ShortString(StrFmt::DISPLAY, string.to_compact()),
}
}
/// Constructs a `PanicVal` usable as a separator between fields or elements.
///
/// This is sensitive to the [`fmtarg.is_alternate`] flag,
/// for more details on that you can look at the docs for
/// [`Separator::to_panicval`](crate::fmt::Separator#method.to_panicval)
///
/// # Panics
///
/// This panics if `string.len()` is greater than 12.
///
/// [`fmtarg.is_alternate`]: crate::FmtArg#structfield.is_alternate
#[cfg(feature = "non_basic")]
pub const fn from_element_separator(
separator: &str,
is_last_field: IsLast,
fmtarg: FmtArg,
) -> Self {
let (concat, rightpad) = match (is_last_field, fmtarg.is_alternate) {
(IsLast::No, false) => (ShortString::concat(&[separator, " "]), 0),
(IsLast::Yes, false) => (ShortString::new(""), 0),
(IsLast::No, true) => (ShortString::concat(&[separator, "\n"]), fmtarg.indentation),
(IsLast::Yes, true) => (ShortString::concat(&[separator, "\n"]), 0),
};
let strfmt = StrFmt {
leftpad: 0,
rightpad,
fmt_kind: FmtKind::Display,
};
Self {
var: PanicVariant::ShortString(strfmt, concat.to_compact()),
}
}
#[inline(always)]
pub(crate) const fn __new(var: PanicVariant<'a>) -> Self {
Self { var }
}
pub(crate) const fn to_class(&self) -> (StrFmt, PanicClass<'_>) {
match &self.var {
&PanicVariant::Str(strfmt, Packed(str)) => {
let ranged = RangedBytes {
start: 0,
end: str.len(),
bytes: str.as_bytes(),
};
(strfmt, PanicClass::PreFmt(ranged))
}
#[cfg(feature = "non_basic")]
PanicVariant::ShortString(strfmt, str) => (*strfmt, PanicClass::PreFmt(str.ranged())),
PanicVariant::PreFmt(str) => (StrFmt::DISPLAY, PanicClass::PreFmt(str.ranged())),
PanicVariant::Int(int) => (StrFmt::DISPLAY, PanicClass::Int(*int)),
#[cfg(feature = "non_basic")]
PanicVariant::Slice(slice) => (
StrFmt::new(slice.fmtarg.unpack()),
PanicClass::Slice(*slice),
),
}
}
pub(crate) const fn to_class_truncated(
&self,
mut truncate_to: usize,
) -> (StrFmt, PanicClass<'_>, WasTruncated) {
let (mut strfmt, class) = self.to_class();
if strfmt.leftpad as usize > truncate_to {
return (
StrFmt {
leftpad: strfmt.leftpad - truncate_to as u8,
rightpad: 0,
fmt_kind: FmtKind::Display,
},
PanicClass::PreFmt(RangedBytes::EMPTY),
WasTruncated::Yes(0),
);
} else {
truncate_to -= strfmt.leftpad as usize;
};
let was_trunc: WasTruncated;
let orig_len: usize;
match class {
PanicClass::PreFmt(str) => {
was_trunc = if let PanicVariant::PreFmt(pfmt) = self.var {
if pfmt.len() <= truncate_to {
WasTruncated::No
} else {
WasTruncated::Yes(0)
}
} else {
if let FmtKind::Display = strfmt.fmt_kind {
crate::utils::truncated_str_len(str, truncate_to)
} else {
crate::utils::truncated_debug_str_len(str, truncate_to)
}
};
orig_len = str.len();
}
PanicClass::Int(int) => {
strfmt.fmt_kind = FmtKind::Display;
was_trunc = if int.len() <= truncate_to {
WasTruncated::No
} else {
WasTruncated::Yes(0)
};
orig_len = int.len();
}
#[cfg(feature = "non_basic")]
PanicClass::Slice(_) => {
was_trunc = WasTruncated::No;
orig_len = 0;
}
}
truncate_to -= was_trunc.get_length(orig_len);
strfmt.rightpad = crate::utils::min_usize(strfmt.rightpad as usize, truncate_to) as u8;
(strfmt, class, was_trunc)
}
}
#[derive(Copy, Clone)]
pub(crate) struct IntVal {
sign: Sign,
number_fmt: NumberFmt,
is_alternate: bool,
// the size of the integer in bits
bits: u8,
// the length of the integer in bytes, once written.
len: u8,
value: Packed<u128>,
}
impl IntVal {
pub(crate) const fn from_u128(n: u128, bits: u8, f: FmtArg) -> PanicVal<'static> {
Self::new(Sign::Positive, n, bits, f)
}
pub(crate) const fn from_i128(n: i128, bits: u8, f: FmtArg) -> PanicVal<'static> {
let is_neg = if n < 0 {
Sign::Negative
} else {
Sign::Positive
};
Self::new(is_neg, n.unsigned_abs(), bits, f)
}
const fn new(sign: Sign, n: u128, bits: u8, fmtarg: FmtArg) -> PanicVal<'static> {
use crate::int_formatting::compute_len;
let len = compute_len(sign, n, bits, fmtarg);
let this = IntVal {
sign,
number_fmt: fmtarg.number_fmt,
is_alternate: fmtarg.is_alternate,
bits,
len,
value: Packed(n),
};
let var = if len as usize <= string_cap::PREFMT {
PanicVariant::PreFmt(this.fmt::<{ string_cap::PREFMT }>())
} else {
PanicVariant::Int(this)
};
PanicVal { var }
}
pub(crate) const fn fmt<const N: usize>(self) -> TailShortString<N> {
use crate::int_formatting::{fmt_binary, fmt_decimal, fmt_hexadecimal};
let IntVal {
sign,
number_fmt,
is_alternate,
len: _,
bits,
value: Packed(n),
} = self;
match number_fmt {
NumberFmt::Decimal => fmt_decimal::<N>(sign, n),
NumberFmt::Binary => {
let masked = apply_mask(sign, n, bits);
fmt_binary::<N>(masked, is_alternate)
}
NumberFmt::Hexadecimal => {
let masked = apply_mask(sign, n, bits);
fmt_hexadecimal::<N>(masked, is_alternate)
}
}
}
pub(crate) const fn len(&self) -> usize {
self.len as usize
}
}
const fn apply_mask(sign: Sign, n: u128, bits: u8) -> u128 {
if let Sign::Negative = sign {
let mask: u128 = if bits == 128 { !0 } else { (1 << bits) - 1 };
(n as i128).wrapping_neg() as u128 & mask
} else {
n
}
}
impl crate::PanicFmt for PanicVal<'_> {
type This = Self;
type Kind = crate::fmt::IsCustomType;
const PV_COUNT: usize = 1;
}
impl<'a> PanicVal<'a> {
/// Wraps this `PanicVal` in a single-element array.
pub const fn to_panicvals(&self, _: FmtArg) -> [PanicVal<'a>; 1] {
[*self]
}
/// Returns a copy of this `PanicVal`.
pub const fn to_panicval(&self, _: FmtArg) -> PanicVal<'a> {
*self
}
}

View File

@@ -0,0 +1,324 @@
/**
Derives the [`PanicFmt`](trait@crate::PanicFmt) trait.
This requires the `"derive"` feature, disabled by default.
This generates a generic [`PanicFmt`](trait@crate::PanicFmt) impl,
as well as one or more inherent `to_panicvals` method definitions
[as described in the trait docs](trait@crate::PanicFmt#implementor).
You can also use [`impl_panicfmt`] as an alternative that requires less time to compile
from scratch, but requires repeating the type definition.
[Jump straight to examples](#examples)
# Limitations
### Type parameters
Types with type parameters can't be generically formatted,
to work around this you can use either or both of these attributes:
- `#[pfmt(ignore(T))]`:
if the type parameter(s) are only used in marker types (eg: `PhantomData`).
- `#[pfmt(impl Foo<Bar, Baz>)]`:
to implement panic formatting with concrete type arguments
(this attribute can be used multiple times to add impls).
This limitation is caused by:
- the lack of trait bound support in stable const fns.
- the need to [have a concrete type argument](#concrete-pv-count)
[example below](#type-parameter-example)
### Const parameters
Const parameters must not affect the value of the `PanicFmt::PV_COUNT` of this type,
since the const parameter [must be replaceable with a concrete value](#concrete-pv-count).
<br>Note that arrays have a `PV_COUNT` of `1` for all lengths.
<a id = "concrete-pv-count"></a>
### Concrete `Self` type for `PanicFmt::PV_COUNT`
The `to_panicvals` method that this macro generates roughly returns a
```text
[PanicVal<'_>; <Self as PanicFmt>::PV_COUNT]
```
Because of limitations in stable const generics,
the generic arguments of `Self` in the above code must be replaced with concrete arguments,
requiring:
- Lifetime arguments to be replaced with `'_`
- Type arguments to be replaced with concrete types
(usually `()` or the concrete types used in [`#[pfmt(impl ....)]`](#pfmt-impl-attr) attributes)
- Const arguments to be replaced with concrete values (usually the default value for the type)
# Attributes
### Container attributes
Attributes used above the type definition.
### `#[pfmt(crate = foo::bar)]`
Replaces the path to `const_panic` with `foo::bar`
[example](#crate-example)
### `#[pfmt(debug_print)]`: <br>
For diagnostics, causes the derive macro to panic with the code generated by it.
<a id = "display_fmt-attr"></a>
### `#[pfmt(display_fmt = fn_expression)]`:
Calls the provided function when the deriving type is attempted to be Display-formatted.
[example below](#display_fmt-example)
<a id = "panicvals_lower_bound-attr"></a>
### `#[pfmt(panicvals_lower_bound = usize_expression)]`:
Tells the derive the minimum amount of [`PanicVal`]s that this type needs,
generally only useful in combination with the [`display_fmt`](#display_fmt-attr) attribute.
<a id = "pfmt-ignored-attr"></a>
### `#[pfmt(ignored(T, C))]`
Accepts the names of type and const parameters,
replacing the generic arguments in [`here`](#concrete-pv-count) with a concrete value.
For type parameters, this replaces the type parameter with `()` unless overriden,
and also tells the derive not to require `T: PanicFmt` in
the `PanicFmt` implementation for the deriving type
(since the type parameter is not formatted).
Const parameters are ignored by default,
replacing them with the default value for that type [^1]
The concrete value for each generic parameter can be overriden with `T = value`
examples:
- `#[pfmt(ignored(T))]`
- `#[pfmt(ignored(T = u16))]`
- `#[pfmt(ignored(T = u32, C))]`
- `#[pfmt(ignored(T, C = 100))]`
- `#[pfmt(ignored(U = str, A = false))]`
([more conplete example](#phantom-type-parameter-example))
[^1]: a private trait is used to get the default value for const parameters.
<a id = "pfmt-impl-attr"></a>
### `#[pfmt(impl Foo<Bar, BAZ>)]`
Tells the derive to generate an inherent `to_panicvals` method for the type in the attribute
(it must be the deriving type with concrete enough generic arguments).
examples:
- `#[pfmt(impl Foo<u32, 10>)]`
- `#[pfmt(impl<T> Foo<T, 'A'>)]`:
this also requires a [`#[pfmt(ignored(T))]`](#pfmt-ignored-attr) attribute
([more conplete example](#type-parameter-example))
# Examples
### Basic struct
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(
const_panic::concat_!(Foo { x: 3, y: &[3, 5, 8] }),
"Foo { x: 3, y: [3, 5, 8] }",
);
#[derive(PanicFmt)]
struct Foo<'a> {
x: u32,
y: &'a [u8],
}
```
### Basic enum
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(const_panic::concat_!(Foo::Bar), "Bar");
assert_eq!(
const_panic::concat_!(Foo::Baz("hello", true)),
"Baz(\"hello\", true)",
);
#[derive(PanicFmt)]
enum Foo {
Bar,
Baz(&'static str, bool),
}
```
<a id = "type-parameter-example"></a>
### Type parameters
This example demonstrates support for types with type parameters.
```rust
use const_panic::{FmtArg, PanicFmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<&str, u8> = Foo {
value: 100u8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_INT),
"Foo { value: 100, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<bool, &str> = Foo {
value: "hello",
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_STR),
r#"Foo { value: "hello", _marker: PhantomData }"#,
);
}
#[derive(Debug, PanicFmt)]
// Tells the derive that the `A` type parameter is not formatted,
// removing the `A: PanicFmt` bound in `impl<A, B> PanicFmt for Foo<A, B>`,
// and using `()` as the `A` type parmeter for
// `<Foo<....> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method.
#[pfmt(ignore(A))]
// Defines a `to_panicvals` method for `Foo<A, u8>`
#[pfmt(impl<A> Foo<A, u8>)]
// Defines a `to_panicvals` method for `Foo<A, &str>`
#[pfmt(impl<A> Foo<A, &str>)]
pub struct Foo<A, B> {
value: B,
_marker: PhantomData<A>,
}
```
<a id = "phantom-type-parameter-example"></a>
### Phantom Type parameters
This example demonstrates how type parameters can be ignored with
`#[pfmt(ignore(...))]`.
```rust
use const_panic::{FmtArg, PanicFmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<u8, bool, 100> = Foo{
value: 5,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_INT),
"Foo { value: 5, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<str, char, 200> = Foo {
value: 8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_STR),
r#"Foo { value: 8, _marker: PhantomData }"#,
);
}
#[derive(Debug, PanicFmt)]
// Tells the derive that the `A` and `B` type parameters are not formatted,
// removing the `A: PanicFmt` and `B: PanicFmt` bounds in the `PanicFmt` impl for `Foo`,
// and using `()` and `u8` as the `A` and `B` type parameters for
// `<Foo<(), u8> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method.
#[pfmt(ignore(A, B = u8))]
pub struct Foo<A: ?Sized, B, const X: u32> {
value: u32,
_marker: PhantomData<(PhantomData<A>, B)>,
}
```
### Const-generic struct
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(const_panic::concat_!(Foo([])), "Foo([])");
assert_eq!(const_panic::concat_!(Foo([3, 5, 8])), "Foo([3, 5, 8])");
#[derive(PanicFmt)]
struct Foo<const LEN: usize>([u8; LEN]);
```
<a id = "display_fmt-example"></a>
### Display formatting
This example demonstrates the [`display_fmt` attribute](#display_fmt-attr)
```rust
use const_panic::{FmtArg, PanicFmt, PanicVal};
assert_eq!(const_panic::concat_!(debug: Foo([3, 5, 8])), "Foo([3, 5, 8])");
assert_eq!(const_panic::concat_!(display: Foo([3, 5, 8])), "3 5 8");
#[derive(PanicFmt)]
#[pfmt(display_fmt = Self::display_fmt)]
// need this attribute to output more PanicVals in Display formatting than in Debug formatting.
#[pfmt(panicvals_lower_bound = 10)]
struct Foo([u8; 3]);
impl Foo {
const fn display_fmt(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::PV_COUNT] {
let [a, b, c] = self.0;
const_panic::flatten_panicvals!{fmtarg, Foo::PV_COUNT;
a, " ", b, " ", c
}
}
}
```
<a id = "crate-example"></a>
### Crate renaming
This example demonstrates how the `const_panic` crate can be renamed,
passing the new name to the derive macro.
```rust
# extern crate const_panic as cpanic;
# extern crate std as const_panic;
#
use cpanic::{FmtArg, PanicFmt};;
assert_eq!(cpanic::concat_!(Foo(Some(13))), "Foo(Some(13))");
#[derive(PanicFmt)]
#[pfmt(crate = cpanic)]
struct Foo(Option<u32>);
```
*/
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "derive")))]
pub use const_panic_proc_macros::PanicFmt;

190
vendor/const_panic/src/slice_stuff.rs vendored Normal file
View File

@@ -0,0 +1,190 @@
use crate::{
fmt::{FmtArg, PackedFmtArg, PanicFmt},
panic_val::{PanicVal, PanicVariant},
utils::Packed,
StdWrapper,
};
macro_rules! impl_panicfmt_array {
($(($variant:ident, $panicval_ctor:ident, $ty:ty)),* $(,)*) => {
#[derive(Copy, Clone)]
#[repr(packed)]
pub(crate) struct Slice<'s> {
pub(crate) fmtarg: PackedFmtArg,
pub(crate) vari: SliceV<'s>,
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub(crate) enum SliceV<'s> {
$(
$variant(Packed<&'s [$ty]>),
)*
}
impl<'s> Slice<'s> {
// length in elements
pub(crate) const fn arr_len(self) -> usize {
match self.vari {
$(
SliceV::$variant(Packed(arr)) => arr.len(),
)*
}
}
}
impl<'s> SliceV<'s> {
const fn get(self, index: usize, fmtarg: FmtArg) -> PanicVal<'s> {
match self {
$(
SliceV::$variant(Packed(arr)) => {
let elem: &'s <$ty as PanicFmt>::This = &arr[index];
StdWrapper(elem).to_panicval(fmtarg)
},
)*
}
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
impl<'s> PanicVal<'s> {
$(
/// Constructs a `PanicVal` from a slice.
pub const fn $panicval_ctor(this: &'s [$ty], mut fmtarg: FmtArg) -> PanicVal<'s> {
fmtarg = fmtarg.indent();
if this.is_empty() {
fmtarg = fmtarg.set_alternate(false);
}
PanicVal::__new(
PanicVariant::Slice(Slice{
fmtarg: fmtarg.pack(),
vari: SliceV::$variant(Packed(this)),
})
)
}
)*
}
$(
impl<'s> PanicFmt for [$ty] {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
impl<'s, const LEN: usize> PanicFmt for [$ty; LEN] {
type This = Self;
type Kind = crate::fmt::IsStdType;
const PV_COUNT: usize = 1;
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
impl<'s> StdWrapper<&'s [$ty]> {
/// Converts the slice to a single-element `PanicVal` array.
pub const fn to_panicvals(self: Self, f:FmtArg) -> [PanicVal<'s>;1] {
[PanicVal::$panicval_ctor(self.0, f)]
}
/// Converts the slice to a `PanicVal`.
pub const fn to_panicval(self: Self, f:FmtArg) -> PanicVal<'s> {
PanicVal::$panicval_ctor(self.0, f)
}
}
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
impl<'s, const LEN: usize> StdWrapper<&'s [$ty; LEN]> {
/// Converts the array to a single-element `PanicVal` array.
pub const fn to_panicvals(self: Self, f:FmtArg) -> [PanicVal<'s>;1] {
[PanicVal::$panicval_ctor(self.0, f)]
}
/// Converts the array to a `PanicVal`.
pub const fn to_panicval(self: Self, f:FmtArg) -> PanicVal<'s> {
PanicVal::$panicval_ctor(self.0, f)
}
}
)*
};
}
impl_panicfmt_array! {
(U8, from_slice_u8, u8),
(U16, from_slice_u16, u16),
(U32, from_slice_u32, u32),
(U64, from_slice_u64, u64),
(U128, from_slice_u128, u128),
(Usize, from_slice_usize, usize),
(I8, from_slice_i8, i8),
(I16, from_slice_i16, i16),
(I32, from_slice_i32, i32),
(I64, from_slice_i64, i64),
(I128, from_slice_i128, i128),
(Isize, from_slice_isize, isize),
(Bool, from_slice_bool, bool),
(Char, from_slice_char, char),
(Str, from_slice_str, &'s str),
}
#[derive(Copy, Clone)]
pub(crate) struct SliceIter<'s> {
slice: SliceV<'s>,
fmtarg: FmtArg,
state: IterState,
arr_len: u32,
}
#[derive(Copy, Clone, PartialEq, Eq)]
struct IterState(u32);
#[allow(non_upper_case_globals)]
impl IterState {
const Start: Self = Self(u32::MAX - 1);
const End: Self = Self(u32::MAX);
}
impl<'s> Slice<'s> {
pub(crate) const fn iter<'b>(&'b self) -> SliceIter<'s> {
SliceIter {
slice: self.vari,
fmtarg: self.fmtarg.unpack(),
state: IterState::Start,
arr_len: self.arr_len() as u32,
}
}
}
impl<'s> SliceIter<'s> {
pub(crate) const fn next(mut self) -> ([PanicVal<'s>; 2], Option<Self>) {
let fmtarg = self.fmtarg;
let ret = match self.state {
IterState::Start => {
self.state = if self.arr_len == 0 {
IterState::End
} else {
IterState(0)
};
[crate::fmt::OpenBracket.to_panicval(fmtarg), PanicVal::EMPTY]
}
IterState::End => {
let close_brace = crate::fmt::CloseBracket.to_panicval(fmtarg.unindent());
return ([close_brace, PanicVal::EMPTY], None);
}
IterState(x) => {
let comma = if x + 1 == self.arr_len {
self.state = IterState::End;
crate::fmt::COMMA_TERM
} else {
self.state = IterState(x + 1);
crate::fmt::COMMA_SEP
}
.to_panicval(fmtarg);
[self.slice.get(x as usize, fmtarg), comma]
}
};
(ret, Some(self))
}
}

View File

@@ -0,0 +1,38 @@
///
#[cfg_attr(feature = "derive", derive(crate::PanicFmt))]
pub enum Direction {
///
Up,
///
Down,
}
///
pub struct GenericStruct<'a> {
x: &'a [u8],
y: u8,
z: char,
}
impl_panicfmt! {
struct GenericStruct<'a> {
x: &'a [u8],
y: u8,
z: char,
}
}
///
pub struct ConcreteStruct {
x: [u8; 4],
y: u8,
z: char,
}
impl_panicfmt! {
struct ConcreteStruct {
x: [u8; 4],
y: u8,
z: char,
}
}

84
vendor/const_panic/src/test_utils.rs vendored Normal file
View File

@@ -0,0 +1,84 @@
use core::{
cmp::PartialEq,
fmt::{self, Debug},
};
pub struct TestString<const LEN: usize> {
pub(crate) len: usize,
pub(crate) buffer: [u8; LEN],
}
impl<const LEN: usize> TestString<LEN> {
pub fn get(&self) -> &[u8] {
&self.buffer[..self.len]
}
pub fn as_str(&self) -> &str {
core::str::from_utf8(self.get()).unwrap()
}
}
impl<const LEN: usize> Debug for TestString<LEN> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match core::str::from_utf8(self.get()) {
Ok(x) => Debug::fmt(x, f),
Err(_) => f
.debug_struct("TestString")
.field("len", &self.len)
.field("buffer", &self.buffer)
.finish(),
}
}
}
impl<const LEN: usize> PartialEq<str> for TestString<LEN> {
fn eq(&self, str: &str) -> bool {
core::str::from_utf8(self.get()).map_or(false, |this| this == str)
}
}
impl<const LEN: usize> PartialEq<&str> for TestString<LEN> {
fn eq(&self, str: &&str) -> bool {
self == *str
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! concat_fmt {
($length:expr, $max_len:expr; $($args:tt)*) => (
$crate::__concat_func_setup!{
(|args| $crate::for_tests::format_panic_message::<1024>(args, $length, $max_len))
[]
[$($args)*,]
}
)
}
// workaround for PhantomData changing to printing type argument
pub struct MyPhantomData<T: ?Sized>(core::marker::PhantomData<T>);
impl<T: ?Sized> Copy for MyPhantomData<T> {}
impl<T: ?Sized> Clone for MyPhantomData<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> core::fmt::Debug for MyPhantomData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_str("PhantomData")
}
}
impl<T: ?Sized> crate::PanicFmt for MyPhantomData<T> {
type This = Self;
type Kind = crate::IsCustomType;
const PV_COUNT: usize = 1;
}
impl<T: ?Sized> MyPhantomData<T> {
pub const NEW: Self = Self(core::marker::PhantomData);
pub const fn to_panicvals(self, _: crate::FmtArg) -> [crate::PanicVal<'static>; 1] {
[crate::PanicVal::write_str("PhantomData")]
}
}

295
vendor/const_panic/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,295 @@
//! Utility functions
use crate::debug_str_fmt::ForEscaping;
#[cfg(feature = "rust_1_64")]
#[cfg(test)]
mod utils_1_64_tests;
#[cfg(feature = "non_basic")]
mod non_basic_utils;
#[cfg(feature = "non_basic")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub use self::non_basic_utils::*;
/// Computes the minimum of `l` and `r`
pub const fn min_usize(l: usize, r: usize) -> usize {
if l < r {
l
} else {
r
}
}
/// Computes the maximum of `l` and `r`
pub const fn max_usize(l: usize, r: usize) -> usize {
if l > r {
l
} else {
r
}
}
#[derive(Copy, Clone)]
pub(crate) struct TailShortString<const LEN: usize> {
start: u8,
buffer: [u8; LEN],
}
pub(crate) type PreFmtString = TailShortString<{ string_cap::PREFMT }>;
pub(crate) mod string_cap {
/// The capacity of a [`ShortString`](crate::fmt::ShortString).
#[cfg(feature = "non_basic")]
pub const TINY: usize = 16;
// the TailShortString that's stored in PanicVal
pub(crate) const PREFMT: usize = 21;
// length of string to alternate binary format a 64 bit integer
pub(crate) const MEDIUM: usize = 66;
// length of string to alternate binary format a 128 bit integer
pub(crate) const LARGE: usize = 130;
}
impl<const LEN: usize> TailShortString<LEN> {
///
/// # Safety
///
/// `buffer` must be valid utf8 starting from the `start` index.
#[inline(always)]
pub(crate) const unsafe fn new(start: u8, buffer: [u8; LEN]) -> Self {
Self { start, buffer }
}
pub(crate) const fn len(&self) -> usize {
LEN - self.start as usize
}
pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> {
RangedBytes {
start: self.start as usize,
end: LEN,
bytes: &self.buffer,
}
}
}
////////////////////////////////////////////////////////
#[repr(packed)]
#[derive(Copy)]
pub(crate) struct Packed<T>(pub(crate) T);
impl<T: Copy> Clone for Packed<T> {
fn clone(&self) -> Self {
*self
}
}
////////////////////////////////////////////////////////
#[derive(Copy, Clone)]
pub(crate) struct RangedBytes<B> {
pub(crate) start: usize,
pub(crate) end: usize,
pub(crate) bytes: B,
}
impl<B> RangedBytes<B> {
pub(crate) const fn len(&self) -> usize {
self.end - self.start
}
}
impl RangedBytes<&'static [u8]> {
pub const EMPTY: Self = RangedBytes {
start: 0,
end: 0,
bytes: &[],
};
}
////////////////////////////////////////////////////////
#[derive(Copy, Clone)]
pub(crate) enum Sign {
Positive,
Negative = 1,
}
#[derive(Copy, Clone)]
pub(crate) enum WasTruncated {
Yes(usize),
No,
}
impl WasTruncated {
pub(crate) const fn get_length(self, len: usize) -> usize {
match self {
WasTruncated::Yes(x) => x,
WasTruncated::No => len,
}
}
}
const fn is_char_boundary(b: u8) -> bool {
(b as i8) >= -0x40
}
// truncates a utf8-encoded string to the character before the `truncate_to` index
//
pub(crate) const fn truncated_str_len(
ranged: RangedBytes<&[u8]>,
truncate_to: usize,
) -> WasTruncated {
if ranged.len() <= truncate_to {
WasTruncated::No
} else {
let mut i = ranged.start + truncate_to;
while i != ranged.start {
// if it's a non-continuation byte, break
if is_char_boundary(ranged.bytes[i]) {
break;
}
i -= 1;
}
WasTruncated::Yes(i - ranged.start)
}
}
pub(crate) const fn truncated_debug_str_len(
ranged: RangedBytes<&[u8]>,
truncate_to: usize,
) -> WasTruncated {
let blen = ranged.end;
// `* 4` because the longest escape is written like `\xNN` which is 4 bytes
// `+ 2` for the quote characters
if blen * 4 + 2 <= truncate_to {
WasTruncated::No
} else if truncate_to == 0 {
WasTruncated::Yes(0)
} else {
let mut i = ranged.start;
// = 1 for opening quote char
let mut fmtlen = 1;
loop {
let next_i = next_char_boundary(ranged, min_usize(i + 1, ranged.end));
let mut j = i;
while j < next_i {
fmtlen += ForEscaping::byte_len(ranged.bytes[j]);
j += 1;
}
if fmtlen > truncate_to {
break;
} else if next_i == ranged.end {
i = next_i;
break;
} else {
i = next_i;
}
}
if i == blen && fmtlen < truncate_to {
WasTruncated::No
} else {
WasTruncated::Yes(i - ranged.start)
}
}
}
const fn next_char_boundary(ranged: RangedBytes<&[u8]>, mut i: usize) -> usize {
while i < ranged.end && !is_char_boundary(ranged.bytes[i]) {
i += 1;
}
i
}
#[cfg_attr(feature = "test", derive(Debug, PartialEq))]
pub(crate) struct StartAndBytes<const LEN: usize> {
pub start: u8,
pub bytes: [u8; LEN],
}
#[track_caller]
pub(crate) const fn tail_byte_array<const TO: usize>(
len: usize,
input: &[u8],
) -> StartAndBytes<TO> {
assert!(len <= TO);
let mut bytes = [0u8; TO];
let start = TO - len;
let mut i = start;
let mut j = 0;
while j < len {
bytes[i] = input[j];
i += 1;
j += 1;
}
assert!(start < 256);
StartAndBytes {
start: start as u8,
bytes,
}
}
////////////////////////////////////////////////////////////////////////////////
/// Const equivalent of `&buffer[..upto]` with saturating indexing.
///
/// "saturating indexing" means that if `upto > buffer.len()`,
/// then this returns all of `buffer` instead of panicking.
///
/// # Example
///
/// ```rust
/// use const_panic::utils::bytes_up_to;
///
/// const BYTES: &[u8] = &[3, 5, 8, 13, 21, 34, 55, 89];
///
/// const SLICE: &[u8] = bytes_up_to(BYTES, 4);
/// assert_eq!(SLICE, &[3, 5, 8, 13][..]);
///
/// const WHOLE: &[u8] = bytes_up_to(BYTES, usize::MAX);
/// assert_eq!(WHOLE, &[3, 5, 8, 13, 21, 34, 55, 89][..]);
///
/// ```
pub const fn bytes_up_to(buffer: &[u8], upto: usize) -> &[u8] {
if upto > buffer.len() {
return buffer;
}
#[cfg(not(feature = "rust_1_64"))]
{
let mut to_truncate = buffer.len() - upto;
let mut out: &[u8] = buffer;
while to_truncate != 0 {
if let [rem @ .., _] = out {
out = rem;
}
to_truncate -= 1;
}
if out.len() != upto {
panic!("BUG!")
}
out
}
#[cfg(feature = "rust_1_64")]
{
// SAFETY: the above conditional ensures that `upto` doesn't
// create a partially-dangling slice
unsafe { core::slice::from_raw_parts(buffer.as_ptr(), upto) }
}
}

View File

@@ -0,0 +1,93 @@
use crate::{FmtArg, PanicVal};
/// For coercing a `&[PanicVal<'_>; LEN]` into a `&[PanicVal<'_>]`.
pub const fn panicvals_id<'a, 'b, const LEN: usize>(
array: &'b [PanicVal<'a>; LEN],
) -> &'b [PanicVal<'a>] {
array
}
/// Flattens a `&[&[PanicVal<'a>]]` into a `[PanicVal<'a>; LEN]`.
///
/// If `LEN` is greater than the amount of `PanicVal`s in the slices,
/// this fills the remaining array with [`PanicVal::EMPTY`].
///
/// # Panics
///
/// Panics if the amount of `PanicVal`s in the slices is greater than `LEN`.
///
pub const fn flatten_panicvals<'a, const LEN: usize>(
mut input: &[&[PanicVal<'a>]],
) -> [PanicVal<'a>; LEN] {
let mut out = [PanicVal::EMPTY; LEN];
let mut len = 0usize;
while let [mut outer, ref rinput @ ..] = *input {
while let [arg, ref router @ ..] = *outer {
out[len] = arg;
len += 1;
outer = router;
}
input = rinput
}
out
}
/// Gets the maximum value between `l` and `r`
///
/// # Example
///
/// ```rust
/// use const_panic::utils::max_usize;
///
/// assert_eq!(max_usize(5, 3), 5);
/// assert_eq!(max_usize(5, 8), 8);
///
/// ```
pub const fn max_usize(l: usize, r: usize) -> usize {
if l > r {
l
} else {
r
}
}
/// Gets the maximum value in `slice`, returns `0` if the slice is empty.
///
/// # Example
///
/// ```rust
/// use const_panic::utils::slice_max_usize;
///
/// assert_eq!(slice_max_usize(&[]), 0);
/// assert_eq!(slice_max_usize(&[3]), 3);
/// assert_eq!(slice_max_usize(&[5, 3]), 5);
/// assert_eq!(slice_max_usize(&[5, 8, 3]), 8);
/// assert_eq!(slice_max_usize(&[5, 13, 8, 3]), 13);
///
/// ```
pub const fn slice_max_usize(mut slice: &[usize]) -> usize {
let mut max = 0;
while let [x, ref rem @ ..] = *slice {
max = max_usize(max, x);
slice = rem;
}
max
}
#[doc(hidden)]
#[track_caller]
pub const fn assert_flatten_panicvals_length(expected_larger: usize, actual_value: usize) {
if actual_value > expected_larger {
crate::concat_panic(&[&[
PanicVal::write_str("length passed to flatten_panicvals macro ("),
PanicVal::from_usize(expected_larger, FmtArg::DISPLAY),
PanicVal::write_str(") is smaller than the computed length ("),
PanicVal::from_usize(actual_value, FmtArg::DISPLAY),
PanicVal::write_str(")"),
]]);
}
}

View File

@@ -0,0 +1,159 @@
use super::{tail_byte_array, StartAndBytes};
#[test]
fn tail_byte_array_eq_gt_tests() {
assert_eq!(
tail_byte_array::<0>(0, &[3]),
StartAndBytes {
bytes: [],
start: 0
}
);
assert_eq!(
tail_byte_array::<0>(0, &[3, 5]),
StartAndBytes {
bytes: [],
start: 0
}
);
assert_eq!(
tail_byte_array::<1>(0, &[3, 5]),
StartAndBytes {
bytes: [0],
start: 1
}
);
assert_eq!(
tail_byte_array::<1>(1, &[3, 5]),
StartAndBytes {
bytes: [3],
start: 0
}
);
assert_eq!(
tail_byte_array::<2>(0, &[3, 5]),
StartAndBytes {
bytes: [0, 0],
start: 2
}
);
assert_eq!(
tail_byte_array::<2>(1, &[3, 5]),
StartAndBytes {
bytes: [0, 3],
start: 1
}
);
assert_eq!(
tail_byte_array::<2>(2, &[3, 5]),
StartAndBytes {
bytes: [3, 5],
start: 0
}
);
assert_eq!(
tail_byte_array::<1>(0, &[]),
StartAndBytes {
bytes: [0],
start: 1
}
);
assert_eq!(
tail_byte_array::<2>(0, &[]),
StartAndBytes {
bytes: [0, 0],
start: 2
}
);
assert_eq!(
tail_byte_array::<1>(1, &[3]),
StartAndBytes {
bytes: [3],
start: 0
}
);
assert_eq!(
tail_byte_array::<2>(1, &[3]),
StartAndBytes {
bytes: [0, 3],
start: 1
}
);
assert_eq!(
tail_byte_array::<3>(1, &[3]),
StartAndBytes {
bytes: [0, 0, 3],
start: 2
}
);
assert_eq!(
tail_byte_array::<2>(1, &[3, 5]),
StartAndBytes {
bytes: [0, 3],
start: 1
}
);
assert_eq!(
tail_byte_array::<3>(1, &[3, 5]),
StartAndBytes {
bytes: [0, 0, 3],
start: 2
}
);
assert_eq!(
tail_byte_array::<4>(1, &[3, 5]),
StartAndBytes {
bytes: [0, 0, 0, 3],
start: 3
}
);
assert_eq!(
tail_byte_array::<2>(2, &[3, 5]),
StartAndBytes {
bytes: [3, 5],
start: 0
}
);
assert_eq!(
tail_byte_array::<3>(2, &[3, 5]),
StartAndBytes {
bytes: [0, 3, 5],
start: 1
}
);
assert_eq!(
tail_byte_array::<4>(2, &[3, 5]),
StartAndBytes {
bytes: [0, 0, 3, 5],
start: 2
}
);
}
#[test]
#[should_panic]
fn tail_byte_array_smaller_test_0() {
let _: StartAndBytes<0> = tail_byte_array(2, &[1]);
}
#[test]
#[should_panic]
fn tail_byte_array_smaller_test_1() {
let _: StartAndBytes<1> = tail_byte_array(3, &[1, 2]);
}
#[test]
#[should_panic]
fn tail_byte_array_smaller_test_2() {
let _: StartAndBytes<1> = tail_byte_array(3, &[1, 2, 3]);
}
#[test]
#[should_panic]
fn tail_byte_array_smaller_test_3() {
let _: StartAndBytes<2> = tail_byte_array(3, &[1, 2, 3, 4, 5, 6]);
}

44
vendor/const_panic/src/wrapper.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
use crate::PanicFmt;
/// A wrapper type used to define methods for std types.
///
/// Std types are coerced to this type through the approach used in the
/// [`coerce_fmt`] macro.
///
/// # Example
///
/// Formatting std types with this type's `to_panicvals` methods,
/// without using macros.
///
#[cfg_attr(feature = "non_basic", doc = "```rust")]
#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
/// use const_panic::{ArrayString, FmtArg, StdWrapper};
///
/// assert_eq!(
/// ArrayString::<99>::from_panicvals(
/// &StdWrapper("hello").to_panicvals(FmtArg::DEBUG)
/// ).unwrap(),
/// r#""hello""#
/// );
///
/// assert_eq!(
/// ArrayString::<99>::from_panicvals(
/// &StdWrapper(&[3u8, 5, 8]).to_panicvals(FmtArg::ALT_DEBUG)
/// ).unwrap(),
/// "[\n 3,\n 5,\n 8,\n]"
/// );
///
/// ```
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct StdWrapper<T>(pub T);
impl<T> PanicFmt for StdWrapper<T>
where
T: PanicFmt,
{
type This = Self;
type Kind = crate::fmt::IsCustomType;
const PV_COUNT: usize = T::PV_COUNT;
}