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

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;
}