const_panic/lib.rs
1//! For panicking with formatting in const contexts.
2//!
3//! This library exists because the panic macro was stabilized for use in const contexts
4//! in Rust 1.57.0, without formatting support.
5//!
6//! All of the types that implement the [`PanicFmt`] trait can be formatted in panics.
7//!
8//! # Examples
9//!
10//! - [Basic](#basic)
11//! - [Custom Types](#custom-types)
12//!
13//! ### Basic
14//!
15//! ```compile_fail
16//! use const_panic::concat_assert;
17//!
18//! const FOO: u32 = 10;
19//! const BAR: u32 = 0;
20//! const _: () = assert_non_zero(FOO, BAR);
21//!
22//! #[track_caller]
23//! const fn assert_non_zero(foo: u32, bar: u32) {
24//!     concat_assert!{
25//!         foo != 0 && bar != 0,
26//!         "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
27//!     }
28//! }
29//! ```
30//! The above code fails to compile with this error:
31//! ```text
32//! error[E0080]: evaluation of constant value failed
33//!  --> src/lib.rs:20:15
34//!   |
35//! 8 | const _: () = assert_non_zero(FOO, BAR);
36//!   |               ^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
37//! neither foo nor bar can be zero!
38//! foo: 10
39//! bar: 0', src/lib.rs:8:15
40//! ```
41//!
42//! When called at runtime
43//! ```should_panic
44//! use const_panic::concat_assert;
45//!
46//! assert_non_zero(10, 0);
47//!
48//! #[track_caller]
49//! const fn assert_non_zero(foo: u32, bar: u32) {
50//!     concat_assert!{
51//!         foo != 0 && bar != 0,
52//!         "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
53//!     }
54//! }
55//! ```
56//! it prints this:
57//! ```text
58//! thread 'main' panicked at '
59//! neither foo nor bar can be zero!
60//! foo: 10
61//! bar: 0', src/lib.rs:6:1
62//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
63//!
64//! ```
65//!
66//! ### Custom types
67//!
68//! Panic formatting for custom types can be done in these ways
69//! (in increasing order of verbosity):
70//! - Using the [`PanicFmt` derive] macro
71//! (requires the opt-in `"derive"` feature)
72//! - Using the [`impl_panicfmt`] macro
73//! (requires the default-enabled `"non_basic"` feature)
74//! - Using the [`flatten_panicvals`] macro
75//! (requires the default-enabled `"non_basic"` feature)
76//! - Manually implementing the [`PanicFmt`] trait as described in its docs.
77//!
78//! This example uses the [`PanicFmt` derive] approach.
79//!
80//! ```compile_fail
81//! use const_panic::{PanicFmt, concat_panic};
82//!
83//! const LAST: u8 = {
84//!     Foo{
85//!         x: &[],
86//!         y: Bar(false, true),
87//!         z: Qux::Left(23),
88//!     }.pop().1
89//! };
90//!
91//! impl Foo<'_> {
92//!     /// Pops the last element
93//!     ///
94//!     /// # Panics
95//!     ///
96//!     /// Panics if `self.x` is empty
97//!     #[track_caller]
98//!     const fn pop(mut self) -> (Self, u8) {
99//!         if let [rem @ .., last] = self.x {
100//!             self.x = rem;
101//!             (self, *last)
102//!         } else {
103//!             concat_panic!(
104//!                 "\nexpected a non-empty Foo, found: \n",
105//!                 // uses alternative Debug formatting for `self`,
106//!                 // otherwise this would use regular Debug formatting.
107//!                 alt_debug: self
108//!             )
109//!         }
110//!     }
111//! }
112//!
113//! #[derive(PanicFmt)]
114//! struct Foo<'a> {
115//!     x: &'a [u8],
116//!     y: Bar,
117//!     z: Qux,
118//! }
119//!
120//! #[derive(PanicFmt)]
121//! struct Bar(bool, bool);
122//!
123//! #[derive(PanicFmt)]
124//! enum Qux {
125//!     Up,
126//!     Down { x: u32, y: u32 },
127//!     Left(u64),
128//! }
129//!
130//! ```
131//! The above code fails to compile with this error:
132//! ```text
133//! error[E0080]: evaluation of constant value failed
134//!   --> src/lib.rs:57:5
135//!    |
136//! 7  | /     Foo{
137//! 8  | |         x: &[],
138//! 9  | |         y: Bar(false, true),
139//! 10 | |         z: Qux::Left(23),
140//! 11 | |     }.pop().1
141//!    | |___________^ the evaluated program panicked at '
142//! expected a non-empty Foo, found:
143//! Foo {
144//!     x: [],
145//!     y: Bar(
146//!         false,
147//!         true,
148//!     ),
149//!     z: Left(
150//!         23,
151//!     ),
152//! }', src/lib.rs:11:7
153//!
154//!
155//! ```
156//!
157//! # Limitations
158#![doc = crate::doc_macros::limitation_docs!()]
159//!
160//! ### Panic message length
161//!
162//! The panic message can only be up to [`MAX_PANIC_MSG_LEN`] long,
163//! after which it is truncated.
164//!
165//! # Cargo features
166//!
167//! - `"non_basic"`(enabled by default):
168//! Enables support for formatting structs, enums, and arrays.
169//! <br>
170//! Without this feature, you can effectively only format primitive types
171//! (custom types can manually implement formatting with more difficulty).
172//!
173//! - `"rust_latest_stable"`(disabled by default):
174//! Enables all the `"rust_1_*"` features.
175//!
176//! - `"rust_1_64"`(disabled by default):
177//! Enables formatting of additional items that require Rust 1.64.0 to do so.
178//!
179//! - `"rust_1_82"`(disabled by default):
180//! Enables formatting of additional items that require Rust 1.82.0 to do so.
181//!
182//! - `"rust_1_88"`(disabled by default):
183//! Enables formatting of additional items that require Rust 1.88.0 to do so.
184//!
185//! - `"derive"`(disabled by default):
186//! Enables the [`PanicFmt` derive] macro.
187//!
188//! # Plans
189//!
190//! None for now
191//!
192//! # No-std support
193//!
194//! `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used.
195//!
196//! # Minimum Supported Rust Version
197//!
198//! This requires Rust 1.57.0, because it uses the `panic` macro in a const context.
199//!
200//!
201//! [`PanicFmt` derive]: derive@crate::PanicFmt
202//! [`PanicFmt`]: trait@crate::PanicFmt
203//! [`impl_panicfmt`]: crate::impl_panicfmt
204//! [`flatten_panicvals`]: crate::flatten_panicvals
205//! [`MAX_PANIC_MSG_LEN`]: crate::MAX_PANIC_MSG_LEN
206#![no_std]
207#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
208#![warn(missing_docs)]
209#![deny(clippy::missing_safety_doc)]
210#![deny(clippy::shadow_unrelated)]
211#![deny(clippy::wildcard_imports)]
212
213extern crate self as const_panic;
214
215#[macro_use]
216mod doc_macros;
217
218#[macro_use]
219mod macros;
220
221mod concat_panic_;
222
223mod debug_str_fmt;
224
225mod int_formatting;
226
227pub mod fmt;
228
229#[cfg(all(doctest, feature = "non_basic"))]
230pub mod doctests;
231
232mod panic_val;
233
234#[cfg(feature = "non_basic")]
235mod const_default;
236
237pub mod utils;
238
239/// tests doc lints with `cargo doc --features="test rust_latest_stable derive"`
240#[cfg(all(feature = "test", feature = "non_basic"))]
241pub mod test_doc_lints;
242
243#[cfg(all(test, not(feature = "test")))]
244compile_error! {r##"please use cargo test --features "test""##}
245
246#[cfg(feature = "non_basic")]
247mod slice_stuff;
248
249#[cfg(feature = "non_basic")]
250mod array_string;
251
252#[cfg(feature = "non_basic")]
253pub use crate::array_string::ArrayString;
254
255mod wrapper;
256
257mod fmt_impls {
258    #[macro_use]
259    pub(crate) mod basic_fmt_impls;
260
261    #[cfg(feature = "rust_1_64")]
262    mod rust_1_64_fmt_impls;
263
264    #[cfg(feature = "rust_1_82")]
265    mod rust_1_82_fmt_impls;
266
267    #[macro_use]
268    #[cfg(feature = "non_basic")]
269    mod option_fmt_impls;
270
271    #[cfg(feature = "non_basic")]
272    mod nonzero_impls;
273
274    #[cfg(feature = "non_basic")]
275    mod other_impls;
276
277    #[cfg(feature = "non_basic")]
278    mod fmt_range;
279
280    #[cfg(all(feature = "non_basic", feature = "rust_1_88"))]
281    mod rust_1_88_nonbasic_fmt_impls;
282}
283
284pub use crate::{
285    concat_panic_::{concat_panic, MAX_PANIC_MSG_LEN},
286    panic_val::PanicVal,
287    wrapper::StdWrapper,
288};
289
290#[doc(no_inline)]
291pub use crate::fmt::{FmtArg, IsCustomType, PanicFmt};
292
293#[cfg(feature = "non_basic")]
294#[doc(no_inline)]
295pub use crate::fmt::{ComputePvCount, TypeDelim};
296
297#[doc(hidden)]
298pub mod __ {
299    pub use core::{
300        assert, compile_error, concat,
301        option::Option::{None, Some},
302        primitive::usize,
303        result::Result::{Err, Ok},
304        stringify,
305    };
306
307    pub use crate::*;
308
309    #[cfg(feature = "non_basic")]
310    pub use crate::reexported_non_basic::*;
311}
312
313#[cfg(feature = "non_basic")]
314#[doc(hidden)]
315mod reexported_non_basic {
316    pub use core::{mem::forget, option::Option, primitive::str, unreachable};
317
318    pub use typewit::MakeTypeWitness;
319
320    pub use crate::{
321        concat_panic_::{compute_length, make_panic_string_unwrapped},
322        const_default::ConstDefault,
323        macros::concat_macro::ConcatCmd,
324        utils::{assert_flatten_panicvals_length, flatten_panicvals, panicvals_id},
325    };
326
327    pub const EPV: crate::PanicVal<'_> = crate::PanicVal::EMPTY;
328}
329
330#[cfg(feature = "derive")]
331include! {"./proc_macro_reexports/panicfmt_derive.rs"}
332
333#[doc(hidden)]
334#[cfg(feature = "test")]
335pub mod test_utils;
336
337#[doc(hidden)]
338#[cfg(feature = "test")]
339pub mod for_tests {
340    pub use crate::concat_panic_::{format_panic_message, NotEnoughSpace};
341}
342
343#[cfg(all(doctest))]
344#[doc = include_str!("../README.md")]
345pub struct ReadmeTest;