const_panic/
macros.rs

1#[macro_use]
2mod concat_assert;
3
4#[cfg(feature = "non_basic")]
5#[macro_use]
6pub(crate) mod concat_macro;
7
8#[cfg(feature = "non_basic")]
9#[macro_use]
10mod non_basic_macros;
11
12#[cfg(feature = "non_basic")]
13#[macro_use]
14mod macro_utils;
15
16#[cfg(feature = "non_basic")]
17#[macro_use]
18mod impl_panicfmt;
19
20#[macro_use]
21mod unwrapping;
22
23#[doc(hidden)]
24#[macro_export]
25macro_rules! __write_array {
26    ($array:expr, $len:expr, $value:expr) => {
27        $array[$len] = $value;
28        $len += 1;
29    };
30}
31
32#[doc(hidden)]
33#[macro_export]
34macro_rules! __write_array_checked {
35    ($array:expr, $len:expr, $value:expr) => {
36        if $array.len() > $len {
37            $array[$len] = $value;
38            $len += 1;
39        }
40    };
41}
42
43/// Coerces `$reff` to a type that has a `to_panicvals` method,
44/// which is expected to return a `[PanicVal<'_>; LEN]`.
45///
46/// # Limitations
47///
48#[doc = crate::doc_macros::limitation_docs!()]
49///
50/// # Example
51///
52/// This example uses [`const_panic::ArrayString`](crate::ArrayString)
53/// to show what the values format into,
54/// which requires the `"non_basic"` crate feature (enabled by default).
55///
56#[cfg_attr(feature = "non_basic", doc = "```rust")]
57#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
58/// use const_panic::{ArrayString, FmtArg, IsCustomType, PanicFmt, PanicVal, coerce_fmt};
59///
60/// type AS = ArrayString<100>;
61///
62/// assert_eq!(
63///     AS::from_panicvals(&coerce_fmt!(100u8).to_panicvals(FmtArg::DEBUG)).unwrap(),
64///     "100",
65/// );
66///
67/// assert_eq!(
68///     AS::from_panicvals(&coerce_fmt!("hello\n").to_panicvals(FmtArg::DEBUG)).unwrap(),
69///     r#""hello\n""#,
70/// );
71///
72/// assert_eq!(
73///     AS::from_panicvals(&coerce_fmt!(IsReal::No).to_panicvals(FmtArg::DEBUG)).unwrap(),
74///     "No",
75/// );
76///
77/// assert_eq!(
78///     AS::from_panicvals(&coerce_fmt!(IsReal::Yes).to_panicvals(FmtArg::DEBUG)).unwrap(),
79///     "Yes",
80/// );
81///
82///
83///
84/// enum IsReal{Yes, No}
85///
86/// // All the code below manually implements panic formatting for a field-less enum.
87/// // This can be written concisely with the `PanicFmt` derive or `impl_panicfmt` macro.
88/// impl PanicFmt for IsReal {
89///     type This = Self;
90///     type Kind = IsCustomType;
91///     const PV_COUNT: usize = 1;
92/// }
93///
94/// impl IsReal {
95///     pub const fn to_panicvals(&self, _f: FmtArg) -> [PanicVal<'_>; IsReal::PV_COUNT] {
96///         let x = match self {
97///             Self::Yes => "Yes",
98///             Self::No => "No",
99///         };
100///         [PanicVal::write_str(x)]
101///     }
102/// }
103///
104/// ```
105#[macro_export]
106macro_rules! coerce_fmt {
107    ($reff:expr) => {
108        match &$reff {
109            reff => $crate::__::PanicFmt::PROOF.infer(reff).coerce(reff),
110        }
111    };
112}
113
114/// Panics with the concanenation of the arguments.
115///
116/// [**Examples below**](#examples)
117///
118/// # Syntax
119///
120/// This macro uses this syntax:
121/// ```text
122/// concat_panic!(
123///     $($fmtarg:expr;)?
124///     $(
125///         $( $format_override:tt: )? $arg_to_fmt:expr
126///     ),*
127///     $(,)?
128/// )
129/// ```
130///
131/// `$fmtarg` is an optional [`FmtArg`](crate::FmtArg) argument
132/// which defaults to `FmtArg::DEBUG`,
133/// determining how non-literal `$arg_to_fmt` arguments are formatted.
134///
135/// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument,
136/// changing how that `$arg_to_fmt` argument is formatted.
137///
138#[doc = formatting_docs!()]
139///
140/// # Limitations
141///
142#[doc = crate::doc_macros::limitation_docs!()]
143///
144/// # Examples
145///
146/// ### `Odd`-type
147///
148/// ```rust, compile_fail
149/// use const_panic::concat_panic;
150///
151/// use odd::Odd;
152///
153/// # fn main(){
154/// const _: Odd = match Odd::new(3 * 4) {
155///     Ok(x) => x,
156///     Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
157/// };
158/// # }
159///
160/// mod odd {
161///     pub struct Odd(u32);
162///
163///     impl Odd {
164///         pub const fn new(n: u32) -> Result<Odd, Even> {
165///             if n % 2 == 1 {
166///                 Ok(Odd(n))
167///             } else {
168///                 Err(Even(n))
169///             }
170///         }
171///     }
172///
173/// #   /*
174///     #[derive(const_panic::PanicFmt))]
175/// #   */
176///     pub struct Even(u32);
177/// #
178/// #   impl const_panic::PanicFmt for Even {
179/// #       type This = Self;
180/// #       type Kind = const_panic::IsCustomType;
181/// #       const PV_COUNT: usize = 1;
182/// #   }
183/// #   impl Even {
184/// #       pub const fn to_panicvals(
185/// #           &self,
186/// #           f: const_panic::FmtArg,
187/// #       ) -> [const_panic::PanicVal<'static>; 1] {
188/// #           const_panic::StdWrapper(&self.0).to_panicvals(f)
189/// #       }
190/// #   }
191/// }
192///
193/// ```
194/// produces this compile-time error:
195/// ```text
196/// error[E0080]: evaluation of constant value failed
197///   --> src/macros.rs:188:15
198///    |
199/// 10 |     Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
200///    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
201/// expected odd number, got `12`', src/macros.rs:10:15
202///    |
203///    = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
204///
205/// ```
206///
207///
208/// ### All the syntax
209///
210/// This example demonstrates using all of the syntax of this macro.
211///
212/// ```compile_fail
213/// use const_panic::{FmtArg, concat_panic, fmt};
214///
215/// const _: () = concat_panic!{
216///     // the optional `$fmtarg` parameter.
217///     // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
218///     FmtArg::ALT_DEBUG;
219///
220///     "\n\nshowing off literals:\n",
221///     100u8,
222///     "hello",
223///
224///     "\n\nnon-literals with formatting determined by the $fmtarg parameter:\n",
225///     // this is considered a non-literal, because it's inside other tokens.
226///     ("a non-literal"),
227///     [100u8, 200],
228///
229///     "\n\nexplicitly debug formatted:\n",
230///     debug: "foo",
231///     // `{?}:` is The same as `debug:`
232///     {?}: "bar",
233///
234///     "\n\nalternate debug formatted:\n",
235///     alt_debug: ["foo"],
236///     // `{#?}:` is The same as `alt_debug:`
237///     {#?}: "bar",
238///
239///     "\n\ndisplay formatted:\n",
240///     display: "baz",
241///     // `{}:` is The same as `display:`
242///     {}: ["qux", "aaa"],
243///
244///     "\n\nalternate display formatted:",
245///     alt_display: ["bbb", "ccc"],
246///     // `{#}:` is The same as `alt_display:`
247///     {#}: ["bbb", "ccc"],
248///
249///     "\n\nbinary formatted:\n",
250///     bin: [3u8, 5, 8, 13],
251///     // `{b}:` is The same as `bin:`
252///     {b}: [3u8, 5, 8, 13],
253///
254///     "\n\nalternate-binary formatted:\n",
255///     alt_bin: [21u8, 34, 55, 89],
256///     // `{#b}:` is The same as `alt_bin:`
257///     {#b}: [21u8, 34, 55, 89],
258///
259///     "\n\nhexadecimal formatted:\n",
260///     hex: [3u8, 5, 8, 13],
261///     // `{X}:` is The same as `hex:`
262///     {X}: [3u8, 5, 8, 13],
263///
264///     "\n\nalternate-hexadecimal formatted:\n",
265///     alt_hex: [21u8, 34, 55, 89],
266///     // `{#X}:` is The same as `alt_hex:`
267///     {#X}: [21u8, 34, 55, 89],
268///
269///     "\n\n",
270/// };
271///
272/// ```
273/// The above code produces this compile-time error:
274/// ```text
275/// error[E0080]: evaluation of constant value failed
276///   --> src/macros.rs:186:15
277///    |
278/// 6  |   const _: () = concat_panic!{
279///    |  _______________^
280/// 7  | |     // the optional `$fmtarg` parameter.
281/// 8  | |     // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
282/// 9  | |     FmtArg::ALT_DEBUG;
283/// ...  |
284/// 60 | |     "\n\n",
285/// 61 | | };
286///    | |_^ the evaluated program panicked at '
287///
288/// showing off literals:
289/// 100hello
290///
291/// non-literals with formatting determined by the $fmtarg parameter:
292/// "a non-literal"[
293///     100,
294///     200,
295/// ]
296///
297/// explicitly debug formatted:
298/// "foo""bar"
299///
300/// alternate debug formatted:
301/// [
302///     "foo",
303/// ]"bar"
304///
305/// display formatted:
306/// baz[qux, aaa]
307///
308/// alternate display formatted:[
309///     bbb,
310///     ccc,
311/// ][
312///     bbb,
313///     ccc,
314/// ]
315///
316/// binary formatted:
317/// [11, 101, 1000, 1101][11, 101, 1000, 1101]
318///
319/// alternate-binary formatted:
320/// [
321///     0b10101,
322///     0b100010,
323///     0b110111,
324///     0b1011001,
325/// ][
326///     0b10101,
327///     0b100010,
328///     0b110111,
329///     0b1011001,
330/// ]
331///
332/// hexadecimal formatted:
333/// [3, 5, 8, D][3, 5, 8, D]
334///
335/// alternate-hexadecimal formatted:
336/// [
337///     0x15,
338///     0x22,
339///     0x37,
340///     0x59,
341/// ][
342///     0x15,
343///     0x22,
344///     0x37,
345///     0x59,
346/// ]
347///
348/// ', src/macros.rs:6:15
349///    |
350///    = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
351///
352/// error: aborting due to previous error
353///
354/// ```
355///
356#[macro_export]
357macro_rules! concat_panic {
358    ($($args:tt)*) => (
359        $crate::__concat_func_setup!{
360            (|args| $crate::concat_panic(args))
361            []
362            [$($args)*,]
363        }
364    )
365}
366
367// This macro takes the optional `$fmt:expr;` argument before everything else.
368// But I had to parse the argument manually,
369// because `$fmt:expr;` fails compilation instead of trying the following branches
370// when the argument isn't valid expression syntax.
371#[doc(hidden)]
372#[macro_export]
373macro_rules! __concat_func_setup {
374    ($args:tt $prev:tt [$($fmt:tt).*; $($rem:tt)* ]) => ({
375        let mut fmt: $crate::FmtArg = $($fmt).*;
376        $crate::__concat_func!{fmt $args $prev [$($rem)*]}
377    });
378    ($args:tt $prev:tt [$(:: $(@$_dummy:tt@)?)? $($fmt:ident)::* ; $($rem:tt)* ]) => ({
379        let mut fmt: $crate::FmtArg = $(:: $($_dummy)?)? $($fmt)::*;
380        $crate::__concat_func!{fmt $args $prev [$($rem)*]}
381    });
382    ($args:tt $prev:tt $rem:tt) => ({
383        let mut fmt: $crate::FmtArg = $crate::FmtArg::DEBUG;
384        $crate::__concat_func!{fmt $args $prev $rem}
385    });
386}
387#[doc(hidden)]
388#[macro_export]
389macro_rules! __concat_func {
390    ($fmt:ident $args:tt [$($prev:tt)*] [$keyword:tt: $expr:expr, $($rem:tt)* ]) => {
391        $crate::__concat_func!{
392            $fmt
393            $args
394            [$($prev)* ($crate::__set_fmt_from_kw!($keyword, $fmt), $expr)]
395            [$($rem)*]
396        }
397    };
398    ($fmt:ident $args:tt [$($prev:tt)*] [$expr:literal, $($rem:tt)* ]) => {
399        $crate::__concat_func!{
400            $fmt
401            $args
402            [$($prev)* ($crate::__set_fmt_from_kw!(display, $fmt), $expr)]
403            [$($rem)*]
404        }
405    };
406    ($fmt:ident $args:tt [$($prev:tt)*] [$expr:expr, $($rem:tt)* ]) => {
407        $crate::__concat_func!{
408            $fmt
409            $args
410            [$($prev)* ($fmt, $expr)]
411            [$($rem)*]
412        }
413    };
414    ($fmt:ident (|$args:ident| $function_call:expr) [$(($fmt_arg:expr, $reff:expr))*] [$(,)*]) => {
415        match &[
416            $(
417                $crate::StdWrapper(
418                    &$crate::coerce_fmt!($reff)
419                    .to_panicvals($fmt_arg)
420                ).deref_panic_vals(),
421            )*
422        ] {
423            $args => $function_call,
424        }
425    };
426}
427
428#[doc(hidden)]
429#[macro_export]
430macro_rules! __set_fmt_from_kw {
431    (open, $fmtarg:ident) => {{
432        $fmtarg = $fmtarg.indent();
433        $fmtarg.set_display()
434    }};
435    (close, $fmtarg:ident) => {{
436        $fmtarg = $fmtarg.unindent();
437        $fmtarg.set_display()
438    }};
439    (display, $fmtarg:ident) => {
440        $fmtarg.set_display().set_alternate(false)
441    };
442    ({}, $fmtarg:ident) => {
443        $fmtarg.set_display().set_alternate(false)
444    };
445    (alt_display, $fmtarg:ident) => {
446        $fmtarg.set_display().set_alternate(true)
447    };
448    ({#}, $fmtarg:ident) => {
449        $fmtarg.set_display().set_alternate(true)
450    };
451    (debug, $fmtarg:ident) => {
452        $fmtarg.set_debug().set_alternate(false)
453    };
454    ({?}, $fmtarg:ident) => {
455        $fmtarg.set_debug().set_alternate(false)
456    };
457    (alt_debug, $fmtarg:ident) => {
458        $fmtarg.set_debug().set_alternate(true)
459    };
460    ({#?}, $fmtarg:ident) => {
461        $fmtarg.set_debug().set_alternate(true)
462    };
463    (hex, $fmtarg:ident) => {
464        $fmtarg.set_hex().set_alternate(false)
465    };
466    ({X}, $fmtarg:ident) => {
467        $fmtarg.set_hex().set_alternate(false)
468    };
469    (alt_hex, $fmtarg:ident) => {
470        $fmtarg.set_hex().set_alternate(true)
471    };
472    ({#X}, $fmtarg:ident) => {
473        $fmtarg.set_hex().set_alternate(true)
474    };
475    (bin, $fmtarg:ident) => {
476        $fmtarg.set_bin().set_alternate(false)
477    };
478    ({b}, $fmtarg:ident) => {
479        $fmtarg.set_bin().set_alternate(false)
480    };
481    (alt_bin, $fmtarg:ident) => {
482        $fmtarg.set_bin().set_alternate(true)
483    };
484    ({#b}, $fmtarg:ident) => {
485        $fmtarg.set_bin().set_alternate(true)
486    };
487    (_, $fmtarg:ident) => {
488        $fmtarg
489    };
490    ($kw:tt, $fmtarg:ident) => {
491        compile_error!(concat!(
492            "unrecognized formatting specifier: ",
493            stringify!($kw),
494            "\n",
495            "expected one of:\n",
496            "- display/{}\n",
497            "- alt_display/{#}\n",
498            "- debug/{?}\n",
499            "- alt_debug/{#?}\n",
500            "- hex/{X}\n",
501            "- alt_hex/{#X}\n",
502            "- bin/{b}\n",
503            "- alt_bin/{#b}\n",
504        ))
505    };
506}