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}