#[macro_use] mod concat_assert; #[cfg(feature = "non_basic")] #[macro_use] pub(crate) mod concat_macro; #[cfg(feature = "non_basic")] #[macro_use] mod non_basic_macros; #[cfg(feature = "non_basic")] #[macro_use] mod macro_utils; #[cfg(feature = "non_basic")] #[macro_use] mod impl_panicfmt; #[macro_use] mod unwrapping; #[doc(hidden)] #[macro_export] macro_rules! __write_array { ($array:expr, $len:expr, $value:expr) => { $array[$len] = $value; $len += 1; }; } #[doc(hidden)] #[macro_export] macro_rules! __write_array_checked { ($array:expr, $len:expr, $value:expr) => { if $array.len() > $len { $array[$len] = $value; $len += 1; } }; } /// Coerces `$reff` to a type that has a `to_panicvals` method, /// which is expected to return a `[PanicVal<'_>; LEN]`. /// /// # Limitations /// #[doc = crate::doc_macros::limitation_docs!()] /// /// # Example /// /// This example uses [`const_panic::ArrayString`](crate::ArrayString) /// to show what the values format into, /// which requires the `"non_basic"` crate feature (enabled by default). /// #[cfg_attr(feature = "non_basic", doc = "```rust")] #[cfg_attr(not(feature = "non_basic"), doc = "```ignore")] /// use const_panic::{ArrayString, FmtArg, IsCustomType, PanicFmt, PanicVal, coerce_fmt}; /// /// type AS = ArrayString<100>; /// /// assert_eq!( /// AS::from_panicvals(&coerce_fmt!(100u8).to_panicvals(FmtArg::DEBUG)).unwrap(), /// "100", /// ); /// /// assert_eq!( /// AS::from_panicvals(&coerce_fmt!("hello\n").to_panicvals(FmtArg::DEBUG)).unwrap(), /// r#""hello\n""#, /// ); /// /// assert_eq!( /// AS::from_panicvals(&coerce_fmt!(IsReal::No).to_panicvals(FmtArg::DEBUG)).unwrap(), /// "No", /// ); /// /// assert_eq!( /// AS::from_panicvals(&coerce_fmt!(IsReal::Yes).to_panicvals(FmtArg::DEBUG)).unwrap(), /// "Yes", /// ); /// /// /// /// enum IsReal{Yes, No} /// /// // All the code below manually implements panic formatting for a field-less enum. /// // This can be written concisely with the `PanicFmt` derive or `impl_panicfmt` macro. /// impl PanicFmt for IsReal { /// type This = Self; /// type Kind = IsCustomType; /// const PV_COUNT: usize = 1; /// } /// /// impl IsReal { /// pub const fn to_panicvals(&self, _f: FmtArg) -> [PanicVal<'_>; IsReal::PV_COUNT] { /// let x = match self { /// Self::Yes => "Yes", /// Self::No => "No", /// }; /// [PanicVal::write_str(x)] /// } /// } /// /// ``` #[macro_export] macro_rules! coerce_fmt { ($reff:expr) => { match &$reff { reff => $crate::__::PanicFmt::PROOF.infer(reff).coerce(reff), } }; } /// Panics with the concanenation of the arguments. /// /// [**Examples below**](#examples) /// /// # Syntax /// /// This macro uses this syntax: /// ```text /// concat_panic!( /// $($fmtarg:expr;)? /// $( /// $( $format_override:tt: )? $arg_to_fmt:expr /// ),* /// $(,)? /// ) /// ``` /// /// `$fmtarg` is an optional [`FmtArg`](crate::FmtArg) argument /// which defaults to `FmtArg::DEBUG`, /// determining how non-literal `$arg_to_fmt` arguments are formatted. /// /// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument, /// changing how that `$arg_to_fmt` argument is formatted. /// #[doc = formatting_docs!()] /// /// # Limitations /// #[doc = crate::doc_macros::limitation_docs!()] /// /// # Examples /// /// ### `Odd`-type /// /// ```rust, compile_fail /// use const_panic::concat_panic; /// /// use odd::Odd; /// /// # fn main(){ /// const _: Odd = match Odd::new(3 * 4) { /// Ok(x) => x, /// Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"), /// }; /// # } /// /// mod odd { /// pub struct Odd(u32); /// /// impl Odd { /// pub const fn new(n: u32) -> Result { /// if n % 2 == 1 { /// Ok(Odd(n)) /// } else { /// Err(Even(n)) /// } /// } /// } /// /// # /* /// #[derive(const_panic::PanicFmt))] /// # */ /// pub struct Even(u32); /// # /// # impl const_panic::PanicFmt for Even { /// # type This = Self; /// # type Kind = const_panic::IsCustomType; /// # const PV_COUNT: usize = 1; /// # } /// # impl Even { /// # pub const fn to_panicvals( /// # &self, /// # f: const_panic::FmtArg, /// # ) -> [const_panic::PanicVal<'static>; 1] { /// # const_panic::StdWrapper(&self.0).to_panicvals(f) /// # } /// # } /// } /// /// ``` /// produces this compile-time error: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros.rs:188:15 /// | /// 10 | Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"), /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// expected odd number, got `12`', src/macros.rs:10:15 /// | /// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info) /// /// ``` /// /// /// ### All the syntax /// /// This example demonstrates using all of the syntax of this macro. /// /// ```compile_fail /// use const_panic::{FmtArg, concat_panic, fmt}; /// /// const _: () = concat_panic!{ /// // the optional `$fmtarg` parameter. /// // If this argument isn't passed, it defaults to `FmtArg::DEBUG` /// FmtArg::ALT_DEBUG; /// /// "\n\nshowing off literals:\n", /// 100u8, /// "hello", /// /// "\n\nnon-literals with formatting determined by the $fmtarg parameter:\n", /// // this is considered a non-literal, because it's inside other tokens. /// ("a non-literal"), /// [100u8, 200], /// /// "\n\nexplicitly debug formatted:\n", /// debug: "foo", /// // `{?}:` is The same as `debug:` /// {?}: "bar", /// /// "\n\nalternate debug formatted:\n", /// alt_debug: ["foo"], /// // `{#?}:` is The same as `alt_debug:` /// {#?}: "bar", /// /// "\n\ndisplay formatted:\n", /// display: "baz", /// // `{}:` is The same as `display:` /// {}: ["qux", "aaa"], /// /// "\n\nalternate display formatted:", /// alt_display: ["bbb", "ccc"], /// // `{#}:` is The same as `alt_display:` /// {#}: ["bbb", "ccc"], /// /// "\n\nbinary formatted:\n", /// bin: [3u8, 5, 8, 13], /// // `{b}:` is The same as `bin:` /// {b}: [3u8, 5, 8, 13], /// /// "\n\nalternate-binary formatted:\n", /// alt_bin: [21u8, 34, 55, 89], /// // `{#b}:` is The same as `alt_bin:` /// {#b}: [21u8, 34, 55, 89], /// /// "\n\nhexadecimal formatted:\n", /// hex: [3u8, 5, 8, 13], /// // `{X}:` is The same as `hex:` /// {X}: [3u8, 5, 8, 13], /// /// "\n\nalternate-hexadecimal formatted:\n", /// alt_hex: [21u8, 34, 55, 89], /// // `{#X}:` is The same as `alt_hex:` /// {#X}: [21u8, 34, 55, 89], /// /// "\n\n", /// }; /// /// ``` /// The above code produces this compile-time error: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros.rs:186:15 /// | /// 6 | const _: () = concat_panic!{ /// | _______________^ /// 7 | | // the optional `$fmtarg` parameter. /// 8 | | // If this argument isn't passed, it defaults to `FmtArg::DEBUG` /// 9 | | FmtArg::ALT_DEBUG; /// ... | /// 60 | | "\n\n", /// 61 | | }; /// | |_^ the evaluated program panicked at ' /// /// showing off literals: /// 100hello /// /// non-literals with formatting determined by the $fmtarg parameter: /// "a non-literal"[ /// 100, /// 200, /// ] /// /// explicitly debug formatted: /// "foo""bar" /// /// alternate debug formatted: /// [ /// "foo", /// ]"bar" /// /// display formatted: /// baz[qux, aaa] /// /// alternate display formatted:[ /// bbb, /// ccc, /// ][ /// bbb, /// ccc, /// ] /// /// binary formatted: /// [11, 101, 1000, 1101][11, 101, 1000, 1101] /// /// alternate-binary formatted: /// [ /// 0b10101, /// 0b100010, /// 0b110111, /// 0b1011001, /// ][ /// 0b10101, /// 0b100010, /// 0b110111, /// 0b1011001, /// ] /// /// hexadecimal formatted: /// [3, 5, 8, D][3, 5, 8, D] /// /// alternate-hexadecimal formatted: /// [ /// 0x15, /// 0x22, /// 0x37, /// 0x59, /// ][ /// 0x15, /// 0x22, /// 0x37, /// 0x59, /// ] /// /// ', src/macros.rs:6:15 /// | /// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info) /// /// error: aborting due to previous error /// /// ``` /// #[macro_export] macro_rules! concat_panic { ($($args:tt)*) => ( $crate::__concat_func_setup!{ (|args| $crate::concat_panic(args)) [] [$($args)*,] } ) } // This macro takes the optional `$fmt:expr;` argument before everything else. // But I had to parse the argument manually, // because `$fmt:expr;` fails compilation instead of trying the following branches // when the argument isn't valid expression syntax. #[doc(hidden)] #[macro_export] macro_rules! __concat_func_setup { ($args:tt $prev:tt [$($fmt:tt).*; $($rem:tt)* ]) => ({ let mut fmt: $crate::FmtArg = $($fmt).*; $crate::__concat_func!{fmt $args $prev [$($rem)*]} }); ($args:tt $prev:tt [$(:: $(@$_dummy:tt@)?)? $($fmt:ident)::* ; $($rem:tt)* ]) => ({ let mut fmt: $crate::FmtArg = $(:: $($_dummy)?)? $($fmt)::*; $crate::__concat_func!{fmt $args $prev [$($rem)*]} }); ($args:tt $prev:tt $rem:tt) => ({ let mut fmt: $crate::FmtArg = $crate::FmtArg::DEBUG; $crate::__concat_func!{fmt $args $prev $rem} }); } #[doc(hidden)] #[macro_export] macro_rules! __concat_func { ($fmt:ident $args:tt [$($prev:tt)*] [$keyword:tt: $expr:expr, $($rem:tt)* ]) => { $crate::__concat_func!{ $fmt $args [$($prev)* ($crate::__set_fmt_from_kw!($keyword, $fmt), $expr)] [$($rem)*] } }; ($fmt:ident $args:tt [$($prev:tt)*] [$expr:literal, $($rem:tt)* ]) => { $crate::__concat_func!{ $fmt $args [$($prev)* ($crate::__set_fmt_from_kw!(display, $fmt), $expr)] [$($rem)*] } }; ($fmt:ident $args:tt [$($prev:tt)*] [$expr:expr, $($rem:tt)* ]) => { $crate::__concat_func!{ $fmt $args [$($prev)* ($fmt, $expr)] [$($rem)*] } }; ($fmt:ident (|$args:ident| $function_call:expr) [$(($fmt_arg:expr, $reff:expr))*] [$(,)*]) => { match &[ $( $crate::StdWrapper( &$crate::coerce_fmt!($reff) .to_panicvals($fmt_arg) ).deref_panic_vals(), )* ] { $args => $function_call, } }; } #[doc(hidden)] #[macro_export] macro_rules! __set_fmt_from_kw { (open, $fmtarg:ident) => {{ $fmtarg = $fmtarg.indent(); $fmtarg.set_display() }}; (close, $fmtarg:ident) => {{ $fmtarg = $fmtarg.unindent(); $fmtarg.set_display() }}; (display, $fmtarg:ident) => { $fmtarg.set_display().set_alternate(false) }; ({}, $fmtarg:ident) => { $fmtarg.set_display().set_alternate(false) }; (alt_display, $fmtarg:ident) => { $fmtarg.set_display().set_alternate(true) }; ({#}, $fmtarg:ident) => { $fmtarg.set_display().set_alternate(true) }; (debug, $fmtarg:ident) => { $fmtarg.set_debug().set_alternate(false) }; ({?}, $fmtarg:ident) => { $fmtarg.set_debug().set_alternate(false) }; (alt_debug, $fmtarg:ident) => { $fmtarg.set_debug().set_alternate(true) }; ({#?}, $fmtarg:ident) => { $fmtarg.set_debug().set_alternate(true) }; (hex, $fmtarg:ident) => { $fmtarg.set_hex().set_alternate(false) }; ({X}, $fmtarg:ident) => { $fmtarg.set_hex().set_alternate(false) }; (alt_hex, $fmtarg:ident) => { $fmtarg.set_hex().set_alternate(true) }; ({#X}, $fmtarg:ident) => { $fmtarg.set_hex().set_alternate(true) }; (bin, $fmtarg:ident) => { $fmtarg.set_bin().set_alternate(false) }; ({b}, $fmtarg:ident) => { $fmtarg.set_bin().set_alternate(false) }; (alt_bin, $fmtarg:ident) => { $fmtarg.set_bin().set_alternate(true) }; ({#b}, $fmtarg:ident) => { $fmtarg.set_bin().set_alternate(true) }; (_, $fmtarg:ident) => { $fmtarg }; ($kw:tt, $fmtarg:ident) => { compile_error!(concat!( "unrecognized formatting specifier: ", stringify!($kw), "\n", "expected one of:\n", "- display/{}\n", "- alt_display/{#}\n", "- debug/{?}\n", "- alt_debug/{#?}\n", "- hex/{X}\n", "- alt_hex/{#X}\n", "- bin/{b}\n", "- alt_bin/{#b}\n", )) }; }