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//! - `"derive"`(disabled by default):
183//! Enables the [`PanicFmt` derive] macro.
184//!
185//! # Plans
186//!
187//! None for now
188//!
189//! # No-std support
190//!
191//! `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used.
192//!
193//! # Minimum Supported Rust Version
194//!
195//! This requires Rust 1.57.0, because it uses the `panic` macro in a const context.
196//!
197//!
198//! [`PanicFmt` derive]: derive@crate::PanicFmt
199//! [`PanicFmt`]: trait@crate::PanicFmt
200//! [`impl_panicfmt`]: crate::impl_panicfmt
201//! [`flatten_panicvals`]: crate::flatten_panicvals
202//! [`MAX_PANIC_MSG_LEN`]: crate::MAX_PANIC_MSG_LEN
203#![no_std]
204#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
205#![warn(missing_docs)]
206#![deny(clippy::missing_safety_doc)]
207#![deny(clippy::shadow_unrelated)]
208#![deny(clippy::wildcard_imports)]
209
210extern crate self as const_panic;
211
212#[macro_use]
213mod doc_macros;
214
215#[macro_use]
216mod macros;
217
218mod concat_panic_;
219
220mod debug_str_fmt;
221
222mod int_formatting;
223
224pub mod fmt;
225
226#[cfg(all(doctest, feature = "non_basic"))]
227pub mod doctests;
228
229mod panic_val;
230
231#[cfg(feature = "non_basic")]
232mod const_default;
233
234pub mod utils;
235
236#[cfg(all(test, not(feature = "test")))]
237compile_error! {r##"please use cargo test --features "test""##}
238
239#[cfg(feature = "non_basic")]
240mod slice_stuff;
241
242#[cfg(feature = "non_basic")]
243mod array_string;
244
245#[cfg(feature = "non_basic")]
246pub use crate::array_string::ArrayString;
247
248mod wrapper;
249
250mod fmt_impls {
251    #[macro_use]
252    pub(crate) mod basic_fmt_impls;
253
254    #[cfg(feature = "rust_1_64")]
255    mod rust_1_64_fmt_impls;
256
257    #[cfg(feature = "rust_1_82")]
258    mod rust_1_82_fmt_impls;
259
260    #[macro_use]
261    #[cfg(feature = "non_basic")]
262    mod option_fmt_impls;
263
264    #[cfg(feature = "non_basic")]
265    mod nonzero_impls;
266
267    #[cfg(feature = "non_basic")]
268    mod other_impls;
269
270    #[cfg(feature = "non_basic")]
271    mod fmt_range;
272}
273
274pub use crate::{
275    concat_panic_::{concat_panic, MAX_PANIC_MSG_LEN},
276    panic_val::PanicVal,
277    wrapper::StdWrapper,
278};
279
280#[doc(no_inline)]
281pub use crate::fmt::{FmtArg, IsCustomType, PanicFmt};
282
283#[cfg(feature = "non_basic")]
284#[doc(no_inline)]
285pub use crate::fmt::{ComputePvCount, TypeDelim};
286
287#[doc(hidden)]
288pub mod __ {
289    pub use core::{
290        assert, compile_error, concat,
291        option::Option::{None, Some},
292        primitive::usize,
293        result::Result::{Err, Ok},
294        stringify,
295    };
296
297    pub use crate::*;
298
299    #[cfg(feature = "non_basic")]
300    pub use crate::reexported_non_basic::*;
301}
302
303#[cfg(feature = "non_basic")]
304#[doc(hidden)]
305mod reexported_non_basic {
306    pub use core::{option::Option, primitive::str};
307
308    pub use typewit::MakeTypeWitness;
309
310    pub use crate::{
311        concat_panic_::{compute_length, make_panic_string_unwrapped},
312        const_default::ConstDefault,
313        macros::concat_macro::ConcatCmd,
314        utils::{assert_flatten_panicvals_length, flatten_panicvals, panicvals_id},
315    };
316
317    pub const EPV: crate::PanicVal<'_> = crate::PanicVal::EMPTY;
318}
319
320#[cfg(feature = "derive")]
321include! {"./proc_macro_reexports/panicfmt_derive.rs"}
322
323#[doc(hidden)]
324#[cfg(feature = "test")]
325pub mod test_utils;
326
327#[doc(hidden)]
328#[cfg(feature = "test")]
329pub mod for_tests {
330    pub use crate::concat_panic_::{format_panic_message, NotEnoughSpace};
331}
332
333#[cfg(all(doctest))]
334#[doc = include_str!("../README.md")]
335pub struct ReadmeTest;