Skip to main content

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