const_panic/fmt/non_basic_fmt.rs
1use crate::{ArrayString, PanicVal};
2
3use super::{FmtArg, IsCustomType, PanicFmt};
4
5/// For outputting an [alternate flag]-aware delimiter.
6///
7/// This was created for formatting structs and enum variants,
8/// so these delimiters have spaces around them to follow the <br>
9/// `Foo { bar: baz }`, `Foo(bar)`, and `[foo, bar]` style.
10///
11/// # Example
12///
13/// ```rust
14/// use const_panic::{
15/// fmt::{self, FmtArg},
16/// ArrayString,
17/// flatten_panicvals,
18/// };
19///
20/// // Debug formatting
21/// assert_eq!(
22/// const_panic::concat_!(FmtArg::DEBUG;
23/// open: fmt::EmptyDelimiter,
24/// 100u8, fmt::COMMA_SEP,
25/// false, fmt::COMMA_SEP,
26/// [0u16; 0], fmt::COMMA_SEP,
27/// // parenthesizing to pass this as a non-literal
28/// // otherwise the string is Display formatted
29/// ("really"), fmt::COMMA_TERM,
30/// close: "",
31/// ),
32/// " 100, false, [], \"really\""
33/// );
34///
35///
36/// // Alternate-Debug formatting
37/// assert_eq!(
38/// const_panic::concat_!(FmtArg::ALT_DEBUG;
39/// open: fmt::EmptyDelimiter,
40/// 100u8, fmt::COMMA_SEP,
41/// false, fmt::COMMA_SEP,
42/// [0u16; 0], fmt::COMMA_SEP,
43/// ("really"), fmt::COMMA_TERM,
44/// close: "",
45/// ),
46/// concat!(
47/// "\n",
48/// " 100,\n",
49/// " false,\n",
50/// " [],\n",
51/// " \"really\",\n",
52/// )
53/// );
54///
55/// ```
56///
57/// [alternate flag]: crate::FmtArg#structfield.is_alternate
58#[non_exhaustive]
59#[derive(Copy, Clone, PartialEq, Eq)]
60#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
61pub enum Delimiter {
62 /// `(`
63 OpenParen,
64 /// `)`
65 CloseParen,
66 /// `[`
67 OpenBracket,
68 /// `]`
69 CloseBracket,
70 /// ` { `
71 OpenBrace,
72 /// ` }`
73 CloseBrace,
74 /// An empty delimiter,
75 /// exists only to add whitespace on the next line when
76 /// the alternate flag is enabled.
77 Empty,
78}
79
80pub use self::Delimiter::{
81 CloseBrace, CloseBracket, CloseParen, Empty as EmptyDelimiter, OpenBrace, OpenBracket,
82 OpenParen,
83};
84
85impl Delimiter {
86 /// Converts this `Delimiter` into an array of one `PanicVal`
87 ///
88 /// When the [alternate flag] is enabled, this and the `to_panicval` method output:
89 /// - the delimiter
90 /// - a newline
91 /// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces
92 ///
93 /// When the [alternate flag] is disabled,
94 /// these methods output braces with spaces around them,
95 /// the empty delimiter as one space,
96 /// and the remaining delimiters with no spaces around them.
97 ///
98 /// [alternate flag]: crate::FmtArg#structfield.is_alternate
99 ///
100 pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] {
101 [self.to_panicval(f)]
102 }
103 /// Converts this `Delimiter` into a `PanicVal`
104 pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> {
105 match (self, f.is_alternate) {
106 (Self::OpenParen, false) => PanicVal::write_str("("),
107 (Self::CloseParen, false) => PanicVal::write_str(")"),
108 (Self::OpenBracket, false) => PanicVal::write_str("["),
109 (Self::CloseBracket, false) => PanicVal::write_str("]"),
110 (Self::OpenBrace, false) => PanicVal::write_str(" { "),
111 (Self::CloseBrace, false) => PanicVal::write_str(" }"),
112 (Self::Empty, false) => PanicVal::write_str(" "),
113 (Self::OpenParen, true) => PanicVal::write_str("(\n").with_rightpad(f),
114 (Self::CloseParen, true) => PanicVal::write_str(")").with_leftpad(f),
115 (Self::OpenBracket, true) => PanicVal::write_str("[\n").with_rightpad(f),
116 (Self::CloseBracket, true) => PanicVal::write_str("]").with_leftpad(f),
117 (Self::OpenBrace, true) => PanicVal::write_str(" {\n").with_rightpad(f),
118 (Self::CloseBrace, true) => PanicVal::write_str("}").with_leftpad(f),
119 (Self::Empty, true) => PanicVal::write_str("\n").with_rightpad(f),
120 }
121 }
122}
123
124impl PanicFmt for Delimiter {
125 type This = Self;
126 type Kind = IsCustomType;
127 const PV_COUNT: usize = 1;
128}
129
130////////////////////////////////////////////////////////////////////////////////
131
132/// How much indentation (in spaces) is added with [`FmtArg::indent`],
133/// and removed with [`FmtArg::unindent`].
134///
135/// [The FmtArg.indentation field](crate::FmtArg#structfield.indentation)
136/// is used by [`fmt::Delimiter`](crate::fmt::Delimiter)
137/// and by [`fmt::Separator`](crate::fmt::Separator),
138/// when the [`is_alternate`](crate::FmtArg#structfield.is_alternate) flag is enabled.
139#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
140pub const INDENTATION_STEP: u8 = 4;
141
142////////////////////////////////////////////////////////////////////////////////
143
144/// A stack allocated string type that's convertible into
145/// [`PanicVal<'static>`](PanicVal),
146/// with [`SHORT_STRING_CAP`] capacity.
147///
148/// # Example
149///
150/// ```rust
151/// use const_panic::{
152/// fmt::ShortString,
153/// ArrayString, FmtArg, PanicVal,
154/// };
155///
156/// let pv: PanicVal<'static> =
157/// PanicVal::write_short_str(ShortString::new("3,13,21,34,55,89"));
158///
159/// assert_eq!(ArrayString::<20>::from_panicvals(&[pv]).unwrap(), "3,13,21,34,55,89");
160///
161///
162/// let pv_debug: PanicVal<'static> =
163/// PanicVal::from_short_str(ShortString::new("foo\n\0bar"), FmtArg::DEBUG);
164///
165/// assert_eq!(
166/// ArrayString::<20>::from_panicvals(&[pv_debug]).unwrap(),
167/// "\"foo\\n\\x00bar\"",
168/// );
169///
170/// ```
171#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
172pub type ShortString = ArrayString<SHORT_STRING_CAP>;
173
174pub use crate::utils::string_cap::TINY as SHORT_STRING_CAP;
175
176impl<'a> PanicVal<'a> {
177 /// Constructs a `PanicVal` from a `ShortString`.
178 pub const fn from_short_str(this: ShortString, f: FmtArg) -> PanicVal<'a> {
179 use crate::panic_val::{PanicVariant, StrFmt};
180 PanicVal::__new(PanicVariant::ShortString(StrFmt::new(f), this.to_compact()))
181 }
182}
183
184////////////////////////////////////////////////////////////////////////////////
185
186/// For computing the [`PanicFmt::PV_COUNT`] of a struct or enum variant,
187/// with the [`call`](ComputePvCount::call) method.
188///
189/// This assumes that you write the `to_panicvals` method like in the
190/// [`flatten_panicvals` examples](crate::flatten_panicvals#examples)
191///
192/// # Examples
193///
194/// These examples demonstrates how to use this type by itself,
195/// for a more complete example you can look at the
196/// [`flatten_panicvals` examples](crate::flatten_panicvals#examples)
197///
198/// ### Struct
199///
200/// ```rust
201/// use const_panic::{ComputePvCount, PanicFmt};
202///
203/// struct Foo<'a> {
204/// x: u32,
205/// y: &'a [&'a str],
206/// z: Bar,
207/// }
208/// # type Bar = u8;
209///
210/// impl PanicFmt for Foo<'_> {
211/// type This = Self;
212/// type Kind = const_panic::IsCustomType;
213///
214/// const PV_COUNT: usize = ComputePvCount{
215/// field_amount: 3,
216/// summed_pv_count:
217/// u32::PV_COUNT +
218/// <&[&str]>::PV_COUNT +
219/// <Bar>::PV_COUNT,
220/// delimiter: const_panic::TypeDelim::Braced,
221/// }.call();
222/// }
223///
224/// assert_eq!(Foo::PV_COUNT, 12);
225///
226/// ```
227///
228/// ### Enum
229///
230/// ```rust
231/// use const_panic::{ComputePvCount, PanicFmt};
232///
233/// enum Foo {
234/// Bar,
235/// Baz(u32, u64),
236/// }
237/// # type Bar = u8;
238///
239/// impl PanicFmt for Foo {
240/// type This = Self;
241/// type Kind = const_panic::IsCustomType;
242///
243/// const PV_COUNT: usize = const_panic::utils::slice_max_usize(&[
244/// ComputePvCount{
245/// field_amount: 0,
246/// summed_pv_count: 0,
247/// delimiter: const_panic::TypeDelim::Braced,
248/// }.call(),
249/// ComputePvCount{
250/// field_amount: 2,
251/// summed_pv_count: <u32>::PV_COUNT + <u64>::PV_COUNT,
252/// delimiter: const_panic::TypeDelim::Tupled,
253/// }.call(),
254/// ]);
255/// }
256///
257/// assert_eq!(Foo::PV_COUNT, 7);
258///
259/// ```
260///
261///
262///
263///
264///
265#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
266pub struct ComputePvCount {
267 /// The amount of fields in the struct
268 pub field_amount: usize,
269 /// The summed up amount of `PanicVal`s that all the fields produce.
270 ///
271 /// Eg: for a struct with `Bar` and `Qux` fields, this would be
272 /// `<Bar as PanicFmt>::PV_COUNT + <Qux as PanicFmt>::PV_COUNT`,
273 ///
274 pub summed_pv_count: usize,
275 /// Whether it's a tupled or braced struct/variant.
276 pub delimiter: TypeDelim,
277}
278
279impl ComputePvCount {
280 /// Does the computation.
281 pub const fn call(&self) -> usize {
282 // field-less structs and variants don't output the empty delimiter
283 if self.field_amount == 0 {
284 return 1;
285 }
286
287 const TYPE_NAME: usize = 1;
288 const DELIM_TOKENS: usize = 2;
289 let field_tokens = match self.delimiter {
290 TypeDelim::Tupled => self.field_amount,
291 TypeDelim::Braced => 2 * self.field_amount,
292 };
293
294 TYPE_NAME + DELIM_TOKENS + field_tokens + self.summed_pv_count
295 }
296}
297
298/// Whether a struct or variant is Tupled or Braced.
299///
300/// Unit structs/variants are considered braced.
301///
302/// # Example
303///
304/// ### Formatting
305///
306/// ```rust
307/// use const_panic::{
308/// fmt::{self, FmtArg, TypeDelim},
309/// ArrayString,
310/// flatten_panicvals,
311/// };
312///
313/// {
314/// assert_eq!(
315/// const_panic::concat_!(FmtArg::DEBUG;
316/// "Foo",
317/// open: TypeDelim::Tupled.open(),
318/// 10u8, fmt::COMMA_SEP,
319/// false, fmt::COMMA_TERM,
320/// close: TypeDelim::Tupled.close(),
321/// ),
322/// "Foo(10, false)"
323/// );
324/// }
325///
326/// {
327/// assert_eq!(
328/// const_panic::concat_!(FmtArg::DEBUG;
329/// "Bar",
330/// open: TypeDelim::Braced.open(),
331/// "x: ", debug: "hello", fmt::COMMA_SEP,
332/// "y: ", true, fmt::COMMA_TERM,
333/// close: TypeDelim::Braced.close(),
334/// ),
335/// "Bar { x: \"hello\", y: true }"
336/// );
337/// }
338///
339/// ```
340///
341#[derive(Copy, Clone, PartialEq, Eq)]
342#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
343pub enum TypeDelim {
344 /// A `Foo(Bar)` type or variant
345 Tupled,
346 /// A `Foo{bar: Baz}` type or variant
347 Braced,
348}
349
350impl TypeDelim {
351 /// Gets the delimiters that surround the fields of a struct or variant.
352 pub const fn get_open_and_close(self) -> (Delimiter, Delimiter) {
353 (self.open(), self.close())
354 }
355 /// Gets the group delimiter that precedes the fields of a struct or variant.
356 pub const fn open(self) -> Delimiter {
357 match self {
358 Self::Tupled => Delimiter::OpenParen,
359 Self::Braced => Delimiter::OpenBrace,
360 }
361 }
362 /// Gets the group delimiter that follows the fields of a struct or variant.
363 pub const fn close(self) -> Delimiter {
364 match self {
365 Self::Tupled => Delimiter::CloseParen,
366 Self::Braced => Delimiter::CloseBrace,
367 }
368 }
369}
370
371////////////////////////////////////////////////////////////////////////////////
372
373/// An [alternate-flag-aware] comma separator for use between fields or elements.
374///
375/// When the alternate flag is enabled, this puts each field/element on its own line.
376///
377/// # Example
378///
379/// ```rust
380/// use const_panic::{
381/// fmt::{self, FmtArg},
382/// ArrayString,
383/// flatten_panicvals,
384/// };
385///
386/// // Debug formatting
387/// assert_eq!(
388/// const_panic::concat_!(FmtArg::DEBUG;
389/// open: fmt::OpenBracket,
390/// 100u8, fmt::COMMA_SEP,
391/// false, fmt::COMMA_SEP,
392/// [0u16; 0], fmt::COMMA_SEP,
393/// // fmt::COMMA_TERM always goes after the last field
394/// debug: "really", fmt::COMMA_TERM,
395/// close: fmt::CloseBracket,
396/// ),
397/// "[100, false, [], \"really\"]"
398/// );
399///
400///
401/// // Alternate-Debug formatting
402/// assert_eq!(
403/// const_panic::concat_!(FmtArg::ALT_DEBUG;
404/// open: fmt::OpenBracket,
405/// 100u8, fmt::COMMA_SEP,
406/// false, fmt::COMMA_SEP,
407/// [0u16; 0], fmt::COMMA_SEP,
408/// // fmt::COMMA_TERM always goes after the last field
409/// debug: "really", fmt::COMMA_TERM,
410/// close: fmt::CloseBracket,
411/// ),
412/// concat!(
413/// "[\n",
414/// " 100,\n",
415/// " false,\n",
416/// " [],\n",
417/// " \"really\",\n",
418/// "]",
419/// )
420/// );
421///
422/// ```
423///
424/// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate
425#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
426pub const COMMA_SEP: Separator<'_> = Separator::new(",", IsLast::No);
427
428/// An [alternate-flag-aware] comma for use after the last field or element.
429///
430/// For an example, you can look at [the one for `COMMA_SEP`](COMMA_SEP#example)
431///
432/// When the alternate flag is enabled, this puts each field/element on its own line.
433///
434/// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate
435#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
436pub const COMMA_TERM: Separator<'_> = Separator::new(",", IsLast::Yes);
437
438/// For telling [`Separator`] whether it comes after the last field or not.
439#[derive(Copy, Clone, PartialEq, Eq)]
440#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
441pub enum IsLast {
442 ///
443 Yes,
444 ///
445 No,
446}
447
448/// For [alternate flag]-aware separation of fields, collection elements, etc.
449///
450/// # Example
451///
452/// ```rust
453/// use const_panic::{
454/// fmt::{self, FmtArg, IsLast, Separator},
455/// ArrayString,
456/// flatten_panicvals,
457/// };
458///
459/// const SEMICOLON_SEP: Separator = Separator::new(";", IsLast::No);
460/// const SEMICOLON_TERM: Separator = Separator::new(";", IsLast::Yes);
461///
462/// // Debug formatting
463/// assert_eq!(
464/// const_panic::concat_!(FmtArg::DEBUG;
465/// open: fmt::OpenBrace,
466/// debug: "foo", SEMICOLON_SEP,
467/// [3u8, 5, 8], SEMICOLON_SEP,
468/// false, SEMICOLON_TERM,
469/// close: fmt::CloseBrace,
470/// ),
471/// // the space before the brace is because Delimiter is intended for
472/// // formatting structs and enum variants.
473/// " { \"foo\"; [3, 5, 8]; false }"
474/// );
475///
476///
477/// // Alternate-Debug formatting
478/// assert_eq!(
479/// const_panic::concat_!(FmtArg::ALT_DEBUG;
480/// open: fmt::OpenBrace,
481/// debug: "foo", SEMICOLON_SEP,
482/// debug: [3u8, 5, 8], SEMICOLON_SEP,
483/// false, SEMICOLON_TERM,
484/// close: fmt::CloseBrace,
485/// ),
486/// concat!(
487/// " {\n",
488/// " \"foo\";\n",
489/// " [3, 5, 8];\n",
490/// " false;\n",
491/// "}",
492/// )
493/// );
494/// ```
495///
496/// [alternate flag]: crate::FmtArg#structfield.is_alternate
497#[derive(Copy, Clone)]
498#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
499pub struct Separator<'a>(&'a str, IsLast);
500
501impl<'a> Separator<'a> {
502 /// Constructs a `Separator` from a custom separator.
503 ///
504 /// # Panics
505 ///
506 /// Panics if `string` is longer than 12 bytes.
507 ///
508 pub const fn new(string: &'a str, is_last_field: IsLast) -> Self {
509 if string.len() > 12 {
510 crate::concat_panic(&[&[
511 PanicVal::write_str("expected a string shorter than 12 bytes,"),
512 PanicVal::write_str("actual length: "),
513 PanicVal::from_usize(string.len(), FmtArg::DISPLAY),
514 PanicVal::write_str(", string: "),
515 PanicVal::from_str(string, FmtArg::DEBUG),
516 ]])
517 }
518
519 Separator(string, is_last_field)
520 }
521
522 /// Converts this `Separator` into an array of one `PanicVal`.
523 /// Otherwise does the same as [`to_panicval`](Self::to_panicval)
524 pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] {
525 [PanicVal::from_element_separator(self.0, self.1, f)]
526 }
527
528 /// Converts this `Separator` into a `PanicVal`.
529 ///
530 /// When the [alternate flag] is enabled, this and the `to_panicvals` method output:
531 /// - the separator
532 /// - a newline
533 /// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces
534 /// if constructed with [`IsLast::No`]
535 ///
536 /// When the [alternate flag] is disabled,
537 /// these methods output the separator and a single space
538 /// if constructed with [`IsLast::No`],
539 /// otherwise output nothing.
540 ///
541 /// [alternate flag]: crate::FmtArg#structfield.is_alternate
542 pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> {
543 PanicVal::from_element_separator(self.0, self.1, f)
544 }
545}
546
547impl PanicFmt for Separator<'_> {
548 type This = Self;
549 type Kind = IsCustomType;
550 const PV_COUNT: usize = 1;
551}