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;