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}