const_panic-0.2.13/.cargo_vcs_info.json0000644000000001360000000000100134170ustar { "git": { "sha1": "58e64432f35726d8c1ba5c3086c8d558b827ace6" }, "path_in_vcs": "" }const_panic-0.2.13/Cargo.lock0000644000000051440000000000100113760ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "const_panic" version = "0.2.13" dependencies = [ "arrayvec", "const_panic_proc_macros", "rand", "typewit", ] [[package]] name = "const_panic_proc_macros" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c5b80a80fb52c1a6ca02e3cd829a76b472ff0a15588196fd8da95221f0c1e4b" dependencies = [ "proc-macro2", "quote", "syn", "unicode-xid", ] [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "syn" version = "2.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2415488199887523e74fd9a5f7be804dfd42d868ae0eca382e3917094d210e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "typewit" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" dependencies = [ "typewit_proc_macros", ] [[package]] name = "typewit_proc_macros" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" const_panic-0.2.13/Cargo.toml0000644000000032530000000000100114200ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.57" name = "const_panic" version = "0.2.13" authors = ["rodrimati1992 "] build = false include = [ "Cargo.toml", "src/**/*.rs", "./README.md", "LICENSE-ZLIB.md", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "const panic with formatting" readme = "README.md" keywords = [ "no-std", "format", "const", "panic", ] categories = ["no-std"] license = "Zlib" repository = "https://github.com/rodrimati1992/const_panic/" [package.metadata.docs.rs] features = [ "derive", "rust_latest_stable", "docsrs", ] [features] default = ["non_basic"] derive = [ "const_panic_proc_macros", "non_basic", ] docsrs = [] non_basic = ["typewit"] rust_1_64 = [] rust_1_82 = ["rust_1_64"] rust_1_88 = ["rust_1_82"] rust_latest_stable = ["rust_1_88"] test = [] [lib] name = "const_panic" path = "src/lib.rs" [dependencies.const_panic_proc_macros] version = "=0.2.12" optional = true [dependencies.typewit] version = "1.9.0" optional = true [dev-dependencies.arrayvec] version = "0.7" [dev-dependencies.rand] version = "0.8.4" features = ["small_rng"] default_features = false const_panic-0.2.13/Cargo.toml.orig000064400000000000000000000021311046102023000150730ustar 00000000000000[package] name = "const_panic" version = "0.2.13" authors = ["rodrimati1992 "] edition = "2021" license = "Zlib" description = "const panic with formatting" rust-version = "1.57" readme="./README.md" keywords = ["no-std", "format", "const", "panic"] categories = ["no-std"] repository = "https://github.com/rodrimati1992/const_panic/" include = [ "Cargo.toml", "src/**/*.rs", "./README.md", "LICENSE-ZLIB.md", ] [workspace] [dependencies.typewit] version = "1.9.0" optional = true [dependencies.const_panic_proc_macros] version = "=0.2.12" path = "./const_panic_proc_macros/" optional = true [dev-dependencies.arrayvec] version = "0.7" [dev-dependencies.rand] version = "0.8.4" default_features = false features = ["small_rng"] [features] default = ["non_basic"] rust_1_64 = [] rust_1_82 = ["rust_1_64"] rust_1_88 = ["rust_1_82"] rust_latest_stable = ["rust_1_88"] non_basic = ["typewit"] docsrs = [] derive = ["const_panic_proc_macros", "non_basic"] # private feature test = [] [package.metadata.docs.rs] features = ["derive", "rust_latest_stable", "docsrs"] const_panic-0.2.13/LICENSE-ZLIB.md000064400000000000000000000015271046102023000143560ustar 00000000000000Copyright (c) 2021 Matias Rodriguez. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.const_panic-0.2.13/README.md000064400000000000000000000130321046102023000134650ustar 00000000000000[![Rust](https://github.com/rodrimati1992/const_panic/workflows/Rust/badge.svg)](https://github.com/rodrimati1992/const_panic/actions) [![crates-io](https://img.shields.io/crates/v/const_panic.svg)](https://crates.io/crates/const_panic) [![api-docs](https://docs.rs/const_panic/badge.svg)](https://docs.rs/const_panic/*) For panicking with formatting in const contexts. This library exists because the panic macro was stabilized for use in const contexts in Rust 1.57.0, without formatting support. All of the types that implement the [`PanicFmt`] trait can be formatted in panics. # Examples - [Basic](#basic) - [Custom Types](#custom-types) ### Basic ```compile_fail use const_panic::concat_assert; const FOO: u32 = 10; const BAR: u32 = 0; const _: () = assert_non_zero(FOO, BAR); #[track_caller] const fn assert_non_zero(foo: u32, bar: u32) { concat_assert!{ foo != 0 && bar != 0, "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar } } ``` The above code fails to compile with this error: ```text error[E0080]: evaluation of constant value failed --> src/lib.rs:20:15 | 8 | const _: () = assert_non_zero(FOO, BAR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' neither foo nor bar can be zero! foo: 10 bar: 0', src/lib.rs:8:15 ``` When called at runtime ```should_panic use const_panic::concat_assert; assert_non_zero(10, 0); #[track_caller] const fn assert_non_zero(foo: u32, bar: u32) { concat_assert!{ foo != 0 && bar != 0, "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar } } ``` it prints this: ```text thread 'main' panicked at ' neither foo nor bar can be zero! foo: 10 bar: 0', src/lib.rs:6:1 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` ### Custom types Panic formatting for custom types can be done in these ways (in increasing order of verbosity): - Using the [`PanicFmt` derive] macro (requires the opt-in `"derive"` feature) - Using the [`impl_panicfmt`] macro (requires the default-enabled `"non_basic"` feature) - Using the [`flatten_panicvals`] macro (requires the default-enabled `"non_basic"` feature) - Manually implementing the [`PanicFmt`] trait as described in its docs. This example uses the [`PanicFmt` derive] approach. ```compile_fail use const_panic::{PanicFmt, concat_panic}; const LAST: u8 = { Foo{ x: &[], y: Bar(false, true), z: Qux::Left(23), }.pop().1 }; impl Foo<'_> { /// Pops the last element /// /// # Panics /// /// Panics if `self.x` is empty #[track_caller] const fn pop(mut self) -> (Self, u8) { if let [rem @ .., last] = self.x { self.x = rem; (self, *last) } else { concat_panic!( "\nexpected a non-empty Foo, found: \n", // uses alternative Debug formatting for `self`, // otherwise this would use regular Debug formatting. alt_debug: self ) } } } #[derive(PanicFmt)] struct Foo<'a> { x: &'a [u8], y: Bar, z: Qux, } #[derive(PanicFmt)] struct Bar(bool, bool); #[derive(PanicFmt)] enum Qux { Up, Down { x: u32, y: u32 }, Left(u64), } ``` The above code fails to compile with this error: ```text error[E0080]: evaluation of constant value failed --> src/lib.rs:57:5 | 7 | / Foo{ 8 | | x: &[], 9 | | y: Bar(false, true), 10 | | z: Qux::Left(23), 11 | | }.pop().1 | |___________^ the evaluated program panicked at ' expected a non-empty Foo, found: Foo { x: [], y: Bar( false, true, ), z: Left( 23, ), }', src/lib.rs:11:7 ``` # Limitations Arguments to the formatting/panicking macros must have a fully inferred concrete type, because `const_panic` macros use duck typing to call methods on those arguments. One effect of that limitation is that you will have to pass suffixed integer literals (eg: `100u8`) when those integers aren't inferred to be a concrete type. ### Panic message length The panic message can only be up to [`MAX_PANIC_MSG_LEN`] long, after which it is truncated. # Cargo features - `"non_basic"`(enabled by default): Enables support for formatting structs, enums, and arrays.
Without this feature, you can effectively only format primitive types (custom types can manually implement formatting with more difficulty). - `"rust_latest_stable"`(disabled by default): Enables all the `"rust_1_*"` features. - `"rust_1_64"`(disabled by default): Enables formatting of additional items that require Rust 1.64.0 to do so. - `"rust_1_82"`(disabled by default): Enables formatting of additional items that require Rust 1.82.0 to do so. - `"rust_1_88"`(disabled by default): Enables formatting of additional items that require Rust 1.88.0 to do so. - `"derive"`(disabled by default): Enables the [`PanicFmt` derive] macro. # Plans None for now # No-std support `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used. # Minimum Supported Rust Version This requires Rust 1.57.0, because it uses the `panic` macro in a const context. [`PanicFmt` derive]: https://docs.rs/const_panic/*/const_panic/derive.PanicFmt.html [`PanicFmt`]: https://docs.rs/const_panic/*/const_panic/fmt/trait.PanicFmt.html [`impl_panicfmt`]: https://docs.rs/const_panic/*/const_panic/macro.impl_panicfmt.html [`flatten_panicvals`]: https://docs.rs/const_panic/*/const_panic/macro.flatten_panicvals.html [`MAX_PANIC_MSG_LEN`]: https://docs.rs/const_panic/*/const_panic/constant.MAX_PANIC_MSG_LEN.htmlconst_panic-0.2.13/src/array_string.rs000064400000000000000000000210541046102023000160520ustar 00000000000000use crate::{ utils::{bytes_up_to, RangedBytes}, FmtArg, PanicFmt, PanicVal, }; use core::{ cmp::PartialEq, fmt::{self, Debug}, }; /// For precomputing a panic message. /// #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[derive(Copy, Clone)] pub struct ArrayString { pub(crate) len: u32, pub(crate) buffer: [u8; CAP], } /// Equivalent of `ArrayString` which can only be up to 255 bytes long. /// /// This stores the length as a `u8`, while `ArrayString` stores it as a `u32`, /// making this 3 bytes smaller and 1-aligned (while ArrayString is aligned to a `u32`). #[derive(Copy, Clone)] pub(crate) struct TinyString { len: u8, buffer: [u8; CAP], } const fn add_up_lengths(mut strings: &[&str]) -> usize { let mut len = 0; while let [x, rem @ ..] = strings { len += x.len(); strings = rem; } len } impl ArrayString { /// Constructs an `ArrayString` from a `&str` /// /// # Panics /// /// Panics if `string` is larger than `CAP`. /// /// # Example /// /// ```rust /// use const_panic::ArrayString; /// /// assert_eq!(ArrayString::<16>::new("Hello, world!"), "Hello, world!"); /// ``` pub const fn new(string: &str) -> Self { Self::concat(&[string]) } /// Constructs an `ArrayString` by concatenating zero or more `&str`s /// /// # Panics /// /// Panics if the concatenated string would be longer than `CAP`. /// /// # Example /// /// ```rust /// use const_panic::ArrayString; /// /// assert_eq!( /// ArrayString::<99>::concat(&["This ", "is ", "a string"]), /// "This is a string" /// ); /// ``` pub const fn concat(strings: &[&str]) -> Self { let mut len = 0u32; let mut buffer = [0u8; CAP]; let mut mstrings = strings; while let [string, ref rem @ ..] = *mstrings { mstrings = rem; let mut bytes = string.as_bytes(); while let [x, ref rem @ ..] = *bytes { if len == u32::MAX || len as usize >= CAP { crate::concat_panic(&[&[ PanicVal::write_str("The input strings were longer than "), PanicVal::from_usize(CAP, FmtArg::DISPLAY), PanicVal::write_str(", concatenated length: "), PanicVal::from_usize(add_up_lengths(strings), FmtArg::DISPLAY), PanicVal::write_str(", strings: "), PanicVal::from_slice_str(strings, FmtArg::DEBUG), ]]) } bytes = rem; buffer[len as usize] = x; len += 1; } } Self { len, buffer } } /// Constructs this string from a `&[&[PanicVal<'_>]]`. /// /// Returns `None` if the formatted args would be larger than `CAP`. /// /// # Example /// /// ```rust /// use const_panic::{ArrayString, FmtArg, flatten_panicvals}; /// /// assert_eq!( /// ArrayString::<17>::concat_panicvals(&[ /// &flatten_panicvals!(FmtArg::DEBUG; 1u8, ("hello")), /// &flatten_panicvals!(FmtArg::DEBUG; &[3u8, 5, 8]), /// ]).unwrap(), /// "1\"hello\"[3, 5, 8]", /// ); /// /// assert!( /// ArrayString::<16>::concat_panicvals(&[ /// &flatten_panicvals!(FmtArg::DEBUG; 1u8, ("hello")), /// &flatten_panicvals!(FmtArg::DEBUG; &[3u8, 5, 8]), /// ]).is_none(), /// ); /// /// ``` /// pub const fn concat_panicvals(args: &[&[PanicVal<'_>]]) -> Option { match crate::concat_panic_::make_panic_string::(args) { Ok(x) => Some(x), Err(_) => None, } } /// Constructs this string from a `&[PanicVal<'_>]`. /// /// Returns `None` if the formatted args would be larger than `CAP`. /// /// # Example /// /// ```rust /// use const_panic::{ArrayString, FmtArg, flatten_panicvals}; /// /// assert_eq!( /// ArrayString::<8>::from_panicvals( /// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello") /// ).unwrap(), /// "100hello", /// ); /// /// // trying to format panicvals into too small an ArrayString /// assert!( /// ArrayString::<7>::from_panicvals( /// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello") /// ).is_none(), /// ); /// /// ``` pub const fn from_panicvals(args: &[PanicVal<'_>]) -> Option { Self::concat_panicvals(&[args]) } /// How long the string is in bytes. /// /// # Example /// /// ```rust /// use const_panic::ArrayString; /// /// assert_eq!(ArrayString::<16>::new("foo").len(), 3); /// assert_eq!(ArrayString::<16>::new("foo bar").len(), 7); /// assert_eq!(ArrayString::<16>::new("Hello, world!").len(), 13); /// ``` pub const fn len(&self) -> usize { self.len as usize } /// Accesses the string as a byte slice. /// /// # Performance /// /// When the "rust_1_64" feature is disabled, /// this takes a linear amount of time to run, proportional to `CAP - self.len()`. /// /// When the "rust_1_64" feature is enabled, /// this takes a constant amount of time to run. /// /// # Example /// /// ```rust /// use const_panic::ArrayString; /// /// assert_eq!(ArrayString::<16>::new("foo").as_bytes(), b"foo"); /// assert_eq!(ArrayString::<16>::new("foo bar").as_bytes(), b"foo bar"); /// assert_eq!(ArrayString::<16>::new("Hello, world!").as_bytes(), b"Hello, world!"); /// ``` pub const fn as_bytes(&self) -> &[u8] { bytes_up_to(&self.buffer, self.len()) } /// Gets the string. /// /// # Performance /// /// This takes a linear amount of time to run. /// /// # Example /// /// ```rust /// use const_panic::ArrayString; /// /// assert_eq!(ArrayString::<16>::new("foo").to_str(), "foo"); /// assert_eq!(ArrayString::<16>::new("foo bar").to_str(), "foo bar"); /// assert_eq!(ArrayString::<16>::new("Hello, world!").to_str(), "Hello, world!"); /// ``` pub const fn to_str(&self) -> &str { #[cfg(not(feature = "rust_1_64"))] { // safety: make_panic_string delegates formatting to the `write_to_buffer` macro, // which is tested as producing valid utf8. unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } } #[cfg(feature = "rust_1_64")] match core::str::from_utf8(self.as_bytes()) { Ok(x) => x, Err(_) => panic!("INTERNAL BUG: the string isn't valid utf-8"), } } /// Creates a single element `PanicVal` borrowing from this string. pub const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; 1] { [PanicVal::from_str(self.to_str(), f)] } /// Creates a `PanicVal` borrowing from this string. pub const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_> { PanicVal::from_str(self.to_str(), f) } } impl ArrayString { pub(crate) const fn to_compact(self) -> TinyString { if self.len() <= 255 { TinyString { len: self.len as u8, buffer: self.buffer, } } else { crate::concat_panic(&[&[ PanicVal::write_str( "The input string is too long, expected `length <= 255`, found length: ", ), PanicVal::from_usize(self.len(), FmtArg::DISPLAY), ]]) } } } impl Debug for ArrayString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(self.to_str(), f) } } impl PartialEq for ArrayString { fn eq(&self, str: &str) -> bool { self.to_str() == str } } impl PartialEq<&str> for ArrayString { fn eq(&self, str: &&str) -> bool { self == *str } } impl PanicFmt for ArrayString { type This = Self; type Kind = crate::fmt::IsCustomType; const PV_COUNT: usize = 1; } //////////////////////////////////////////////////////////////////////////////// impl TinyString { pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> { RangedBytes { start: 0, end: self.len as usize, bytes: &self.buffer, } } } const_panic-0.2.13/src/concat_panic_.rs000064400000000000000000000240771046102023000161360ustar 00000000000000use crate::{ fmt::FmtKind, panic_val::{PanicClass, PanicVal, StrFmt}, utils::{bytes_up_to, string_cap, WasTruncated}, }; /// Panics by concatenating the argument slice. /// /// This is the function that the [`concat_panic`](macro@concat_panic) macro calls to panic. /// /// # Example /// /// Here's how to panic with formatting without using any macros: /// /// ```compile_fail /// use const_panic::{FmtArg, PanicVal, concat_panic}; /// /// const _: () = concat_panic(&[&[ /// PanicVal::write_str("\nthe error was "), /// PanicVal::from_u8(100, FmtArg::DISPLAY), /// PanicVal::write_str(" and "), /// PanicVal::from_str("\nHello\tworld", FmtArg::DEBUG), /// ]]); /// /// /// ``` /// That fails to compile with this error message: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/concat_panic_.rs:13:15 /// | /// 6 | const _: () = concat_panic(&[&[ /// | _______________^ /// 7 | | PanicVal::write_str("\nthe error was "), /// 8 | | PanicVal::from_u8(100, FmtArg::DISPLAY), /// 9 | | PanicVal::write_str(" and "), /// 10 | | PanicVal::from_str("\nHello\tworld", FmtArg::DEBUG), /// 11 | | ]]); /// | |___^ the evaluated program panicked at ' /// the error was 100 and "\nHello\tworld"', src/concat_panic_.rs:6:15 /// ``` /// #[cold] #[inline(never)] #[track_caller] pub const fn concat_panic(args: &[&[PanicVal<'_>]]) -> ! { // The panic message capacity starts small and gets larger each time, // so that platforms with smaller stacks can call this at runtime. // // Also, given that most(?) panic messages are smaller than 1024 bytes long, // it's not going to be any less efficient in the common case. if let Err(_) = panic_inner::<1024>(args) {} if let Err(_) = panic_inner::<{ 1024 * 6 }>(args) {} match panic_inner::(args) { Ok(x) => match x {}, Err(_) => panic!( "\ unreachable:\n\ the `write_panicval_to_buffer` macro must not return Err when \ $capacity == $max_capacity\ " ), } } /// The maximum length of panic messages (in bytes), /// after which the message is truncated. // this should probably be smaller on platforms where this // const fn is called at runtime, and the stack is finy. pub const MAX_PANIC_MSG_LEN: usize = 32768; // writes a single PanicVal to an array macro_rules! write_panicval { ( $outer_label:lifetime, $mout:ident, $lout:ident, $tct:expr, ( $len:expr, $capacity:expr, $max_capacity:expr, $not_enough_space:expr, $write_buffer:ident, $write_buffer_checked:ident, ) ) => { let rem_space = $capacity - $len; let (strfmt, class, was_truncated) = $tct; let StrFmt { leftpad: mut lpad, rightpad: mut rpad, fmt_kind, } = strfmt; let ranged = match class { PanicClass::PreFmt(str) => str, PanicClass::Int(int) => { if int.len() <= string_cap::MEDIUM { $mout = int.fmt::<{ string_cap::MEDIUM }>(); $mout.ranged() } else { $lout = int.fmt::<{ string_cap::LARGE }>(); $lout.ranged() } } #[cfg(feature = "non_basic")] PanicClass::Slice(_) => unreachable!(), }; let trunc_end = ranged.start + was_truncated.get_length(ranged.len()); while lpad != 0 { $write_buffer! {b' '} lpad -= 1; } if let FmtKind::Display = fmt_kind { let mut i = ranged.start; while i < trunc_end { $write_buffer! {ranged.bytes[i]} i += 1; } } else if rem_space != 0 { $write_buffer! {b'"'} let mut i = 0; while i < trunc_end { use crate::debug_str_fmt::{hex_as_ascii, ForEscaping}; let c = ranged.bytes[i]; let mut written_c = c; if ForEscaping::is_escaped(c) { $write_buffer! {b'\\'} if ForEscaping::is_backslash_escaped(c) { written_c = ForEscaping::get_backslash_escape(c); } else { $write_buffer! {b'x'} $write_buffer! {hex_as_ascii(c >> 4)} written_c = hex_as_ascii(c & 0b1111); }; } $write_buffer! {written_c} i += 1; } if let WasTruncated::No = was_truncated { $write_buffer_checked! {b'"'} } } while rpad != 0 { $write_buffer! {b' '} rpad -= 1; } if let WasTruncated::Yes(_) = was_truncated { if $capacity < $max_capacity { return $not_enough_space; } else { break $outer_label; } } }; } macro_rules! write_to_buffer_inner { ( $args:ident ( $len:expr, $capacity:expr, $($_rem:tt)* ) $wptb_args:tt ) => { let mut args = $args; let mut mout; let mut lout; 'outer: while let [mut outer, ref nargs @ ..] = args { while let [arg, nouter @ ..] = outer { let tct = arg.to_class_truncated($capacity - $len); match tct.1 { #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] PanicClass::Slice(slice) => { let mut iter = slice.iter(); 'iter: loop { let (two_args, niter) = iter.next(); let mut two_args: &[_] = &two_args; while let [arg, ntwo_args @ ..] = two_args { let tct = arg.to_class_truncated($capacity - $len); write_panicval! {'outer, mout, lout, tct, $wptb_args} two_args = ntwo_args; } match niter { Some(x) => iter = x, None => break 'iter, } } } _ => { write_panicval! {'outer, mout, lout, tct, $wptb_args} } } outer = nouter; } args = nargs; } }; } macro_rules! write_to_buffer { ($args:ident $wptb_args:tt) => { write_to_buffer_inner! { $args $wptb_args $wptb_args } }; } macro_rules! make_buffer_writer_macros { ($buffer:ident, $len:ident) => { macro_rules! write_buffer { ($value:expr) => { __write_array! {$buffer, $len, $value} }; } macro_rules! write_buffer_checked { ($value:expr) => { __write_array_checked! {$buffer, $len, $value} }; } }; } #[cold] #[inline(never)] #[track_caller] const fn panic_inner(args: &[&[PanicVal<'_>]]) -> Result { let mut buffer = [0u8; LEN]; let mut len = 0usize; make_buffer_writer_macros! {buffer, len} write_to_buffer! { args ( len, LEN, MAX_PANIC_MSG_LEN, Err(NotEnoughSpace), write_buffer, write_buffer_checked, ) } unsafe { let buffer = bytes_up_to(&buffer, len); let str = core::str::from_utf8_unchecked(buffer); panic!("{}", str) } } #[doc(hidden)] #[derive(Debug)] pub struct NotEnoughSpace; enum Never {} #[cfg(feature = "test")] use crate::test_utils::TestString; #[doc(hidden)] #[cfg(feature = "test")] pub fn format_panic_message( args: &[&[PanicVal<'_>]], capacity: usize, max_capacity: usize, ) -> Result, NotEnoughSpace> { let mut buffer = [0u8; LEN]; let mut len = 0usize; { // intentionally shadowed let buffer = &mut buffer[..capacity]; make_buffer_writer_macros! {buffer, len} write_to_buffer! { args ( len, capacity, max_capacity, Err(NotEnoughSpace), write_buffer, write_buffer_checked, ) } } Ok(TestString { buffer, len }) } #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[doc(hidden)] pub(crate) const fn make_panic_string( args: &[&[PanicVal<'_>]], ) -> Result, NotEnoughSpace> { let mut buffer = [0u8; LEN]; let mut len = 0usize; make_buffer_writer_macros! {buffer, len} write_to_buffer! { args (len, LEN, LEN + 1, Err(NotEnoughSpace), write_buffer, write_buffer_checked,) } assert!(len as u32 as usize == len, "the panic message is too large"); Ok(crate::ArrayString { buffer, len: len as u32, }) } #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[doc(hidden)] #[track_caller] pub const fn make_panic_string_unwrapped( args: &[&[PanicVal<'_>]], ) -> crate::ArrayString { match make_panic_string(args) { Ok(x) => x, Err(_) => panic!("arguments are too large to fit in LEN"), } } #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[doc(hidden)] pub const fn compute_length(args: &[&[PanicVal<'_>]]) -> usize { let mut len = 0usize; macro_rules! add_to_len { ($value:expr) => {{ let _: u8 = $value; len += 1; }}; } write_to_buffer! { args ( len, usize::MAX - 1, usize::MAX, usize::MAX, add_to_len, add_to_len, ) } len } const_panic-0.2.13/src/const_default.rs000064400000000000000000000014361046102023000162020ustar 00000000000000/// Default values for const parameters /// /// This trait is sealed so that it can be potentially replaced with /// an external `ConstDefault` trait when a potential future feature is enabled. #[doc(hidden)] pub trait ConstDefault: sealed::Sealed { const DEFAULT: Self; } mod sealed { pub trait Sealed {} } macro_rules! impl_constdefault { ($($ty:ty = $val:expr),* $(,)?) => ( $( impl sealed::Sealed for $ty {} impl ConstDefault for $ty { const DEFAULT: Self = $val; } )* ) } impl_constdefault! { u8 = 0, u16 = 0, u32 = 0, u64 = 0, u128 = 0, usize = 0, i8 = 0, i16 = 0, i32 = 0, i64 = 0, i128 = 0, isize = 0, bool = false, char = '\0', &str = "", } const_panic-0.2.13/src/debug_str_fmt.rs000064400000000000000000000047631046102023000162020ustar 00000000000000// most of this copied from const_format, which is mine, but still.. #[doc(hidden)] pub(crate) struct ForEscaping { pub(crate) is_escaped: u128, pub(crate) is_backslash_escaped: u128, pub(crate) escape_char: [u8; 16], } impl ForEscaping { /// Gets the backslash escape for a character that is kwown to be escaped with a backslash. #[inline(always)] pub(crate) const fn get_backslash_escape(b: u8) -> u8 { FOR_ESCAPING.escape_char[(b & 0b1111) as usize] } // how long this byte inside a utf8 string takes to represent in debug formatting. pub(crate) const fn byte_len(c: u8) -> usize { if c < 128 { let shifted = 1 << c; if (FOR_ESCAPING.is_escaped & shifted) != 0 { if (FOR_ESCAPING.is_backslash_escaped & shifted) != 0 { 2 } else { 4 } } else { 1 } } else { 1 } } pub(crate) const fn is_escaped(c: u8) -> bool { (c < 128) && ((FOR_ESCAPING.is_escaped & (1 << c)) != 0) } pub(crate) const fn is_backslash_escaped(c: u8) -> bool { (c < 128) && ((FOR_ESCAPING.is_backslash_escaped & (1 << c)) != 0) } } #[doc(hidden)] /// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F' #[inline(always)] pub(crate) const fn hex_as_ascii(n: u8) -> u8 { if n < 10 { n + b'0' } else { n - 10 + b'A' } } #[doc(hidden)] pub(crate) const FOR_ESCAPING: &ForEscaping = { let mut is_backslash_escaped = 0; let escaped = [ (b'\t', b't'), (b'\n', b'n'), (b'\r', b'r'), (b'\'', b'\''), (b'"', b'"'), (b'\\', b'\\'), ]; // Using the fact that the characters above all have different bit patterns for // the lowest 4 bits. let mut escape_char = [0u8; 16]; let escaped_len = escaped.len(); let mut i = 0; while i < escaped_len { let (code, escape) = escaped[i]; is_backslash_escaped |= 1 << code; let ei = (code & 0b1111) as usize; if escape_char[ei] != 0 { panic!("Oh no, some escaped character uses the same 4 lower bits as another") } escape_char[ei] = escape; i += 1; } // Setting all the control characters as being escaped. let is_escaped = is_backslash_escaped | 0xFFFF_FFFF; &ForEscaping { escape_char, is_backslash_escaped, is_escaped, } }; const_panic-0.2.13/src/doc_macros.rs000064400000000000000000000036121046102023000154570ustar 00000000000000macro_rules! formatting_docs {($($additional_fmt_overrides:expr)?) => { concat!(r##" # Formatting Literals are Display formatted by default, so that you can pass string literals without worrying about what the current formatting settings are. Expressions are formatted as determined by the `$fmtarg` argument. Note that literals inside parentheses (eg: `(100)`) are considered expressions by this macro. ### Formatting overrides You can override how an argument is formatted by prefixing the argument expression with any of the options below: - `debug:` or `{?}:`: `Debug` formats the argument. - `alt_debug:` or `{#?}:`: alternate-`Debug` formats the argument. - `display:` or `{}:`: `Display` formats the argument. - `alt_display:` or `{#}:`: alternate-`Display` formats the argument. - `bin:` or `{b}:`: `Debug` formats the argument, with binary-formatted numbers. - `alt_bin:` or `{#b}:`: alternate-`Debug` formats the argument, with binary-formatted numbers. - `hex:` or `{X}:`: `Debug` formats the argument, with hexadecimal-formatted numbers. - `alt_hex:` or `{#X}:`: alternate-`Debug` formats the argument, with hexadecimal-formatted numbers. "##, $($additional_fmt_overrides,)? r##" ### String formatting String expressions are debug-formatted like this: - Prepending and appending the double quote character (`"`). - Escaping the `'\t'`,`'\n'`,`'\r'`,`'\\'`, `'\''`, and`'\"'` characters. - Escaping control characters with `\xYY`, where `YY` is the hexadecimal value of the control character. "## )}} macro_rules! limitation_docs {() => { " Arguments to the formatting/panicking macros must have a fully inferred concrete type, because `const_panic` macros use duck typing to call methods on those arguments. One effect of that limitation is that you will have to pass suffixed integer literals (eg: `100u8`) when those integers aren't inferred to be a concrete type. " }} pub(crate) use limitation_docs; const_panic-0.2.13/src/doctests.rs000064400000000000000000000007621046102023000152010ustar 00000000000000/// ################################################### /// /// Ensure that phantom type parameters must be ignored /// /// ```compile_fail /// struct Foo(std::marker::PhantomData); /// /// const_panic::impl_panicfmt!{ /// struct Foo(std::marker::PhantomData); /// } /// ``` /// /// ```rust /// struct Foo(std::marker::PhantomData); /// /// const_panic::impl_panicfmt!{ /// struct Foo(std::marker::PhantomData); /// } /// ``` /// /// pub struct ImplPanicFmt; const_panic-0.2.13/src/fmt/char_formatting/tests.rs000064400000000000000000000054451046102023000204530ustar 00000000000000use super::{char_debug_len, char_to_debug, char_to_display, FmtChar}; fn as_bytes(fmt: &FmtChar) -> &[u8] { &fmt.encoded()[..fmt.len()] } #[test] fn char_to_utf8_encoding_test() { for c in '\0'..=core::char::MAX { let mut utf8_std = [0u8; 4]; let utf8_std = c.encode_utf8(&mut utf8_std); let utf8_here = char_to_display(c); assert_eq!(utf8_std.as_bytes(), as_bytes(&utf8_here)); } } #[test] fn char_to_utf8_display_test() { for c in '\0'..=core::char::MAX { let mut utf8_std = [0u8; 4]; let utf8_std = c.encode_utf8(&mut utf8_std); let utf8_here = char_to_display(c); assert_eq!(utf8_std.as_bytes(), as_bytes(&utf8_here)); } } #[test] fn char_to_utf8_debug_test() { let first_escapes = [ ('\x00', r#"'\x00'"#), ('\x01', r#"'\x01'"#), ('\x02', r#"'\x02'"#), ('\x03', r#"'\x03'"#), ('\x04', r#"'\x04'"#), ('\x05', r#"'\x05'"#), ('\x06', r#"'\x06'"#), ('\x07', r#"'\x07'"#), ('\x08', r#"'\x08'"#), ('\t', r#"'\t'"#), ('\n', r#"'\n'"#), ('\x0B', r#"'\x0B'"#), ('\x0C', r#"'\x0C'"#), ('\r', r#"'\r'"#), ('\x0E', r#"'\x0E'"#), ('\x0F', r#"'\x0F'"#), ('\x10', r#"'\x10'"#), ('\x11', r#"'\x11'"#), ('\x12', r#"'\x12'"#), ('\x13', r#"'\x13'"#), ('\x14', r#"'\x14'"#), ('\x15', r#"'\x15'"#), ('\x16', r#"'\x16'"#), ('\x17', r#"'\x17'"#), ('\x18', r#"'\x18'"#), ('\x19', r#"'\x19'"#), ('\x1A', r#"'\x1A'"#), ('\x1B', r#"'\x1B'"#), ('\x1C', r#"'\x1C'"#), ('\x1D', r#"'\x1D'"#), ('\x1E', r#"'\x1E'"#), ('\x1F', r#"'\x1F'"#), ]; for (c, expected) in first_escapes.iter().copied() { let utf8_here = char_to_debug(c); assert_eq!(expected.as_bytes(), as_bytes(&utf8_here), "{:?}", c); assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); } let other_escapes = [('\'', r#"'\''"#), ('\"', r#"'"'"#), ('\\', r#"'\\'"#)]; let mut buffer = arrayvec::ArrayString::<12>::new(); for c in '\x20'..=core::char::MAX { let utf8_here = char_to_debug(c); if let Some((_, expected)) = Some(c) .filter(|c| *c <= '\x7F') .and_then(|c| other_escapes.iter().copied().find(|x| x.0 == c)) { assert_eq!(expected.as_bytes(), as_bytes(&utf8_here), "{:?}", c); assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); } else { buffer.clear(); buffer.push('\''); buffer.push(c); buffer.push('\''); assert_eq!(buffer.as_bytes(), as_bytes(&utf8_here), "{:?}", c); assert_eq!(buffer.len(), char_debug_len(c), "{:?}", c); } } } const_panic-0.2.13/src/fmt/char_formatting.rs000064400000000000000000000102071046102023000173010ustar 00000000000000//! `char`-formatted related items use crate::{ fmt::{FmtArg, FmtKind}, fmt_impls::basic_fmt_impls::primitive_static_panicfmt, panic_val::{PanicVal, PanicVariant}, utils::{string_cap, PreFmtString, StartAndBytes}, }; #[cfg(all(test, not(miri)))] mod tests; impl PanicVal<'_> { /// Constructs a `PanicVal` from a `char`. pub const fn from_char(c: char, fmtarg: FmtArg) -> Self { let StartAndBytes { start, bytes } = match fmtarg.fmt_kind { FmtKind::Display => { let (arr, len) = char_to_utf8(c); crate::utils::tail_byte_array::<{ string_cap::PREFMT }>(len, &arr) } FmtKind::Debug => { let fmtchar = char_to_debug(c); crate::utils::tail_byte_array(fmtchar.len(), &fmtchar.encoded) } }; // SAFETY: // char_to_utf8 is exhaustively tested in the tests module. // char_to_debug is exhaustively tested in the tests module. // tail_byte_array is also tested for smaller/equal/larger input arrays. let prefmt = unsafe { PreFmtString::new(start, bytes) }; PanicVal { var: PanicVariant::PreFmt(prefmt), } } } primitive_static_panicfmt! { fn[](&self: char, fmtarg) { PanicVal::from_char(*self.0, fmtarg) } } /// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F' #[inline] const fn hex_as_ascii(n: u8) -> u8 { if n < 10 { n + b'0' } else { n - 10 + b'A' } } #[cfg(test)] pub(crate) const fn char_debug_len(c: char) -> usize { let inner = match c { '\t' | '\r' | '\n' | '\\' | '\'' => 2, '\x00'..='\x1F' => 4, _ => c.len_utf8(), }; inner + 2 } const fn char_to_utf8(char: char) -> ([u8; 4], usize) { let u32 = char as u32; match u32 { 0..=127 => ([u32 as u8, 0, 0, 0], 1), 0x80..=0x7FF => { let b0 = 0b1100_0000 | (u32 >> 6) as u8; let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, 0, 0], 2) } 0x800..=0xFFFF => { let b0 = 0b1110_0000 | (u32 >> 12) as u8; let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, b2, 0], 3) } 0x10000..=u32::MAX => { let b0 = 0b1111_0000 | (u32 >> 18) as u8; let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8; let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, b2, b3], 4) } } } /// Display formats a `char` pub const fn char_to_display(char: char) -> FmtChar { let ([b0, b1, b2, b3], len) = char_to_utf8(char); FmtChar { encoded: [b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0, 0], len: len as u8, } } /// Debug formats a `char` pub const fn char_to_debug(c: char) -> FmtChar { let ([b0, b1, b2, b3], len) = match c { '\t' => (*br#"\t "#, 2), '\r' => (*br#"\r "#, 2), '\n' => (*br#"\n "#, 2), '\\' => (*br#"\\ "#, 2), '\'' => (*br#"\' "#, 2), '\"' => (*br#"" "#, 1), '\x00'..='\x1F' => { let n = c as u8; ( [b'\\', b'x', hex_as_ascii(n >> 4), hex_as_ascii(n & 0b1111)], 4, ) } _ => char_to_utf8(c), }; let mut encoded = [b'\'', b0, b1, b2, b3, 0, 0, 0, 0, 0, 0, 0]; encoded[len + 1] = b'\''; FmtChar { encoded, len: (len as u8) + 2, } } /// An byte slice with a display/debug formatted `char`. /// /// To get the encoded character, you need to do /// `&fmt_char.encoded()[..fmt_char.len()]`. #[derive(Copy, Clone)] pub struct FmtChar { encoded: [u8; 12], len: u8, } impl FmtChar { /// Array which contains the display/debug-formatted `char`, /// and trailing `0` padding. pub const fn encoded(&self) -> &[u8; 12] { &self.encoded } /// The length of the subslice that contains the formatted character. pub const fn len(&self) -> usize { self.len as usize } } const_panic-0.2.13/src/fmt/fmt_compressed.rs000064400000000000000000000046501046102023000171510ustar 00000000000000use crate::fmt::{FmtArg, FmtKind, NumberFmt}; /// A version of FmtArg which occupies less space, but needs to be unpacked to be used. #[derive(Copy, Clone)] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub struct PackedFmtArg { indentation: u8, bitfields: u8, } const FMT_KIND_OFFSET: u8 = 1; const NUMBER_FMT_OFFSET: u8 = FMT_KIND_OFFSET + FmtKind::BITS; impl FmtArg { /// Converts this `FmtArg` into a `PackedFmtArg`, /// which is smaller but can only be converted back into a `FmtArg`. #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub const fn pack(self) -> PackedFmtArg { let Self { indentation, is_alternate, fmt_kind, number_fmt, } = self; PackedFmtArg { indentation, bitfields: is_alternate as u8 | ((fmt_kind as u8) << FMT_KIND_OFFSET) | ((number_fmt as u8) << NUMBER_FMT_OFFSET), } } } impl PackedFmtArg { /// Converts this `PackedFmtArg` back into a `FmtArg`. pub const fn unpack(self) -> FmtArg { let indentation = self.indentation; let is_alternate = (self.bitfields & 1) != 0; let fmt_kind = FmtKind::from_prim(self.bitfields >> FMT_KIND_OFFSET); let number_fmt = NumberFmt::from_prim(self.bitfields >> NUMBER_FMT_OFFSET); FmtArg { indentation, is_alternate, fmt_kind, number_fmt, } } } macro_rules! enum_prim { ( $type:ident, $bits:expr; default $default:ident, $($variant:ident),* $(,)? ) => ( #[allow(non_upper_case_globals)] const _: () = { $(const $variant: u8 = $type::$variant as u8;)* const __MASK: u8 = 2u8.pow($bits) - 1; match $type::$default { $($type::$variant => (),)* $type::$default => (), } impl $type { const BITS: u8 = $bits; const fn from_prim(n: u8) -> Self { match n & __MASK { $($variant => Self::$variant,)* _ => Self::$default, } } } }; ) } use enum_prim; enum_prim! { FmtKind, 2; default Debug, Display, } enum_prim! { NumberFmt, 2; default Decimal, Binary, Hexadecimal, } const_panic-0.2.13/src/fmt/non_basic_fmt.rs000064400000000000000000000410741046102023000167410ustar 00000000000000use crate::{ArrayString, PanicVal}; use super::{FmtArg, IsCustomType, PanicFmt}; /// For outputting an [alternate flag]-aware delimiter. /// /// This was created for formatting structs and enum variants, /// so these delimiters have spaces around them to follow the
/// `Foo { bar: baz }`, `Foo(bar)`, and `[foo, bar]` style. /// /// # Example /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg}, /// ArrayString, /// flatten_panicvals, /// }; /// /// // Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; /// open: fmt::EmptyDelimiter, /// 100u8, fmt::COMMA_SEP, /// false, fmt::COMMA_SEP, /// [0u16; 0], fmt::COMMA_SEP, /// // parenthesizing to pass this as a non-literal /// // otherwise the string is Display formatted /// ("really"), fmt::COMMA_TERM, /// close: "", /// ), /// " 100, false, [], \"really\"" /// ); /// /// /// // Alternate-Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; /// open: fmt::EmptyDelimiter, /// 100u8, fmt::COMMA_SEP, /// false, fmt::COMMA_SEP, /// [0u16; 0], fmt::COMMA_SEP, /// ("really"), fmt::COMMA_TERM, /// close: "", /// ), /// concat!( /// "\n", /// " 100,\n", /// " false,\n", /// " [],\n", /// " \"really\",\n", /// ) /// ); /// /// ``` /// /// [alternate flag]: crate::FmtArg#structfield.is_alternate #[non_exhaustive] #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub enum Delimiter { /// `(` OpenParen, /// `)` CloseParen, /// `[` OpenBracket, /// `]` CloseBracket, /// ` { ` OpenBrace, /// ` }` CloseBrace, /// An empty delimiter, /// exists only to add whitespace on the next line when /// the alternate flag is enabled. Empty, } pub use self::Delimiter::{ CloseBrace, CloseBracket, CloseParen, Empty as EmptyDelimiter, OpenBrace, OpenBracket, OpenParen, }; impl Delimiter { /// Converts this `Delimiter` into an array of one `PanicVal` /// /// When the [alternate flag] is enabled, this and the `to_panicval` method output: /// - the delimiter /// - a newline /// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces /// /// When the [alternate flag] is disabled, /// these methods output braces with spaces around them, /// the empty delimiter as one space, /// and the remaining delimiters with no spaces around them. /// /// [alternate flag]: crate::FmtArg#structfield.is_alternate /// pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] { [self.to_panicval(f)] } /// Converts this `Delimiter` into a `PanicVal` pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> { match (self, f.is_alternate) { (Self::OpenParen, false) => PanicVal::write_str("("), (Self::CloseParen, false) => PanicVal::write_str(")"), (Self::OpenBracket, false) => PanicVal::write_str("["), (Self::CloseBracket, false) => PanicVal::write_str("]"), (Self::OpenBrace, false) => PanicVal::write_str(" { "), (Self::CloseBrace, false) => PanicVal::write_str(" }"), (Self::Empty, false) => PanicVal::write_str(" "), (Self::OpenParen, true) => PanicVal::write_str("(\n").with_rightpad(f), (Self::CloseParen, true) => PanicVal::write_str(")").with_leftpad(f), (Self::OpenBracket, true) => PanicVal::write_str("[\n").with_rightpad(f), (Self::CloseBracket, true) => PanicVal::write_str("]").with_leftpad(f), (Self::OpenBrace, true) => PanicVal::write_str(" {\n").with_rightpad(f), (Self::CloseBrace, true) => PanicVal::write_str("}").with_leftpad(f), (Self::Empty, true) => PanicVal::write_str("\n").with_rightpad(f), } } } impl PanicFmt for Delimiter { type This = Self; type Kind = IsCustomType; const PV_COUNT: usize = 1; } //////////////////////////////////////////////////////////////////////////////// /// How much indentation (in spaces) is added with [`FmtArg::indent`], /// and removed with [`FmtArg::unindent`]. /// /// [The FmtArg.indentation field](crate::FmtArg#structfield.indentation) /// is used by [`fmt::Delimiter`](crate::fmt::Delimiter) /// and by [`fmt::Separator`](crate::fmt::Separator), /// when the [`is_alternate`](crate::FmtArg#structfield.is_alternate) flag is enabled. #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub const INDENTATION_STEP: u8 = 4; //////////////////////////////////////////////////////////////////////////////// /// A stack allocated string type that's convertible into /// [`PanicVal<'static>`](PanicVal), /// with [`SHORT_STRING_CAP`] capacity. /// /// # Example /// /// ```rust /// use const_panic::{ /// fmt::ShortString, /// ArrayString, FmtArg, PanicVal, /// }; /// /// let pv: PanicVal<'static> = /// PanicVal::write_short_str(ShortString::new("3,13,21,34,55,89")); /// /// assert_eq!(ArrayString::<20>::from_panicvals(&[pv]).unwrap(), "3,13,21,34,55,89"); /// /// /// let pv_debug: PanicVal<'static> = /// PanicVal::from_short_str(ShortString::new("foo\n\0bar"), FmtArg::DEBUG); /// /// assert_eq!( /// ArrayString::<20>::from_panicvals(&[pv_debug]).unwrap(), /// "\"foo\\n\\x00bar\"", /// ); /// /// ``` #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub type ShortString = ArrayString; pub use crate::utils::string_cap::TINY as SHORT_STRING_CAP; impl<'a> PanicVal<'a> { /// Constructs a `PanicVal` from a `ShortString`. pub const fn from_short_str(this: ShortString, f: FmtArg) -> PanicVal<'a> { use crate::panic_val::{PanicVariant, StrFmt}; PanicVal::__new(PanicVariant::ShortString(StrFmt::new(f), this.to_compact())) } } //////////////////////////////////////////////////////////////////////////////// /// For computing the [`PanicFmt::PV_COUNT`] of a struct or enum variant, /// with the [`call`](ComputePvCount::call) method. /// /// This assumes that you write the `to_panicvals` method like in the /// [`flatten_panicvals` examples](crate::flatten_panicvals#examples) /// /// # Examples /// /// These examples demonstrates how to use this type by itself, /// for a more complete example you can look at the /// [`flatten_panicvals` examples](crate::flatten_panicvals#examples) /// /// ### Struct /// /// ```rust /// use const_panic::{ComputePvCount, PanicFmt}; /// /// struct Foo<'a> { /// x: u32, /// y: &'a [&'a str], /// z: Bar, /// } /// # type Bar = u8; /// /// impl PanicFmt for Foo<'_> { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// const PV_COUNT: usize = ComputePvCount{ /// field_amount: 3, /// summed_pv_count: /// u32::PV_COUNT + /// <&[&str]>::PV_COUNT + /// ::PV_COUNT, /// delimiter: const_panic::TypeDelim::Braced, /// }.call(); /// } /// /// assert_eq!(Foo::PV_COUNT, 12); /// /// ``` /// /// ### Enum /// /// ```rust /// use const_panic::{ComputePvCount, PanicFmt}; /// /// enum Foo { /// Bar, /// Baz(u32, u64), /// } /// # type Bar = u8; /// /// impl PanicFmt for Foo { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// const PV_COUNT: usize = const_panic::utils::slice_max_usize(&[ /// ComputePvCount{ /// field_amount: 0, /// summed_pv_count: 0, /// delimiter: const_panic::TypeDelim::Braced, /// }.call(), /// ComputePvCount{ /// field_amount: 2, /// summed_pv_count: ::PV_COUNT + ::PV_COUNT, /// delimiter: const_panic::TypeDelim::Tupled, /// }.call(), /// ]); /// } /// /// assert_eq!(Foo::PV_COUNT, 7); /// /// ``` /// /// /// /// /// #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub struct ComputePvCount { /// The amount of fields in the struct pub field_amount: usize, /// The summed up amount of `PanicVal`s that all the fields produce. /// /// Eg: for a struct with `Bar` and `Qux` fields, this would be /// `::PV_COUNT + ::PV_COUNT`, /// pub summed_pv_count: usize, /// Whether it's a tupled or braced struct/variant. pub delimiter: TypeDelim, } impl ComputePvCount { /// Does the computation. pub const fn call(&self) -> usize { // field-less structs and variants don't output the empty delimiter if self.field_amount == 0 { return 1; } const TYPE_NAME: usize = 1; const DELIM_TOKENS: usize = 2; let field_tokens = match self.delimiter { TypeDelim::Tupled => self.field_amount, TypeDelim::Braced => 2 * self.field_amount, }; TYPE_NAME + DELIM_TOKENS + field_tokens + self.summed_pv_count } } /// Whether a struct or variant is Tupled or Braced. /// /// Unit structs/variants are considered braced. /// /// # Example /// /// ### Formatting /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg, TypeDelim}, /// ArrayString, /// flatten_panicvals, /// }; /// /// { /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; /// "Foo", /// open: TypeDelim::Tupled.open(), /// 10u8, fmt::COMMA_SEP, /// false, fmt::COMMA_TERM, /// close: TypeDelim::Tupled.close(), /// ), /// "Foo(10, false)" /// ); /// } /// /// { /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; /// "Bar", /// open: TypeDelim::Braced.open(), /// "x: ", debug: "hello", fmt::COMMA_SEP, /// "y: ", true, fmt::COMMA_TERM, /// close: TypeDelim::Braced.close(), /// ), /// "Bar { x: \"hello\", y: true }" /// ); /// } /// /// ``` /// #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub enum TypeDelim { /// A `Foo(Bar)` type or variant Tupled, /// A `Foo{bar: Baz}` type or variant Braced, } impl TypeDelim { /// Gets the delimiters that surround the fields of a struct or variant. pub const fn get_open_and_close(self) -> (Delimiter, Delimiter) { (self.open(), self.close()) } /// Gets the group delimiter that precedes the fields of a struct or variant. pub const fn open(self) -> Delimiter { match self { Self::Tupled => Delimiter::OpenParen, Self::Braced => Delimiter::OpenBrace, } } /// Gets the group delimiter that follows the fields of a struct or variant. pub const fn close(self) -> Delimiter { match self { Self::Tupled => Delimiter::CloseParen, Self::Braced => Delimiter::CloseBrace, } } } //////////////////////////////////////////////////////////////////////////////// /// An [alternate-flag-aware] comma separator for use between fields or elements. /// /// When the alternate flag is enabled, this puts each field/element on its own line. /// /// # Example /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg}, /// ArrayString, /// flatten_panicvals, /// }; /// /// // Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; /// open: fmt::OpenBracket, /// 100u8, fmt::COMMA_SEP, /// false, fmt::COMMA_SEP, /// [0u16; 0], fmt::COMMA_SEP, /// // fmt::COMMA_TERM always goes after the last field /// debug: "really", fmt::COMMA_TERM, /// close: fmt::CloseBracket, /// ), /// "[100, false, [], \"really\"]" /// ); /// /// /// // Alternate-Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; /// open: fmt::OpenBracket, /// 100u8, fmt::COMMA_SEP, /// false, fmt::COMMA_SEP, /// [0u16; 0], fmt::COMMA_SEP, /// // fmt::COMMA_TERM always goes after the last field /// debug: "really", fmt::COMMA_TERM, /// close: fmt::CloseBracket, /// ), /// concat!( /// "[\n", /// " 100,\n", /// " false,\n", /// " [],\n", /// " \"really\",\n", /// "]", /// ) /// ); /// /// ``` /// /// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub const COMMA_SEP: Separator<'_> = Separator::new(",", IsLast::No); /// An [alternate-flag-aware] comma for use after the last field or element. /// /// For an example, you can look at [the one for `COMMA_SEP`](COMMA_SEP#example) /// /// When the alternate flag is enabled, this puts each field/element on its own line. /// /// [alternate-flag-aware]: crate::FmtArg#structfield.is_alternate #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub const COMMA_TERM: Separator<'_> = Separator::new(",", IsLast::Yes); /// For telling [`Separator`] whether it comes after the last field or not. #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub enum IsLast { /// Yes, /// No, } /// For [alternate flag]-aware separation of fields, collection elements, etc. /// /// # Example /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg, IsLast, Separator}, /// ArrayString, /// flatten_panicvals, /// }; /// /// const SEMICOLON_SEP: Separator = Separator::new(";", IsLast::No); /// const SEMICOLON_TERM: Separator = Separator::new(";", IsLast::Yes); /// /// // Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; /// open: fmt::OpenBrace, /// debug: "foo", SEMICOLON_SEP, /// [3u8, 5, 8], SEMICOLON_SEP, /// false, SEMICOLON_TERM, /// close: fmt::CloseBrace, /// ), /// // the space before the brace is because Delimiter is intended for /// // formatting structs and enum variants. /// " { \"foo\"; [3, 5, 8]; false }" /// ); /// /// /// // Alternate-Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; /// open: fmt::OpenBrace, /// debug: "foo", SEMICOLON_SEP, /// debug: [3u8, 5, 8], SEMICOLON_SEP, /// false, SEMICOLON_TERM, /// close: fmt::CloseBrace, /// ), /// concat!( /// " {\n", /// " \"foo\";\n", /// " [3, 5, 8];\n", /// " false;\n", /// "}", /// ) /// ); /// ``` /// /// [alternate flag]: crate::FmtArg#structfield.is_alternate #[derive(Copy, Clone)] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub struct Separator<'a>(&'a str, IsLast); impl<'a> Separator<'a> { /// Constructs a `Separator` from a custom separator. /// /// # Panics /// /// Panics if `string` is longer than 12 bytes. /// pub const fn new(string: &'a str, is_last_field: IsLast) -> Self { if string.len() > 12 { crate::concat_panic(&[&[ PanicVal::write_str("expected a string shorter than 12 bytes,"), PanicVal::write_str("actual length: "), PanicVal::from_usize(string.len(), FmtArg::DISPLAY), PanicVal::write_str(", string: "), PanicVal::from_str(string, FmtArg::DEBUG), ]]) } Separator(string, is_last_field) } /// Converts this `Separator` into an array of one `PanicVal`. /// Otherwise does the same as [`to_panicval`](Self::to_panicval) pub const fn to_panicvals(self, f: FmtArg) -> [PanicVal<'static>; 1] { [PanicVal::from_element_separator(self.0, self.1, f)] } /// Converts this `Separator` into a `PanicVal`. /// /// When the [alternate flag] is enabled, this and the `to_panicvals` method output: /// - the separator /// - a newline /// - [fmtarg.indentation](crate::FmtArg#structfield.indentation) amount of spaces /// if constructed with [`IsLast::No`] /// /// When the [alternate flag] is disabled, /// these methods output the separator and a single space /// if constructed with [`IsLast::No`], /// otherwise output nothing. /// /// [alternate flag]: crate::FmtArg#structfield.is_alternate pub const fn to_panicval(self, f: FmtArg) -> PanicVal<'static> { PanicVal::from_element_separator(self.0, self.1, f) } } impl PanicFmt for Separator<'_> { type This = Self; type Kind = IsCustomType; const PV_COUNT: usize = 1; } const_panic-0.2.13/src/fmt.rs000064400000000000000000000267601046102023000141450ustar 00000000000000//! Formatting-related items //! //! Panic formatting for custom types can be done in these ways //! (in increasing order of verbosity): //! - Using the [`PanicFmt` derive] macro //! (requires the opt-in `"derive"` feature) //! - Using the [`impl_panicfmt`] macro //! (requires the default-enabled `"non_basic"` feature) //! - Using the [`flatten_panicvals`] macro //! (requires the default-enabled `"non_basic"` feature) //! - Manually implementing the [`PanicFmt`] trait as described in its docs. //! //! [`PanicFmt` derive]: derive@crate::PanicFmt //! [`PanicFmt`]: trait@crate::fmt::PanicFmt //! [`impl_panicfmt`]: crate::impl_panicfmt //! [`flatten_panicvals`]: crate::flatten_panicvals #[cfg(feature = "non_basic")] mod non_basic_fmt; #[cfg(feature = "non_basic")] mod fmt_compressed; pub mod char_formatting; #[cfg(feature = "non_basic")] pub use self::{fmt_compressed::PackedFmtArg, non_basic_fmt::*}; use crate::wrapper::StdWrapper; use core::marker::PhantomData; /// Trait for types that can be formatted by const panics. /// /// # Implementor /// /// Implementors are expected to also define this inherent method to format the type: /// ```rust /// # use const_panic::fmt::{FmtArg, IsCustomType, PanicFmt}; /// # use const_panic::PanicVal; /// # struct Foo; /// # impl Foo { /// const fn to_panicvals<'a>(&'a self, f: FmtArg) -> [PanicVal<'a>; ::PV_COUNT] /// # { loop{} } /// # } /// # impl PanicFmt for Foo { /// # type This = Self; /// # type Kind = IsCustomType; /// # const PV_COUNT: usize = 1; /// # } /// ``` /// The returned [`PanicVal`](crate::PanicVal) can also be `PanicVal<'static>`. /// /// # Implementation examples /// /// This trait can be implemented in these ways (in increasing order of verbosity): /// - Using the [`PanicFmt` derive] macro /// (requires the opt-in `"derive"` feature) /// - Using the [`impl_panicfmt`](impl_panicfmt#examples) macro /// (requires the default-enabled `"non_basic"` feature) /// - Using the [`flatten_panicvals`](flatten_panicvals#examples) macro /// (requires the default-enabled `"non_basic"` feature) /// - Using no macros at all /// /// ### Macro-less impl /// /// Implementing this trait for a simple enum without using macros. /// #[cfg_attr(feature = "non_basic", doc = "```rust")] #[cfg_attr(not(feature = "non_basic"), doc = "```ignore")] /// use const_panic::{ArrayString, FmtArg, PanicFmt, PanicVal}; /// /// // `ArrayString` requires the "non_basic" crate feature (enabled by default), /// // everything else in this example works with no enabled crate features. /// assert_eq!( /// ArrayString::<99>::concat_panicvals(&[ /// &Foo::Bar.to_panicvals(FmtArg::DEBUG), /// &[PanicVal::write_str(",")], /// &Foo::Baz.to_panicvals(FmtArg::DEBUG), /// &[PanicVal::write_str(",")], /// &Foo::Qux.to_panicvals(FmtArg::DEBUG), /// ]).unwrap(), /// "Bar,Baz,Qux", /// ); /// /// /// enum Foo { /// Bar, /// Baz, /// Qux, /// } /// /// impl PanicFmt for Foo { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// const PV_COUNT: usize = 1; /// } /// /// impl Foo { /// pub const fn to_panicvals(self, _: FmtArg) -> [PanicVal<'static>; Foo::PV_COUNT] { /// match self { /// Self::Bar => [PanicVal::write_str("Bar")], /// Self::Baz => [PanicVal::write_str("Baz")], /// Self::Qux => [PanicVal::write_str("Qux")], /// } /// } /// } /// /// ``` /// [`PanicFmt` derive]: derive@crate::PanicFmt /// [`PanicFmt`]: trait@crate::fmt::PanicFmt /// [`impl_panicfmt`]: crate::impl_panicfmt /// [`flatten_panicvals`]: crate::flatten_panicvals pub trait PanicFmt { /// The type after dereferencing all references. /// /// User-defined types should generally set this to `Self`. type This: ?Sized; /// Whether this is a user-defined type or standard library type. /// /// User-defined types should generally set this to [`IsCustomType`]. type Kind; /// The length of the array returned in `Self::to_panicvals` /// (an inherent method that formats the type for panic messages). const PV_COUNT: usize; /// A marker type that proves that `Self` implements `PanicFmt`. /// /// Used by const_panic macros to coerce both standard library and /// user-defined types into some type that has a `to_panicvals` method. const PROOF: IsPanicFmt = IsPanicFmt::NEW; } impl<'a, T: PanicFmt + ?Sized> PanicFmt for &'a T { type This = T::This; type Kind = T::Kind; const PV_COUNT: usize = T::PV_COUNT; } /// Marker type used as the [`PanicFmt::Kind`] associated type for std types. pub struct IsStdType; /// Marker type used as the [`PanicFmt::Kind`] for user-defined types. pub struct IsCustomType; /// A marker type that proves that `S` implements /// [`PanicFmt`](PanicFmt). /// /// Used by const_panic macros to coerce both standard library and /// user-defined types into some type that has a `to_panicvals` method. /// pub struct IsPanicFmt { self_: PhantomData S>, this: PhantomData T>, kind: PhantomData K>, _priv: (), } impl IsPanicFmt { /// Constucts an `IsPanicFmt` pub const NEW: Self = Self { self_: PhantomData, this: PhantomData, kind: PhantomData, _priv: (), }; } impl IsPanicFmt { /// Infers the `S` type parameter with the argument. /// /// Because the only ways to construct `IsPanicFmt` /// use `IsPanicFmt`, /// the other type parameters are inferred along with `S`. pub const fn infer(self, _: &S) -> Self { self } } impl IsPanicFmt { /// For coercing `&T` to `StdWrapper<&T>`. pub const fn coerce(self, x: &T) -> StdWrapper<&T> { StdWrapper(x) } } impl IsPanicFmt { /// For coercing `&T` (with any amount of stacked references) to `&T`. pub const fn coerce(self, x: &T) -> &T { x } } impl Copy for IsPanicFmt {} impl Clone for IsPanicFmt { fn clone(&self) -> Self { *self } } /// Carries all of the configuration for formatting functions. /// /// # Example /// #[cfg_attr(feature = "non_basic", doc = "```rust")] #[cfg_attr(not(feature = "non_basic"), doc = "```ignore")] /// use const_panic::{ArrayString, FmtArg, StdWrapper}; /// /// // `StdWrapper` wraps references to std types to provide their `to_panicvals` methods /// const ARRAY: &[&str] = &["3", "foo\nbar", "\0qux"]; /// /// // Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; ARRAY), /// r#"["3", "foo\nbar", "\x00qux"]"# /// ); /// /// // Alternate-Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; ARRAY), /// concat!( /// "[\n", /// " \"3\",\n", /// " \"foo\\nbar\",\n", /// " \"\\x00qux\",\n", /// "]", /// ) /// ); /// /// // Display formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DISPLAY; ARRAY), /// "[3, foo\nbar, \x00qux]" /// ); /// /// // Alternate-Display formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DISPLAY; ARRAY), /// concat!( /// "[\n", /// " 3,\n", /// " foo\n", /// "bar,\n", /// " \x00qux,\n", /// "]", /// ) /// ); /// /// ``` #[derive(Debug, Copy, Clone, PartialEq)] pub struct FmtArg { /// How much indentation is needed for a field/array element. /// /// Indentation is used by [`fmt::Delimiter`](crate::fmt::Delimiter) /// and by [`fmt::Separator`](crate::fmt::Separator), /// when the [`is_alternate` field](#structfield.is_alternate) flag is enabled. pub indentation: u8, /// Whether alternate formatting is being used. pub is_alternate: bool, /// Whether this is intended to be `Display` or `Debug` formatted. pub fmt_kind: FmtKind, /// What integers are formatted as: decimal, hexadecimal, or binary. pub number_fmt: NumberFmt, } impl FmtArg { /// A `FmtArg` with no indentation and `Display` formatting. pub const DISPLAY: Self = Self { indentation: 0, fmt_kind: FmtKind::Display, is_alternate: false, number_fmt: NumberFmt::Decimal, }; /// A `FmtArg` with alternate `Display` formatting, starting with no indentation. pub const ALT_DISPLAY: Self = Self::DISPLAY.set_alternate(true); /// A `FmtArg` with `Debug` formatting and no indentation. pub const DEBUG: Self = Self::DISPLAY.set_debug(); /// A `FmtArg` with alternate `Debug` formatting, starting with no indentation. pub const ALT_DEBUG: Self = Self::DEBUG.set_alternate(true); /// A `FmtArg` with `Debug` and `Binary` formatting and no indentation. pub const BIN: Self = Self::DISPLAY.set_bin(); /// A `FmtArg` with alternate `Debug` and `Binary` formatting, /// starting with no indentation. pub const ALT_BIN: Self = Self::BIN.set_alternate(true); /// A `FmtArg` with `Debug` and `Hexadecimal` formatting and no indentation. pub const HEX: Self = Self::DISPLAY.set_hex(); /// A `FmtArg` with alternate `Debug` and `Hexadecimal` formatting, /// starting with no indentation. pub const ALT_HEX: Self = Self::HEX.set_alternate(true); /// Sets whether alternate formatting is enabled pub const fn set_alternate(mut self, is_alternate: bool) -> Self { self.is_alternate = is_alternate; self } /// Changes the formatting to `Display`. pub const fn set_display(mut self) -> Self { self.fmt_kind = FmtKind::Display; self } /// Changes the formatting to `Debug`. pub const fn set_debug(mut self) -> Self { self.fmt_kind = FmtKind::Debug; self } /// Changes the formatting to `Debug`, and number formatting to `Hexadecimal`. pub const fn set_hex(mut self) -> Self { self.fmt_kind = FmtKind::Debug; self.number_fmt = NumberFmt::Hexadecimal; self } /// Changes the formatting to `Debug`, and number formatting to `Binary`. pub const fn set_bin(mut self) -> Self { self.fmt_kind = FmtKind::Debug; self.number_fmt = NumberFmt::Binary; self } } #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] impl FmtArg { /// Increments the indentation by [`INDENTATION_STEP`] spaces. pub const fn indent(mut self) -> Self { self.indentation += INDENTATION_STEP; self } /// Decrement the indentation by [`INDENTATION_STEP`] spaces. pub const fn unindent(mut self) -> Self { self.indentation = self.indentation.saturating_sub(INDENTATION_STEP); self } } //////////////////////////////////////////////////////////////////////////////// /// What kind of formatting to do, either `Display` or `Debug`. #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq)] pub enum FmtKind { /// `Debug` formatting Debug = 0, /// `Display` formatting Display = 1, } //////////////////////////////////////////////////////////////////////////////// /// What integers are formatted as. #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq)] pub enum NumberFmt { /// Formatted as decimal. Decimal = 0, /// Formatted as binary, eg: `101`, `0b110`. Binary = 1, /// Formatted as hexadecimal, eg: `FAD`, `0xDE`. Hexadecimal = 2, } const_panic-0.2.13/src/fmt_impls/basic_fmt_impls.rs000064400000000000000000000134411046102023000204740ustar 00000000000000use crate::{ panic_val::{IntVal, PanicVal, PanicVariant, StrFmt}, utils::Packed, FmtArg, PanicFmt, StdWrapper, }; macro_rules! primitive_static_panicfmt { ( fn[$($impl:tt)*](&$self:ident: $ty:ty, $f:ident) { $($content:tt)* } ) => { impl<$($impl)*> crate::PanicFmt for $ty { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } impl<$($impl)*> crate::StdWrapper<&$ty> { #[doc = concat!( "Converts this `", stringify!($ty), "` to a single-element `PanicVal` array." )] pub const fn to_panicvals($self, $f: crate::FmtArg) -> [PanicVal<'static>; 1] { [{ $($content)* }] } #[doc = concat!( "Converts this `", stringify!($ty), "` to a `PanicVal`." )] pub const fn to_panicval($self, $f: crate::FmtArg) -> PanicVal<'static> { $($content)* } } } } pub(crate) use primitive_static_panicfmt; macro_rules! impl_panicfmt_panicval_array { ( PV_COUNT = $pv_len:expr; fn[$($impl:tt)*](&$self:ident: $ty:ty) -> $ret:ty { $($content:tt)* } ) => ( impl<$($impl)*> PanicFmt for $ty { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = $pv_len; } impl<'s, $($impl)*> StdWrapper<&'s $ty> { /// pub const fn to_panicvals($self: Self, _: FmtArg) -> $ret { $($content)* } } ) } macro_rules! impl_panicfmt_panicarg { ( fn $panic_arg_ctor:ident[$($impl:tt)*]( $this:ident: $ty:ty, $f:ident ) -> PanicVal<$pa_lt:lifetime> $panic_args:block )=>{ impl PanicVal<'_> { #[doc = concat!("Constructs a `PanicVal` from a `", stringify!($ty), "` .")] pub const fn $panic_arg_ctor<$($impl)*>($this: $ty, $f: FmtArg) -> PanicVal<$pa_lt> $panic_args } impl<$($impl)*> PanicFmt for $ty { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } impl<'s, $($impl)*> StdWrapper<&'s $ty> { #[doc = concat!( "Formats this `", stringify!($ty), "` into a single-`PanicVal` array", )] pub const fn to_panicvals(self: Self, f: FmtArg) -> [PanicVal<$pa_lt>;1] { [PanicVal::$panic_arg_ctor(*self.0, f)] } #[doc = concat!("Formats this `", stringify!($ty), "` into a `PanicVal`")] pub const fn to_panicval(self: Self, f: FmtArg) -> PanicVal<$pa_lt> { PanicVal::$panic_arg_ctor(*self.0, f) } } } } macro_rules! impl_panicfmt_int { ($panic_arg_ctor:ident, $intarg_contructor:ident, $ty:ty) => { impl PanicVal<'_> { /// Constructs this `PanicVal` from an integer. pub const fn $panic_arg_ctor(this: $ty, f: FmtArg) -> PanicVal<'static> { const BITS: u8 = core::mem::size_of::<$ty>() as u8 * 8; IntVal::$intarg_contructor(this as _, BITS, f) } } primitive_static_panicfmt! { fn[](&self: $ty, f) { PanicVal::$panic_arg_ctor(*self.0, f) } } }; } impl_panicfmt_int! {from_u8, from_u128, u8} impl_panicfmt_int! {from_u16, from_u128, u16} impl_panicfmt_int! {from_u32, from_u128, u32} impl_panicfmt_int! {from_u64, from_u128, u64} impl_panicfmt_int! {from_u128, from_u128, u128} impl_panicfmt_int! {from_usize, from_u128, usize} impl_panicfmt_int! {from_i8, from_i128, i8} impl_panicfmt_int! {from_i16, from_i128, i16} impl_panicfmt_int! {from_i32, from_i128, i32} impl_panicfmt_int! {from_i64, from_i128, i64} impl_panicfmt_int! {from_i128, from_i128, i128} impl_panicfmt_int! {from_isize, from_i128, isize} impl_panicfmt_panicarg! { fn from_bool[](this: bool, _f) -> PanicVal<'static> { PanicVal::write_str(if this { "true" } else { "false" }) } } impl<'a> PanicVal<'a> { /// Constructs a `PanicVal` from a `&str` pub const fn from_str(this: &'a str, f: FmtArg) -> PanicVal<'a> { PanicVal::__new(PanicVariant::Str(StrFmt::new(f), Packed(this))) } } impl PanicFmt for str { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } impl<'a> StdWrapper<&'a str> { /// Formats this `&str` into a single-`PanicVal` array pub const fn to_panicvals(self: Self, f: FmtArg) -> [PanicVal<'a>; 1] { [PanicVal::from_str(self.0, f)] } /// Formats this `&str` into a `PanicVal` pub const fn to_panicval(self: Self, f: FmtArg) -> PanicVal<'a> { PanicVal::from_str(self.0, f) } } impl_panicfmt_panicval_array! { PV_COUNT = N; fn['a, const N: usize](&self: [PanicVal<'a>; N]) -> &'s [PanicVal<'a>; N] { self.0 } } impl_panicfmt_panicval_array! { PV_COUNT = usize::MAX; fn['a](&self: [PanicVal<'a>]) -> &'s [PanicVal<'a>] { self.0 } } impl<'a, 'b> StdWrapper<&'a &'b [PanicVal<'b>]> { /// Coerces a `&&[PanicVal<'_>]` into a `&[PanicVal<'_>]` pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] { *self.0 } } impl<'a, 'b, const N: usize> StdWrapper<&'a &'b [PanicVal<'b>; N]> { /// Coerces a `&&[PanicVal<'_>; N]` into a `&[PanicVal<'_>]` pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] { *self.0 } } impl<'b, const N: usize> StdWrapper<&'b [PanicVal<'b>; N]> { /// Coerces a `&[PanicVal<'_>; N]` into a `&[PanicVal<'_>]` pub const fn deref_panic_vals(self) -> &'b [PanicVal<'b>] { self.0 } } const_panic-0.2.13/src/fmt_impls/fmt_range.rs000064400000000000000000000047111046102023000173030ustar 00000000000000use crate::{FmtArg, PanicFmt, PanicVal, StdWrapper}; use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; macro_rules! impl_range_panicfmt_one { ( fn(&$self:ident: $ty:ty, $f:ident) -> $pv_count:literal { $($content:tt)* } ) => { impl PanicFmt for $ty { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = $pv_count; } impl crate::StdWrapper<&$ty> { #[doc = concat!( "Converts this `", stringify!($ty), "` to a single-element `PanicVal` array." )] pub const fn to_panicvals($self, $f: FmtArg) -> [PanicVal<'static>; $pv_count] { $($content)* } } } } macro_rules! impl_range_panicfmt { ($elem_ty:ty) => { impl_range_panicfmt_one! { fn(&self: Range<$elem_ty>, f) -> 3 { [ StdWrapper(&self.0.start).to_panicval(f), PanicVal::write_str(".."), StdWrapper(&self.0.end).to_panicval(f), ] } } impl_range_panicfmt_one! { fn(&self: RangeFrom<$elem_ty>, f) -> 2 { [ StdWrapper(&self.0.start).to_panicval(f), PanicVal::write_str(".."), ] } } impl_range_panicfmt_one! { fn(&self: RangeTo<$elem_ty>, f) -> 2 { [ PanicVal::write_str(".."), StdWrapper(&self.0.end).to_panicval(f), ] } } impl_range_panicfmt_one! { fn(&self: RangeToInclusive<$elem_ty>, f) -> 2 { [ PanicVal::write_str("..="), StdWrapper(&self.0.end).to_panicval(f), ] } } impl_range_panicfmt_one! { fn(&self: RangeInclusive<$elem_ty>, f) -> 3 { [ StdWrapper(self.0.start()).to_panicval(f), PanicVal::write_str("..="), StdWrapper(self.0.end()).to_panicval(f), ] } } }; } impl_range_panicfmt! {usize} //////////////////////////////////////////////////////////////////////////////// impl_range_panicfmt_one! { fn(&self: RangeFull, _f) -> 1 { [PanicVal::write_str("..")] } } const_panic-0.2.13/src/fmt_impls/nonzero_impls.rs000064400000000000000000000016261046102023000202410ustar 00000000000000use crate::{FmtArg, PanicVal}; use core::num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }; macro_rules! nonzero_impls { ($(($int_ctor:ident, $ty:ty))*) => ( $( primitive_static_panicfmt!{ fn[](&self: $ty, fmtarg) { PanicVal::$int_ctor(self.0.get(), fmtarg) } } )* impl_for_option!{ $((for[], 'static, $ty, $ty))* } ) } nonzero_impls! { (from_u8, NonZeroU8) (from_i8, NonZeroI8) (from_u16, NonZeroU16) (from_i16, NonZeroI16) (from_u32, NonZeroU32) (from_i32, NonZeroI32) (from_u64, NonZeroU64) (from_i64, NonZeroI64) (from_u128, NonZeroU128) (from_i128, NonZeroI128) (from_usize, NonZeroUsize) (from_isize, NonZeroIsize) } const_panic-0.2.13/src/fmt_impls/option_fmt_impls.rs000064400000000000000000000044231046102023000207230ustar 00000000000000use crate::{FmtArg, PanicFmt, PanicVal}; /// Note: there is only `to_panicvals` methods for `Option`s of standard library types /// for now. /// impl PanicFmt for Option where T: PanicFmt, { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 4 + T::PV_COUNT; } macro_rules! impl_for_option { ( $((for[$($generics:tt)*],$lt:lifetime, $ty:ty, $unref:ty))* ) => ( $( impl<'s, $($generics)*> crate::StdWrapper<&'s Option<$ty>> { #[doc = concat!( "Converts this `Option<", stringify!($ty), ">` to a `PanicVal` array." )] pub const fn to_panicvals( self: Self, mut fmtarg: FmtArg, ) -> [PanicVal<$lt>; 5] { use crate::{PanicVal, StdWrapper, __::EPV, fmt}; match self.0 { Some(x) => [ PanicVal::write_str("Some"), {fmtarg = fmtarg.indent(); fmt::OpenParen.to_panicval(fmtarg)}, StdWrapper::<&$unref>(x).to_panicval(fmtarg), fmt::COMMA_TERM.to_panicval(fmtarg), {fmtarg = fmtarg.unindent(); fmt::CloseParen.to_panicval(fmtarg)}, ], None => [PanicVal::write_str("None"), EPV, EPV, EPV, EPV], } } } )* ) } macro_rules! impl_for_option_outer { ( $(($lt:lifetime, $ty:ty, $unref:ty))* ) => ( impl_for_option!{ $( (for[], $lt, $ty, $unref) (for[const N: usize], 's, [$ty; N], [$ty; N]) (for[const N: usize], 's, &'s [$ty; N], [$ty; N]) (for[], 's, &'s [$ty], [$ty]) )* } ) } impl_for_option_outer! { ('static, bool, bool) ('static, u8, u8) ('static, u16, u16) ('static, u32, u32) ('static, u64, u64) ('static, u128, u128) ('static, i8, i8) ('static, i16, i16) ('static, i32, i32) ('static, i64, i64) ('static, i128, i128) ('static, isize, isize) ('static, usize, usize) ('s, &'s str, str) } const_panic-0.2.13/src/fmt_impls/other_impls.rs000064400000000000000000000034721046102023000176710ustar 00000000000000use crate::{FmtArg, PanicVal}; use core::{ marker::{PhantomData, PhantomPinned}, ptr::NonNull, }; use core as std; macro_rules! ptr_impls { ($ty:ty) => { primitive_static_panicfmt! { fn[T: ?Sized](&self: $ty, _f) { PanicVal::write_str("") } } }; } ptr_impls! {*const T} ptr_impls! {*mut T} ptr_impls! {NonNull} impl_for_option! { (for[T], 'static, NonNull, NonNull) } primitive_static_panicfmt! { fn[T: ?Sized](&self: PhantomData, _f) { PanicVal::write_str("PhantomData") } } primitive_static_panicfmt! { fn[](&self: PhantomPinned, _f) { PanicVal::write_str("PhantomPinned") } } primitive_static_panicfmt! { fn[](&self: (), _f) { PanicVal::write_str("()") } } impl_for_option! { (for[], 'static, core::cmp::Ordering, core::cmp::Ordering) } primitive_static_panicfmt! { fn[](&self: std::cmp::Ordering, _f) { let v = match self.0 { std::cmp::Ordering::Less => "Less", std::cmp::Ordering::Equal => "Equal", std::cmp::Ordering::Greater => "Greater", }; PanicVal::write_str(v) } } primitive_static_panicfmt! { fn[](&self: std::sync::atomic::Ordering, _f) { use std::sync::atomic::Ordering; let v = match self.0 { Ordering::Relaxed => "Relaxed", Ordering::Release => "Release", Ordering::Acquire => "Acquire", Ordering::AcqRel => "AcqRel", Ordering::SeqCst => "SeqCst", _ => "", }; PanicVal::write_str(v) } } #[allow(unreachable_code)] const _: () = { primitive_static_panicfmt! { fn[](&self: std::convert::Infallible, _f) { match *self.0 {} } } }; const_panic-0.2.13/src/fmt_impls/rust_1_64_fmt_impls.rs000064400000000000000000000043571046102023000211470ustar 00000000000000use crate::{ fmt::{FmtArg, FmtKind, PanicFmt}, PanicVal, StdWrapper, }; use core::str::Utf8Error; #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))] impl PanicFmt for Utf8Error { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 5; } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))] impl StdWrapper<&Utf8Error> { /// Formats a `Utf8Error` (supports both Debug and Display formatting). pub const fn to_panicvals(self, fmtarg: FmtArg) -> [PanicVal<'static>; Utf8Error::PV_COUNT] { let this = *self.0; match fmtarg.fmt_kind { FmtKind::Display => { let [pv0, pv1, pv2] = match this.error_len() { Some(x) => [ PanicVal::write_str("invalid utf-8 sequence of "), PanicVal::from_usize(x, fmtarg), PanicVal::write_str(" bytes "), ], None => [ PanicVal::write_str("incomplete utf-8 byte sequence "), PanicVal::EMPTY, PanicVal::EMPTY, ], }; [ pv0, pv1, pv2, PanicVal::write_str("from index "), PanicVal::from_usize(this.valid_up_to(), fmtarg), ] } FmtKind::Debug => { let [pv0, pv1, pv2] = match this.error_len() { Some(x) => [ PanicVal::write_str(", error_len: Some("), PanicVal::from_usize(x, fmtarg), PanicVal::write_str(") }"), ], None => [ PanicVal::write_str(", error_len: None }"), PanicVal::EMPTY, PanicVal::EMPTY, ], }; [ PanicVal::write_str("Utf8Error { valid_up_to: "), PanicVal::from_usize(this.valid_up_to(), fmtarg), pv0, pv1, pv2, ] } } } } const_panic-0.2.13/src/fmt_impls/rust_1_82_fmt_impls.rs000064400000000000000000000057571046102023000211540ustar 00000000000000use crate::{ fmt::{FmtArg, FmtKind, PanicFmt}, PanicVal, StdWrapper, }; use core::num::{IntErrorKind, ParseIntError}; #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))] impl PanicFmt for ParseIntError { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))] impl StdWrapper<&ParseIntError> { /// Formats a `ParseIntError` (supports both Debug and Display formatting). pub const fn to_panicvals( self, fmtarg: FmtArg, ) -> [PanicVal<'static>; ParseIntError::PV_COUNT] { [self.to_panicval(fmtarg)] } /// Formats a `ParseIntError` (supports both Debug and Display formatting). pub const fn to_panicval(self, fmtarg: FmtArg) -> PanicVal<'static> { macro_rules! debug_fmt { ($variant:ident) => { concat!("ParseIntError { kind: ", stringify!($variant), " }") }; } let this = self.0; match fmtarg.fmt_kind { FmtKind::Display => PanicVal::write_str(match this.kind() { IntErrorKind::Empty => "cannot parse integer from empty string", IntErrorKind::InvalidDigit => "invalid digit found in string", IntErrorKind::PosOverflow => "number too large to fit in target type", IntErrorKind::NegOverflow => "number too small to fit in target type", IntErrorKind::Zero => "number would be zero for non-zero type", _ => "", }), FmtKind::Debug => PanicVal::write_str(match this.kind() { IntErrorKind::Empty => debug_fmt!(Empty), IntErrorKind::InvalidDigit => debug_fmt!(InvalidDigit), IntErrorKind::PosOverflow => debug_fmt!(PosOverflow), IntErrorKind::NegOverflow => debug_fmt!(NegOverflow), IntErrorKind::Zero => debug_fmt!(Zero), _ => "", }), } } } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))] impl PanicFmt for IntErrorKind { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_82")))] impl StdWrapper<&IntErrorKind> { /// Formats a `IntErrorKind` (supports only Debug formatting). pub const fn to_panicvals(self, fmtarg: FmtArg) -> [PanicVal<'static>; IntErrorKind::PV_COUNT] { [self.to_panicval(fmtarg)] } /// Formats a `IntErrorKind` (supports only Debug formatting). pub const fn to_panicval(self, _: FmtArg) -> PanicVal<'static> { PanicVal::write_str(match *self.0 { IntErrorKind::Empty => "Empty", IntErrorKind::InvalidDigit => "InvalidDigit", IntErrorKind::PosOverflow => "PosOverflow", IntErrorKind::NegOverflow => "NegOverflow", IntErrorKind::Zero => "Zero", _ => "", }) } } const_panic-0.2.13/src/fmt_impls/rust_1_88_nonbasic_fmt_impls.rs000064400000000000000000000062331046102023000230240ustar 00000000000000use crate::{ fmt::{self as cfmt, ComputePvCount, FmtArg, FmtKind, PanicFmt}, PanicVal, StdWrapper, }; use core::ffi::{FromBytesUntilNulError, FromBytesWithNulError}; #[cfg_attr( feature = "docsrs", doc(cfg(all(feature = "non_basic", feature = "rust_1_88"))) )] impl PanicFmt for FromBytesWithNulError { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = ComputePvCount { field_amount: 1, summed_pv_count: ::PV_COUNT, delimiter: cfmt::TypeDelim::Braced, } .call(); } #[cfg_attr( feature = "docsrs", doc(cfg(all(feature = "non_basic", feature = "rust_1_88"))) )] impl StdWrapper<&FromBytesWithNulError> { /// Formats a `FromBytesWithNulError`. pub const fn to_panicvals( self, fmtarg: FmtArg, ) -> [PanicVal<'static>; FromBytesWithNulError::PV_COUNT] { match (fmtarg.fmt_kind, *self.0) { (FmtKind::Display, FromBytesWithNulError::InteriorNul { position }) => { crate::utils::flatten_panicvals(&[&[ PanicVal::write_str("data provided contains an interior nul byte at byte pos "), PanicVal::from_usize(position, fmtarg), ]]) } (FmtKind::Display, FromBytesWithNulError::NotNulTerminated) => { crate::utils::flatten_panicvals(&[&[PanicVal::write_str( "data provided is not nul terminated", )]]) } (FmtKind::Debug, FromBytesWithNulError::InteriorNul { position }) => { flatten_panicvals! {fmtarg; "InteriorNul", open: cfmt::OpenBrace, "position: ", usize => position, cfmt::COMMA_TERM, close: cfmt::CloseBrace, } } (FmtKind::Debug, FromBytesWithNulError::NotNulTerminated) => { crate::utils::flatten_panicvals(&[&[PanicVal::write_str("NotNulTerminated")]]) } } } } #[cfg_attr( feature = "docsrs", doc(cfg(all(feature = "non_basic", feature = "rust_1_88"))) )] impl PanicFmt for FromBytesUntilNulError { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = ComputePvCount { field_amount: 1, summed_pv_count: <()>::PV_COUNT, delimiter: cfmt::TypeDelim::Tupled, } .call(); } #[cfg_attr( feature = "docsrs", doc(cfg(all(feature = "non_basic", feature = "rust_1_88"))) )] impl StdWrapper<&FromBytesUntilNulError> { /// Formats a `FromBytesUntilNulError`. pub const fn to_panicvals( self, fmtarg: FmtArg, ) -> [PanicVal<'static>; FromBytesUntilNulError::PV_COUNT] { match fmtarg.fmt_kind { FmtKind::Display => crate::utils::flatten_panicvals(&[&[PanicVal::write_str( "data provided does not contain a nul", )]]), FmtKind::Debug => flatten_panicvals! {fmtarg; "FromBytesUntilNulError", open: cfmt::OpenParen, () => (), cfmt::COMMA_TERM, close: cfmt::CloseParen, }, } } } const_panic-0.2.13/src/int_formatting.rs000064400000000000000000000065571046102023000164050ustar 00000000000000use crate::{ fmt::{FmtArg, NumberFmt}, utils::{Sign, TailShortString}, }; pub(crate) const fn fmt_decimal(sign: Sign, mut n: u128) -> TailShortString { let mut start = N; let mut buffer = [0u8; N]; loop { start -= 1; let digit = (n % 10) as u8; buffer[start] = b'0' + digit; n /= 10; if n == 0 { break; } } if let Sign::Negative = sign { start -= 1; buffer[start] = b'-'; } // safety: buffer is only ever written ascii, so its automatically valid utf8. unsafe { TailShortString::new(start as u8, buffer) } } pub(crate) const fn fmt_binary( mut n: u128, is_alternate: bool, ) -> TailShortString { let mut start = N; let mut buffer = [0u8; N]; loop { start -= 1; let digit = (n & 1) as u8; buffer[start] = b'0' + digit; n >>= 1; if n == 0 { break; } } if is_alternate { start -= 1; buffer[start] = b'b'; start -= 1; buffer[start] = b'0'; } // safety: buffer is only ever written ascii, so its automatically valid utf8. unsafe { TailShortString::new(start as u8, buffer) } } pub(crate) const fn fmt_hexadecimal( mut n: u128, is_alternate: bool, ) -> TailShortString { let mut start = N; let mut buffer = [0u8; N]; loop { start -= 1; let digit = (n & 0xF) as u8; buffer[start] = match digit { 0..=9 => b'0' + digit, _ => b'A' - 10 + digit, }; n >>= 4; if n == 0 { break; } } if is_alternate { start -= 1; buffer[start] = b'x'; start -= 1; buffer[start] = b'0'; } // safety: buffer is only ever written ascii, so its automatically valid utf8. unsafe { TailShortString::new(start as u8, buffer) } } pub(crate) const fn compute_len(sign: Sign, int: u128, bits: u8, fmt: FmtArg) -> u8 { match fmt.number_fmt { NumberFmt::Decimal => compute_decimal_len(sign, int), NumberFmt::Hexadecimal => { let with_0x = (fmt.is_alternate as u8) * 2; let i = match sign { Sign::Negative => bits, Sign::Positive => (128 - int.leading_zeros()) as u8, }; let tmp = if i == 0 { 1 } else { i / 4 + (i % 4 != 0) as u8 }; tmp + with_0x } NumberFmt::Binary => { let with_0b = (fmt.is_alternate as u8) * 2; let i = match sign { Sign::Negative => bits, Sign::Positive => (128 - int.leading_zeros()) as u8, }; (if i == 0 { 1 } else { i }) + with_0b } } } const fn compute_decimal_len(sign: Sign, mut n: u128) -> u8 { let mut len = matches!(sign, Sign::Negative) as u8 + 1; if n >= 1_0000_0000_0000_0000 { n /= 1_0000_0000_0000_0000; len += 16; } if n >= 1_0000_0000_0000 { n /= 1_0000_0000_0000; len += 12; } if n >= 1_0000_0000 { n /= 100_000_000; len += 8; } if n >= 1_0000 { n /= 1_0000; len += 4; } if n >= 100 { n /= 100; len += 2; } if n >= 10 { len += 1; } len } const_panic-0.2.13/src/lib.rs000064400000000000000000000203341046102023000141140ustar 00000000000000//! For panicking with formatting in const contexts. //! //! This library exists because the panic macro was stabilized for use in const contexts //! in Rust 1.57.0, without formatting support. //! //! All of the types that implement the [`PanicFmt`] trait can be formatted in panics. //! //! # Examples //! //! - [Basic](#basic) //! - [Custom Types](#custom-types) //! //! ### Basic //! //! ```compile_fail //! use const_panic::concat_assert; //! //! const FOO: u32 = 10; //! const BAR: u32 = 0; //! const _: () = assert_non_zero(FOO, BAR); //! //! #[track_caller] //! const fn assert_non_zero(foo: u32, bar: u32) { //! concat_assert!{ //! foo != 0 && bar != 0, //! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar //! } //! } //! ``` //! The above code fails to compile with this error: //! ```text //! error[E0080]: evaluation of constant value failed //! --> src/lib.rs:20:15 //! | //! 8 | const _: () = assert_non_zero(FOO, BAR); //! | ^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' //! neither foo nor bar can be zero! //! foo: 10 //! bar: 0', src/lib.rs:8:15 //! ``` //! //! When called at runtime //! ```should_panic //! use const_panic::concat_assert; //! //! assert_non_zero(10, 0); //! //! #[track_caller] //! const fn assert_non_zero(foo: u32, bar: u32) { //! concat_assert!{ //! foo != 0 && bar != 0, //! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar //! } //! } //! ``` //! it prints this: //! ```text //! thread 'main' panicked at ' //! neither foo nor bar can be zero! //! foo: 10 //! bar: 0', src/lib.rs:6:1 //! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace //! //! ``` //! //! ### Custom types //! //! Panic formatting for custom types can be done in these ways //! (in increasing order of verbosity): //! - Using the [`PanicFmt` derive] macro //! (requires the opt-in `"derive"` feature) //! - Using the [`impl_panicfmt`] macro //! (requires the default-enabled `"non_basic"` feature) //! - Using the [`flatten_panicvals`] macro //! (requires the default-enabled `"non_basic"` feature) //! - Manually implementing the [`PanicFmt`] trait as described in its docs. //! //! This example uses the [`PanicFmt` derive] approach. //! //! ```compile_fail //! use const_panic::{PanicFmt, concat_panic}; //! //! const LAST: u8 = { //! Foo{ //! x: &[], //! y: Bar(false, true), //! z: Qux::Left(23), //! }.pop().1 //! }; //! //! impl Foo<'_> { //! /// Pops the last element //! /// //! /// # Panics //! /// //! /// Panics if `self.x` is empty //! #[track_caller] //! const fn pop(mut self) -> (Self, u8) { //! if let [rem @ .., last] = self.x { //! self.x = rem; //! (self, *last) //! } else { //! concat_panic!( //! "\nexpected a non-empty Foo, found: \n", //! // uses alternative Debug formatting for `self`, //! // otherwise this would use regular Debug formatting. //! alt_debug: self //! ) //! } //! } //! } //! //! #[derive(PanicFmt)] //! struct Foo<'a> { //! x: &'a [u8], //! y: Bar, //! z: Qux, //! } //! //! #[derive(PanicFmt)] //! struct Bar(bool, bool); //! //! #[derive(PanicFmt)] //! enum Qux { //! Up, //! Down { x: u32, y: u32 }, //! Left(u64), //! } //! //! ``` //! The above code fails to compile with this error: //! ```text //! error[E0080]: evaluation of constant value failed //! --> src/lib.rs:57:5 //! | //! 7 | / Foo{ //! 8 | | x: &[], //! 9 | | y: Bar(false, true), //! 10 | | z: Qux::Left(23), //! 11 | | }.pop().1 //! | |___________^ the evaluated program panicked at ' //! expected a non-empty Foo, found: //! Foo { //! x: [], //! y: Bar( //! false, //! true, //! ), //! z: Left( //! 23, //! ), //! }', src/lib.rs:11:7 //! //! //! ``` //! //! # Limitations #![doc = crate::doc_macros::limitation_docs!()] //! //! ### Panic message length //! //! The panic message can only be up to [`MAX_PANIC_MSG_LEN`] long, //! after which it is truncated. //! //! # Cargo features //! //! - `"non_basic"`(enabled by default): //! Enables support for formatting structs, enums, and arrays. //!
//! Without this feature, you can effectively only format primitive types //! (custom types can manually implement formatting with more difficulty). //! //! - `"rust_latest_stable"`(disabled by default): //! Enables all the `"rust_1_*"` features. //! //! - `"rust_1_64"`(disabled by default): //! Enables formatting of additional items that require Rust 1.64.0 to do so. //! //! - `"rust_1_82"`(disabled by default): //! Enables formatting of additional items that require Rust 1.82.0 to do so. //! //! - `"rust_1_88"`(disabled by default): //! Enables formatting of additional items that require Rust 1.88.0 to do so. //! //! - `"derive"`(disabled by default): //! Enables the [`PanicFmt` derive] macro. //! //! # Plans //! //! None for now //! //! # No-std support //! //! `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used. //! //! # Minimum Supported Rust Version //! //! This requires Rust 1.57.0, because it uses the `panic` macro in a const context. //! //! //! [`PanicFmt` derive]: derive@crate::PanicFmt //! [`PanicFmt`]: trait@crate::PanicFmt //! [`impl_panicfmt`]: crate::impl_panicfmt //! [`flatten_panicvals`]: crate::flatten_panicvals //! [`MAX_PANIC_MSG_LEN`]: crate::MAX_PANIC_MSG_LEN #![no_std] #![cfg_attr(feature = "docsrs", feature(doc_cfg))] #![warn(missing_docs)] #![deny(clippy::missing_safety_doc)] #![deny(clippy::shadow_unrelated)] #![deny(clippy::wildcard_imports)] extern crate self as const_panic; #[macro_use] mod doc_macros; #[macro_use] mod macros; mod concat_panic_; mod debug_str_fmt; mod int_formatting; pub mod fmt; #[cfg(all(doctest, feature = "non_basic"))] pub mod doctests; mod panic_val; #[cfg(feature = "non_basic")] mod const_default; pub mod utils; #[cfg(all(test, not(feature = "test")))] compile_error! {r##"please use cargo test --features "test""##} #[cfg(feature = "non_basic")] mod slice_stuff; #[cfg(feature = "non_basic")] mod array_string; #[cfg(feature = "non_basic")] pub use crate::array_string::ArrayString; mod wrapper; mod fmt_impls { #[macro_use] pub(crate) mod basic_fmt_impls; #[cfg(feature = "rust_1_64")] mod rust_1_64_fmt_impls; #[cfg(feature = "rust_1_82")] mod rust_1_82_fmt_impls; #[macro_use] #[cfg(feature = "non_basic")] mod option_fmt_impls; #[cfg(feature = "non_basic")] mod nonzero_impls; #[cfg(feature = "non_basic")] mod other_impls; #[cfg(feature = "non_basic")] mod fmt_range; #[cfg(all(feature = "non_basic", feature = "rust_1_88"))] mod rust_1_88_nonbasic_fmt_impls; } pub use crate::{ concat_panic_::{concat_panic, MAX_PANIC_MSG_LEN}, panic_val::PanicVal, wrapper::StdWrapper, }; #[doc(no_inline)] pub use crate::fmt::{FmtArg, IsCustomType, PanicFmt}; #[cfg(feature = "non_basic")] #[doc(no_inline)] pub use crate::fmt::{ComputePvCount, TypeDelim}; #[doc(hidden)] pub mod __ { pub use core::{ assert, compile_error, concat, option::Option::{None, Some}, primitive::usize, result::Result::{Err, Ok}, stringify, }; pub use crate::*; #[cfg(feature = "non_basic")] pub use crate::reexported_non_basic::*; } #[cfg(feature = "non_basic")] #[doc(hidden)] mod reexported_non_basic { pub use core::{option::Option, primitive::str}; pub use typewit::MakeTypeWitness; pub use crate::{ concat_panic_::{compute_length, make_panic_string_unwrapped}, const_default::ConstDefault, macros::concat_macro::ConcatCmd, utils::{assert_flatten_panicvals_length, flatten_panicvals, panicvals_id}, }; pub const EPV: crate::PanicVal<'_> = crate::PanicVal::EMPTY; } #[cfg(feature = "derive")] include! {"./proc_macro_reexports/panicfmt_derive.rs"} #[doc(hidden)] #[cfg(feature = "test")] pub mod test_utils; #[doc(hidden)] #[cfg(feature = "test")] pub mod for_tests { pub use crate::concat_panic_::{format_panic_message, NotEnoughSpace}; } #[cfg(all(doctest))] #[doc = include_str!("../README.md")] pub struct ReadmeTest; const_panic-0.2.13/src/macros/concat_assert.rs000064400000000000000000000100651046102023000174620ustar 00000000000000/// Asserts that `$condition` is true. /// /// When only the `$condition` argument is passed, /// this delegates to the [`core::assert`] macro. /// /// When two or more arguments are passed, /// this panics with formatting by delegating the second and remaining arguments /// to the [`concat_panic`](macro@crate::concat_panic) macro. /// /// ### Examples /// /// ### Formatted assertion /// /// ```compile_fail /// use const_panic::concat_assert; /// /// const ONE: Even = Even::new(1); /// /// struct Even(u32); /// /// impl Even { /// #[track_caller] /// const fn new(n: u32) -> Self { /// concat_assert!(n % 2 == 0, "\nexpected the argument to be even, found: ", n); /// /// Even(n) /// } /// } /// ``` /// the above code errors with this message: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/concat_assert.rs:16:19 /// | /// 4 | const ONE: Even = Even::new(1); /// | ^^^^^^^^^^^^ the evaluated program panicked at ' /// expected the argument to be even, found: 1', src/macros/concat_assert.rs:4:19 /// /// ``` /// /// ### More formatting /// /// This example demonstrates what error non-`#[track_caller]` functions produce, /// and uses the `"non_basic"` feature(enabled by default). /// /// ```compile_fail /// use const_panic::concat_assert; /// /// const SUM: u64 = sum(&[3, 5, 8], 1..40); /// /// const fn sum(mut slice: &[u32], range: std::ops::Range) -> u64 { /// concat_assert!( /// range.start <= range.end && range.end <= slice.len(), /// "\ncannot index slice of length `", slice.len(), /// "` with `", range, "` range" /// ); /// /// let mut sum = 0u64; /// /// while let [curr, ref rem @ ..] = *slice { /// sum += curr as u64; /// /// slice = rem; /// } /// /// sum /// } /// ``` /// the above code errors with this message: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/concat_assert.rs:52:5 /// | /// 6 | const SUM: u64 = sum(&[3, 5, 8], 1..40); /// | ---------------------- inside `SUM` at src/macros/concat_assert.rs:6:18 /// ... /// 9 | / concat_assert!( /// 10 | | range.start <= range.end && range.end <= slice.len(), /// 11 | | "\ncannot index slice of length `", slice.len(), /// 12 | | "` with `", range, "` range" /// 13 | | ); /// | | ^ /// | | | /// | |_____the evaluated program panicked at ' /// cannot index slice of length `3` with `1..40` range', src/macros/concat_assert.rs:9:5 /// | inside `_doctest_main_src_macros_concat_assert_rs_46_0::sum` at /home/matias/Documents/proyectos programacion/const_panic/src/macros.rs:240:21 /// | /// = note: this error originates in the macro `$crate::concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info) /// ``` /// /// ### Unformatted assertion /// /// When only the `$condition` argument is passed, /// this delegates to the [`core::assert`] macro. /// /// ```compile_fail /// use const_panic::concat_assert; /// /// const _: () = concat_assert!(cfg!(any(feature = "foo", feature = "bar")) ); /// ``` /// the above code errors with this message: /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/concat_assert.rs:48:15 /// | /// 6 | const _: () = concat_assert!(cfg!(any(feature = "foo", feature = "bar")) ); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: cfg!(any(feature = \"foo\", feature = \"bar\"))', src/macros/concat_assert.rs:6:15 /// | /// = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) /// ``` /// /// #[macro_export] macro_rules! concat_assert { ($condition:expr $(,)?) => { $crate::__::assert!($condition); }; ($condition:expr, $($fmt:tt)*) => {{ #[allow(clippy::equatable_if_let)] if let false = $condition { $crate::concat_panic!{$($fmt)*} } }}; } const_panic-0.2.13/src/macros/concat_macro.rs000064400000000000000000000046411046102023000172650ustar 00000000000000use crate::ArrayString; use typewit::{MakeTypeWitness, TypeEq, TypeWitnessTypeArg}; /// Concatenates [`PanicFmt`] constants into a `&'static str` /// /// This formats arguments the same as the [`concat_panic`] macro, /// also requiring the arguments to be constant expressions. /// /// # Example /// /// ### Basic /// /// ```rust /// use const_panic::concat_; /// /// assert_eq!(concat_!("array: ", &[3u8, 5, 8, 13]), "array: [3, 5, 8, 13]"); /// /// ``` /// /// ### Formatted /// /// ```rust /// use const_panic::concat_; /// /// assert_eq!(concat_!({?}: get_message(), {}: get_message()), r#""hello"hello"#); /// /// const fn get_message() -> &'static str { /// "hello" /// } /// /// ``` /// /// [`PanicFmt`]: crate::fmt::PanicFmt /// [`concat_panic`]: macro@crate::concat_panic /// #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[macro_export] macro_rules! concat_ { () => (""); ($($args:tt)*) => ({ const fn __func_zxe7hgbnjs( cmd: $crate::__::ConcatCmd ) -> Ret_ZXE7HGBNJS { $crate::__concat_func_setup!{ (|args| { match cmd { $crate::__::ConcatCmd::ComputeLength(te) => te.to_left($crate::__::compute_length(args)), $crate::__::ConcatCmd::BuildArray(te) => te.to_left($crate::__::make_panic_string_unwrapped(args)), } }) [] [$($args)*,] } } const LEN_ZXE7HGBNJS: $crate::__::usize = __func_zxe7hgbnjs($crate::__::MakeTypeWitness::MAKE); const AS_ZXE7HGBNJS: $crate::ArrayString = __func_zxe7hgbnjs($crate::__::MakeTypeWitness::MAKE); const S_ZXE7HGBNJS: &$crate::__::str = AS_ZXE7HGBNJS.to_str(); S_ZXE7HGBNJS }) } #[doc(hidden)] pub enum ConcatCmd { ComputeLength(TypeEq), BuildArray(TypeEq>), } impl TypeWitnessTypeArg for ConcatCmd { type Arg = Ret; } impl MakeTypeWitness for ConcatCmd { const MAKE: Self = Self::ComputeLength(TypeEq::NEW); } impl MakeTypeWitness for ConcatCmd, CAP> { const MAKE: Self = Self::BuildArray(TypeEq::NEW); } const_panic-0.2.13/src/macros/impl_panicfmt.rs000064400000000000000000001005331046102023000174540ustar 00000000000000/// Implements the [`PanicFmt`](crate::fmt::PanicFmt) /// trait and the `to_panicvals` method it requires. /// /// For a derive macro alternative, there's the [`PanicFmt`](derive@crate::PanicFmt) derive, /// which requires the `"derive"` feature(disabled by default). /// /// # Syntax /// /// This macro roughly takes a type definition and a (conditionally required) list of impls, /// [for an example demonstrating all the parts of the syntax look here](#all-the-syntax). /// /// This macro has these optional attributes that go above the item definition /// (and must go in this order): /// /// - `#[pfmt(display_fmt = $display_fmt:expr)]`[**(example below)**](#display-example): /// Tells the macro to use the `$display_fmt` function to Display-format the type. /// /// /// - `#[pfmt(panicvals_lower_bound = $panicvals_lower_bound:expr)]`: /// Tells the macro to use at least `$panicvals_lower_bound` [`PanicVal`]s for /// formatting the type, useful for Display formatting with the /// `#[pfmt(display_fmt = ...)]` attribute. /// /// /// # Limitations /// /// ### Type parameters /// /// Types with type parameters can't be generically formatted, which has two workarounds. /// /// The first workaround is marking a type parameter as ignored with an `ignore` prefix, /// if the type parameter(s) are only used in marker types (eg: `PhantomData`). /// [example of this workaround](#phantom-type-parameter-example) /// /// The second workaround is to implement panic formatting with concrete type arguments, /// using trailing `(impl Foo)`s. /// [example of this workaround](#type-parameter-example) /// /// This limitation is caused by: /// - the lack of trait bound support in stable const fns. /// - the need to [have a concrete type argument](#concrete-pv-count) /// /// [example below](#type-parameter-example) /// /// ### Const parameters /// /// Const parameters must not affect the value of the `PanicFmt::PV_COUNT` of this type, /// since the const parameter [must be replaceable with a concrete value](#concrete-pv-count). ///
Note that arrays have a `PV_COUNT` of `1` for all lengths. /// /// /// ### Concrete `Self` type for `PanicFmt::PV_COUNT` /// /// The `to_panicvals` method that this macro generates roughly returns a /// ```text /// [PanicVal<'_>; ::PV_COUNT] /// ``` /// /// Because of limitations in stable const generics, /// the generic arguments of `Self` in the above code must be replaced with concrete arguments, /// requiring: /// - Lifetime arguments to be replaced with `'_` /// - Type arguments to be replaced with concrete types (usually `()`) /// - Const arguments to be replaced with concrete values (usually the default value for the type) /// /// # Examples /// /// ### Struct formatting /// /// ```rust /// use const_panic::{ArrayString, FmtArg, impl_panicfmt}; /// /// fn main(){ /// const FOO: Foo = Foo { /// x: &[3, 5, 8, 13], /// y: 21, /// z: Bar(false, true), /// }; /// /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; FOO), /// "Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }", /// ); /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; FOO), /// concat!( /// "Foo {\n", /// " x: [\n", /// " 3,\n", /// " 5,\n", /// " 8,\n", /// " 13,\n", /// " ],\n", /// " y: 21,\n", /// " z: Bar(\n", /// " false,\n", /// " true,\n", /// " ),\n", /// "}", /// ), /// ); /// } /// /// /// struct Foo<'a> { /// x: &'a [u8], /// y: u8, /// z: Bar, /// } /// /// // Implementing `PanicFmt` and the `to_panicvals` method for `Foo<'a>` /// impl_panicfmt!{ /// struct Foo<'a> { /// x: &'a [u8], /// y: u8, /// z: Bar, /// } /// } /// /// /// struct Bar(bool, bool); /// /// impl_panicfmt!{ /// struct Bar(bool, bool); /// } /// /// ``` /// /// ### Enum Formatting /// /// ```rust /// use const_panic::{ArrayString, FmtArg, impl_panicfmt}; /// /// fn main() { /// const UP: Qux = Qux::Up; /// // Debug formatting the Up variant /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; UP), /// "Up", /// ); /// /// /// const DOWN: Qux = Qux::Down { x: 21, y: 34, z: 55 }; /// // Debug formatting the Down variant /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; DOWN), /// "Down { x: 21, y: 34, z: 55 }", /// ); /// // Alternate-Debug formatting the Down variant /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; DOWN), /// concat!( /// "Down {\n", /// " x: 21,\n", /// " y: 34,\n", /// " z: 55,\n", /// "}", /// ) /// ); /// /// /// const LEFT: Qux = Qux::Left(89); /// // Debug formatting the Left variant /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; LEFT), /// "Left(89)", /// ); /// // Alternate-Debug formatting the Left variant /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; LEFT), /// concat!( /// "Left(\n", /// " 89,\n", /// ")", /// ) /// ); /// } /// /// enum Qux { /// Up, /// Down { x: T, y: T, z: T }, /// Left(u64), /// } /// /// /// // Because of limitations of stable const evaluation, /// // `Qux` can't generically implement panic formatting, /// // so this macro invocation implements panic formatting for these specifically: /// // - `Qux` /// // - `Qux` /// // - `Qux` /// impl_panicfmt!{ /// enum Qux { /// Up, /// Down { x: T, y: T, z: T }, /// Left(u64), /// } /// /// (impl Qux) /// (impl Qux) /// (impl Qux) /// } /// ``` /// /// /// ### Type parameters /// /// This example demonstrates support for types with type parameters. /// /// ```rust /// use const_panic::{ArrayString, FmtArg, impl_panicfmt}; /// /// use std::marker::PhantomData; /// /// { /// const WITH_INT: Foo<&str, u8> = Foo { /// value: 100u8, /// _marker: PhantomData, /// }; /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; WITH_INT), /// "Foo { value: 100, _marker: PhantomData }", /// ); /// } /// { /// const WITH_STR: Foo = Foo { /// value: "hello", /// _marker: PhantomData, /// }; /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; WITH_STR), /// r#"Foo { value: "hello", _marker: PhantomData }"#, /// ); /// } /// /// #[derive(Debug)] /// pub struct Foo { /// value: B, /// _marker: PhantomData, /// } /// /// impl_panicfmt!{ /// // `ignore` here tells the macro that this type parameter is not formatted. /// struct Foo { /// value: B, /// _marker: PhantomData, /// } /// /// // Because type parameters can't be generically formatted, /// // you need to list impls with concrete `B` type arguments. /// // /// // the generic parameters to the impl block go inside `[]` /// (impl[A] Foo) /// // the bounds in where clauses also go inside `[]` /// (impl[A] Foo where[A: 'static]) /// } /// /// /// /// ``` /// /// /// /// ### Phantom Type parameters /// /// This example demonstrates how type parameters can be ignored. /// /// ```rust /// use const_panic::{ArrayString, FmtArg, impl_panicfmt}; /// /// use std::marker::PhantomData; /// /// { /// const WITH_INT: Foo = Foo{ /// value: 5, /// _marker: PhantomData, /// }; /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; WITH_INT), /// "Foo { value: 5, _marker: PhantomData }", /// ); /// } /// { /// const WITH_STR: Foo = Foo { /// value: 8, /// _marker: PhantomData, /// }; /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; WITH_STR), /// r#"Foo { value: 8, _marker: PhantomData }"#, /// ); /// } /// /// #[derive(Debug)] /// pub struct Foo { /// value: u32, /// _marker: PhantomData<(PhantomData, B)>, /// } /// /// impl_panicfmt!{ /// // `ignore` here tells the macro that this type parameter is not formatted. /// // /// // `ignore(u8)` tells the macro to use `u8` as the `B` type parameter for /// // ` as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method. /// struct Foo /// // bounds must be written in the where clause, like this: /// where[ A: ?Sized ] /// { /// value: u32, /// _marker: PhantomData<(PhantomData, B)>, /// } /// } /// ``` /// /// /// ### Display formatting /// /// ```rust /// use const_panic::{impl_panicfmt, FmtArg, PanicFmt, PanicVal}; /// /// assert_eq!(const_panic::concat_!(debug: Foo([3, 5, 8])), "Foo([3, 5, 8])"); /// assert_eq!(const_panic::concat_!(display: Foo([3, 5, 8])), "3 5 8"); /// /// struct Foo([u8; 3]); /// /// impl_panicfmt! { /// // these (optional) attributes are the only supported struct-level attributes and /// // can only go in this order /// #[pfmt(display_fmt = Self::display_fmt)] /// // need this attribute to output more PanicVals in Display formatting than /// // in Debug formatting. /// #[pfmt(panicvals_lower_bound = 10)] /// struct Foo([u8; 3]); /// } /// /// impl Foo { /// const fn display_fmt(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::PV_COUNT] { /// let [a, b, c] = self.0; /// /// const_panic::flatten_panicvals!{fmtarg, Foo::PV_COUNT; /// a, " ", b, " ", c /// } /// } /// } /// /// ``` /// /// /// ### All the syntax /// /// ```rust /// # use const_panic::{impl_panicfmt, PanicFmt, PanicVal}; /// # use const_panic::fmt::FmtArg; /// # /// # use std::marker::PhantomData; /// # /// # #[derive(Debug)] /// # pub struct Foo<'a, 'b, C, D, E, const X: u32> /// # where /// # C: ?Sized, /// # D: ?Sized, /// # E: ?Sized, /// # { /// # _lifetimes: PhantomData<(&'a (), &'b ())>, /// # _marker: PhantomData<(PhantomData, PhantomData, PhantomData)>, /// # } /// impl_panicfmt!{ /// // these are the only supported struct-level attributes and can only go in this order /// #[pfmt(display_fmt = Self::display_fmt)] /// #[pfmt(panicvals_lower_bound = 100)] /// struct Foo< /// 'a, /// 'b, /// // For type parameters that aren't formatted. /// // Removes the `PanicFmt` bound on this type parameter /// // and uses `()` as the type argument for this to get /// // ` as PanicFmt>::PV_COUNT` in the generated `to_panicvals` methods. /// ignore C, /// // same as `C`, using `u8` instead of `()` /// ignore(u8) D, /// // un-`ignore`d type parameters must be replaced with concrete types in the impls below /// E, /// const X: u32, /// > /// // bounds must be written in the where clause, like this: /// where[ C: ?Sized, D: ?Sized, E: ?Sized ] /// { /// _lifetimes: PhantomData<(&'a (), &'b ())>, /// _marker: PhantomData<(PhantomData, PhantomData, PhantomData)>, /// } /// /// // The impls for this type, this is required for types with un-`ignore`d type parameters. /// // Otherwise, a generic impl is automatically generated. /// ( /// impl['a, 'b, D, E: ?Sized, const X: u32] Foo<'a, 'b, D, E, u32, X> /// where[D: ?Sized] /// ) /// ( /// impl['a, 'b, D, E: ?Sized, const X: u32] Foo<'a, 'b, D, E, &str, X> /// where[D: ?Sized] /// ) /// } /// /// impl<'a, 'b, C, D, E, const X: u32> Foo<'a, 'b, C, D, E, X> /// where /// C: ?Sized, /// D: ?Sized, /// E: ?Sized, /// { /// const fn display_fmt( /// &self, /// fmt: FmtArg, /// ) -> [PanicVal<'_>; >::PV_COUNT] { /// const_panic::flatten_panicvals!{fmt, >::PV_COUNT; /// "Foo: ", X /// } /// } /// } /// /// ``` /// /// [`PanicVal`]: crate::PanicVal #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[macro_export] macro_rules! impl_panicfmt { ( $(# $attrs:tt)* $kind:ident $typename:ident < $($rem:tt)* ) => ( $crate::__impl_panicfmt_step_aaa!{ ($(# $attrs)* $kind $typename) () ($($rem)*) } ); ( $(# $attrs:tt)* $kind:ident $typename:ident $($rem:tt)* ) => ( $crate::__impl_panicfmt_step_aaa!{ ($(# $attrs)* $kind $typename) () (> $($rem)*) } ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_panicfmt_step_aaa { ( ($(# $attrs:tt)* struct $struct_name:ident) $generic_params:tt ( $(,)? > $(( $($tupled:tt)* ))? $(where[$($where_preds:tt)*])?; $($rem:tt)* ) ) => ( $crate::__impl_panicfmt_step_ccc!{ [ $(# $attrs)* struct $struct_name $generic_params ($($($where_preds)*)?) $($rem)* ] [] [ $struct_name ( $($($tupled)*)? ) ] } ); ( ($(# $attrs:tt)* struct $struct_name:ident) $generic_params:tt ( $(,)? > $(where[$($where_preds:tt)*])? $(( $($tupled:tt)* ))?; $($rem:tt)* ) ) => ( compile_error!{"the where clause must come after the tuple struct fields"} ); ( ($(# $attrs:tt)* struct $struct_name:ident) $generic_params:tt ( $(,)?> $(where[$($where_preds:tt)*])? { $($braced:tt)* } $($rem:tt)* ) ) => ( $crate::__impl_panicfmt_step_ccc!{ [ $(# $attrs)* struct $struct_name $generic_params ($($($where_preds)*)?) $($rem)* ] [] [ $struct_name { $($braced)* } ] } ); ( ($(# $attrs:tt)* enum $enum_name:ident) $generic_params:tt ( $(,)?> $(where[$($where_preds:tt)*])? { $( $variant:ident $({ $($braced:tt)* })? $(( $($tupled:tt)* ))? ),* $(,)? } $($rem:tt)* ) ) => ( $crate::__impl_panicfmt_step_ccc!{ [ $(# $attrs)* enum $enum_name $generic_params ($($($where_preds)*)?) $($rem)* ] [] [ $( $variant $({ $($braced)* })? $(( $($tupled)* ))?, )* ] } ); //////////////////////////////////// ( $fixed:tt ($($prev_params:tt)*) ($(,)? $(ignore)? $lifetime:lifetime $($rem:tt)*) ) => ( $crate::__impl_panicfmt_step_aaa!{ $fixed ($($prev_params)* ($lifetime ($lifetime) ignore ('_)) ) ($($rem)*) } ); ( $fixed:tt $prev_params:tt ($(,)? $(ignore $(($($const_def:tt)*))? )? const $const_param:ident: $const_ty:ty , $($rem:tt)*) ) => ( $crate::__impl_panicfmt_step_aaa_const!{ $fixed ($(ignore $(($($const_def)*))? )?) $prev_params ($const_param: $const_ty) ($($rem)*) } ); ( $fixed:tt $prev_params:tt ($(,)? $(ignore $(($($const_def:tt)*))? )? const $const_param:ident: $const_ty:ty > $($rem:tt)*) ) => ( $crate::__impl_panicfmt_step_aaa_const!{ $fixed ($(ignore $(($($const_def)*))? )?) $prev_params ($const_param: $const_ty) (> $($rem)*) } ); ( $fixed:tt ($($prev_params:tt)*) ($(,)? ignore $(($ignore_ty:ty))? $type_param:ident $($rem:tt)*) ) => ( $crate::__impl_panicfmt_step_aaa!{ $fixed ($($prev_params)* ($type_param ($type_param) ignore (($($ignore_ty)?)) ) ) ($($rem)*) } ); ( $fixed:tt ($($prev_params:tt)*) ($(,)? $type_param:ident $($rem:tt)*) ) => ( $crate::__impl_panicfmt_step_aaa!{ $fixed (kept_type[$type_param] $($prev_params)* ($type_param ($type_param) kept ()) ) ($($rem)*) } ); ( $fixed:tt $prev_params:tt ($($rem:tt)+) ) => ( $crate::__::compile_error!{concat!( "could not parse this in the generic parameter(s) of the type definition:", stringify!($($rem:tt)+) )} ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_panicfmt_step_aaa_const { ( $fixed:tt (ignore($const_def:expr)) ($($prev_params:tt)*) ($const_param:ident: $const_ty:ty) $rem:tt ) => { $crate::__impl_panicfmt_step_aaa!{ $fixed ( $($prev_params)* ( $const_param (const $const_param: $const_ty) ignore ({$const_def}) ) ) $rem } }; ( $fixed:tt ($(ignore)?) ($($prev_params:tt)*) ($const_param:ident: $const_ty:ty) $rem:tt ) => { $crate::__impl_panicfmt_step_aaa!{ $fixed ( $($prev_params)* ( $const_param (const $const_param: $const_ty) ignore ({$crate::__::ConstDefault::DEFAULT}) ) ) $rem } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_panicfmt_step_ccc { ( $kept:tt $prev_variants:tt [ $variant:ident $($(@$is_brace:tt@)? {$($br_field:ident: $br_ty:ty),* $(,)*})? $($(@$is_tuple:tt@)? ( $($tup_ty:ty),* $(,)* ))? $(,$($rem_variants:tt)*)? ] ) => { $crate::zip_counter_and_last!{ $crate::__impl_panicfmt_step_ccc_inner!{ $kept $prev_variants $variant ( $($($is_brace)? Braced)? $($($is_tuple)? Tupled)? Braced ) [$($($rem_variants)*)?] } ($($(($br_field, $br_ty))*)? $($((, $tup_ty))*)?) ( (0 fi0) (1 fi1) (2 fi2) (3 fi3) (4 fi4) (5 fi5) (6 fi6) (7 fi7) (8 fi8) (9 fi9) (10 fi10) (11 fi11) (12 fi12) (13 fi13) (14 fi14) (15 fi15) (16 fi16) (17 fi17) (18 fi18) (19 fi19) (20 fi20) (21 fi21) (22 fi22) (23 fi23) (24 fi24) (25 fi25) (26 fi26) (27 fi27) (28 fi28) (29 fi29) (30 fi30) (31 fi31) (32 fi32) (33 fi33) (34 fi34) (35 fi35) (36 fi36) (37 fi37) (38 fi38) (39 fi39) (40 fi40) (41 fi41) (42 fi42) (43 fi43) (44 fi44) (45 fi45) (46 fi46) (47 fi47) (48 fi48) (49 fi49) (50 fi50) (51 fi51) (52 fi52) (53 fi53) (54 fi54) (55 fi55) (56 fi56) (57 fi57) (58 fi58) (59 fi59) (60 fi60) (61 fi61) (62 fi62) (63 fi63) ) } }; // Parsing unit variants / structs ( $kept:tt [$($prev_variants:tt)*] [ $variant:ident $(, $($rem_variants:tt)*)? ] ) => { $crate::__impl_panicfmt_step_ccc!{ $kept [$($prev_variants)* ($variant Braced) ] [$($($rem_variants)*)?] } }; // Finished parsing variants/structs, // ( $kept:tt $variants:tt [] ) => { $crate::__impl_panicfmt_step__panicfmt_impl!{ $kept variants $variants $kept $variants } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_panicfmt_step_ccc_inner { ( $kept:tt [$($prev_variants:tt)*] $variant:ident ($delim:ident $($ignore0:tt)*) [$($rem_variants:tt)*] $(prefix (($($p_fname:ident)?, $p_ty:ty) ($p_index:tt $p_fi_index:tt)))* $(last (($($l_fname:ident)?, $l_ty:ty) ($l_index:tt $l_fi_index:tt)) )? ) => { $crate::__impl_panicfmt_step_ccc!{ $kept [ $($prev_variants)* ( $variant $delim ($($l_index + 1,)? 0,) => $(prefix (($($p_fname)? $p_index), ($($p_fname)? $p_fi_index), $p_ty))* $(last (($($l_fname)? $l_index), ($($l_fname)? $l_fi_index), $l_ty))? ) ] [$($rem_variants)*] } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_panicfmt_step__panicfmt_impl { ( [ $(#[pfmt(display_fmt = $__display_fmt:expr)])? $(#[pfmt(panicvals_lower_bound = $panicvals_lower_bound:expr)])? $type_kind:ident $type_name:ident ( $($kept_type:ident [$kept_type_:ident])* $(( $gp_arg:tt ($($gp_param:tt)*) $ignorance:tt ($($gp_arg_concrete:tt)*) ))* ) ($($where_preds:tt)*) $($to_panicval_impls:tt)* ] variants[$( ( $variant:ident $delimiter:ident ($field_amount:expr, $($ignore2:tt)*) => $( $is_last_field:ident ( ($fpati:tt $($ignore3:tt)?), ($fname:tt $($ignore4:tt)?), $ty:ty ) )* ) )*] $kept:tt $variants:tt ) => ( impl<$($($gp_param)*),*> $crate::PanicFmt for $type_name<$($gp_arg),*> where $($kept_type_: $crate::PanicFmt,)* $($where_preds)* { type This = Self; type Kind = $crate::fmt::IsCustomType; const PV_COUNT: $crate::__::usize = $crate::utils::slice_max_usize(&[ $( $crate::fmt::ComputePvCount{ field_amount: $field_amount, summed_pv_count: 0 $( + <$ty as $crate::PanicFmt>::PV_COUNT )*, delimiter: $crate::fmt::TypeDelim::$delimiter }.call(), )* $($panicvals_lower_bound)? ]); } macro_rules! __assert_type_name__ { ($type_name) => () } $crate::__impl_to_panicvals!{ [$($kept_type)*] [$($ignorance ($($gp_arg_concrete)*))*] [ $type_name [$($where_preds)*] $(( ($($gp_param)*) $gp_arg ))* ] [$($to_panicval_impls)*] $kept $variants } ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_to_panicvals { ( [$(kept_type)*] $ignorances:tt $gp_param:tt [$($to_panicval_impls:tt)+] $kept:tt $variants:tt ) => ( $( $crate::__impl_to_panicvals_step_aaa!{ $ignorances $to_panicval_impls $to_panicval_impls $kept $variants } )* ); ( [] $ignorances:tt [$type_name:ident [$($where_preds:tt)*] $(( ($($gp_param:tt)*) $gp_arg:tt ))* ] [] $kept:tt $variants:tt ) => ( $crate::__impl_to_panicvals_step_aaa!{ $ignorances ( impl[$($($gp_param)*),*] $type_name<$($gp_arg),*> where[$($where_preds)*] ) ( impl[$($($gp_param)*),*] $type_name<$($gp_arg),*> where[$($where_preds)*] ) $kept $variants } ); ([$(kept_type)+] $ignorances:tt $gp_param:tt [] $kept:tt $variants:tt) => ( $crate::__::compile_error!{"\ type parameters must either be:\n\ - all prefixed with ignore\n\ - be replaced with concrete type arguments in \ `(impl Foo)`s after the type definition\n\ "} ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_to_panicvals_step_aaa { ( $ignorances:tt (impl $([$($impl_param:tt)*])? $type_name:ident $(where $where_preds:tt)?) $impl:tt $kept:tt $variants:tt ) => { __assert_type_name__!{$type_name} $crate::__impl_to_panicvals_step_bbb!{ ([$($($impl_param)*)?] $type_name $kept $variants) $ignorances [] [> $(where $where_preds)?] } }; ( $ignorances:tt (impl $([$($impl_param:tt)*])? $type_name:ident <$($args:tt)*) (impl $([$($impl_paramb:tt)*])? $type:path $(where $where_preds:tt)? ) $kept:tt $variants:tt ) => { __assert_type_name__!{$type_name} $crate::__impl_to_panicvals_step_bbb!{ ([$($($impl_param)*)?] $type $kept $variants) $ignorances [] [$($args)*] } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_to_panicvals_step_bbb { // finished parsing ( $kept:tt [] $cself:tt [> $(where[$($where_preds:tt)*])?] ) => { $crate::__impl_to_panicvals_finish!{ $kept [$($($where_preds)*)?] $cself } }; // const or lifetime argument ($kept:tt $ignorance:tt $prev_cself:tt [$(,)? $gen_arg:tt , $($rem:tt)*]) => { $crate::__impl_to_panicvals_step_bbb_inner!{ $kept $ignorance [$gen_arg] $prev_cself [$($rem)*] } }; ($kept:tt $ignorance:tt $prev_cself:tt [$(,)? $gen_arg:tt > $($rem:tt)*]) => { $crate::__impl_to_panicvals_step_bbb_inner!{ $kept $ignorance [$gen_arg] $prev_cself [> $($rem)*] } }; // type argument ($kept:tt $ignorance:tt $prev_cself:tt [$(,)? $ty_arg:ty , $($rem:tt)* ]) => { $crate::__impl_to_panicvals_step_bbb_inner!{ $kept $ignorance [$ty_arg] $prev_cself [$($rem)*] } }; ($kept:tt $ignorance:tt $prev_cself:tt [$(,)? $ty_arg:ty > $($rem:tt)*]) => { $crate::__impl_to_panicvals_step_bbb_inner!{ $kept $ignorance [$ty_arg] $prev_cself [> $($rem)*] } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_to_panicvals_step_bbb_inner { ( $kept:tt [kept $gp_arg_concrete:tt $($rem_ignorance:tt)*] [$gen_arg:tt] [$($prev_cself:tt)*] $rem:tt ) => { $crate::__impl_to_panicvals_step_bbb!{ $kept [$($rem_ignorance)*] [$($prev_cself)* $gen_arg,] $rem } }; ( $kept:tt [ignore ($($gp_arg_concrete:tt)*) $($rem_ignorance:tt)*] $gen_arg:tt [$($prev_cself:tt)*] $rem:tt ) => { $crate::__impl_to_panicvals_step_bbb!{ $kept [$($rem_ignorance)*] [$($prev_cself)* $($gp_arg_concrete)*,] $rem } }; } #[doc(hidden)] #[macro_export] macro_rules! __impl_to_panicvals_finish { // finished parsing ( ( [$($impl_param:tt)*] $type:ty [ $(#[pfmt(display_fmt = $display_fmt:expr)])? $(#[pfmt(panicvals_lower_bound = $__panicvals_lower_bound:expr)])? $type_kind:ident $type_name:ident $generics:tt $type_where_preds:tt $($to_panicval_impls:tt)* ] [$( ( $variant:ident $delimiter:ident ($field_amount:expr, $($ignore2:tt)*) => $( $is_last_field:ident ( ($fpati:tt $($ignore3:tt)?), ($fname:tt $($ignore4:tt)?), $ty:ty ) )* ) )*] ) [ $($where_preds:tt)* ] $cself:tt ) => { impl<$($impl_param)*> $type where $($where_preds)* { pub const fn to_panicvals( &self, mut fmt: $crate::FmtArg, ) -> [$crate::PanicVal<'_>; $crate::__ipm_cself!($type_name $cself)] { $( if let $crate::fmt::FmtKind::Display = fmt.fmt_kind { $display_fmt(self, fmt) } else )? { match self { $( $crate::__ipm_pattern!($type_kind $variant{$($fpati: $fname,)* ..}) => $crate::__ipm_fmt!{ ($crate::__ipm_cself!($type_name $cself)) $delimiter $variant fmt ( $($is_last_field ($fname, $ty))* ) }, )* } } } } }; } #[doc(hidden)] #[macro_export] macro_rules! __ipm_pattern { (struct $name:ident {$($patterns:tt)*}) => { $name {$($patterns)*} }; (enum $name:ident {$($patterns:tt)*}) => { Self::$name {$($patterns)*} }; } #[doc(hidden)] #[macro_export] macro_rules! __ipm_fmt { ( ($count:expr) $delimiter:ident $typename:ident $fmt:ident ( $($is_last_field:ident ($fname:ident, $ty:ty))+ ) ) => ({ let (open, close) = $crate::fmt::TypeDelim::$delimiter.get_open_and_close(); $crate::__::flatten_panicvals::<{$count}>(&[ &[ $crate::PanicVal::write_str($crate::__::stringify!($typename)), { $fmt = $fmt.indent(); open.to_panicval($fmt) } ], $( $crate::__ipm_pv_fmt_field_name!($delimiter $fname), &$crate::PanicFmt::PROOF .infer($fname) .coerce($fname) .to_panicvals($fmt), &$crate::__ipm_pv_comma!($is_last_field) .to_panicvals($fmt), )* &[ { $fmt = $fmt.unindent(); close.to_panicval($fmt) } ], ]) }); ( ($count:expr) $delimiter:ident $typename:ident $fmt:ident () ) => { $crate::__::flatten_panicvals::<{$count}>(&[ &[$crate::PanicVal::write_str($crate::__::stringify!($typename))] ]) } } #[doc(hidden)] #[macro_export] macro_rules! __ipm_pv_fmt_field_name { (Tupled $field_name:ident) => { &[] }; (Braced $field_name:ident) => { &[$crate::PanicVal::write_str($crate::__::concat!( $crate::__::stringify!($field_name), ": " ))] }; } #[doc(hidden)] #[macro_export] macro_rules! __ipm_pv_comma { (prefix) => { $crate::fmt::COMMA_SEP }; (last) => { $crate::fmt::COMMA_TERM }; } #[doc(hidden)] #[macro_export] macro_rules! __ipm_cself { ($type_name:ident [$($cself:tt)*]) => { <$type_name<$($cself)*> as $crate::PanicFmt>::PV_COUNT }; } const_panic-0.2.13/src/macros/macro_utils.rs000064400000000000000000000023751046102023000171600ustar 00000000000000#[doc(hidden)] #[macro_export] macro_rules! zip_counter_and_last { ( $(:: $(@$dummy:tt@)?)? $($macro:ident)::* ! $prev_args:tt ($($iter:tt)*) ($($counter:tt)*) ) => { $crate::__zip_counter_and_last_inner!{ (($(:: $($dummy)?)? $($macro)::*) $prev_args) [] ($($iter)*) ($($counter)*) } } } #[doc(hidden)] #[macro_export] macro_rules! __zip_counter_and_last_inner { ( $macro:tt [$($prev:tt)*] ($elem:tt $($iter:tt)+) ($counter:tt $($rem_counter:tt)+) ) => { $crate::__zip_counter_and_last_inner!{ $macro [$($prev)* prefix($elem $counter)] ($($iter)*) ($($rem_counter)*) } }; ( ( ($($macro:tt)*) { $($prev_args:tt)* } ) [$($prev:tt)*] ($elem:tt) ($counter:tt $($rem_counter:tt)*) ) => { $($macro)* !{ $($prev_args)* $($prev)* last($elem $counter) } }; ( ( ($($macro:tt)*) { $($prev_args:tt)* } ) [$($prev:tt)*] () ($($rem_counter:tt)*) ) => { $($macro)* !{ $($prev_args)* $($prev)* } }; } const_panic-0.2.13/src/macros/non_basic_macros.rs000064400000000000000000000505251046102023000201360ustar 00000000000000/// Formats multiple values into an array of `PanicVal`s. /// /// The `flatten`ing part comes from the fact that each argument /// is converted to an array of `PanicVal`, /// which are then concatenated into a single array. /// /// # Arguments /// /// The syntax for this macro is /// ```text /// flatten_panicvals!( /// $fmtarg:expr $(, $pv_count:expr)?; /// $( /// $($Type:ty => )? $($format_override:tt :)? $arg_to_fmt:expr /// ),* /// $()? /// ) /// ``` /// /// `$fmtarg` is a [`FmtArg`](crate::FmtArg) argument /// which determines how non-literal `$arg_to_fmt` arguments are formatted. /// /// `$pv_count` is an optional argument which overrides the length of the array /// that this returns. ///
If this argument is smaller than the flattened arrays would create, /// it produces a compile-time error. /// If this is larger, this fills the trailing elements with `PanicVal::EMPTY`. /// /// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument, /// changing how that `$arg_to_fmt` argument is formatted. /// /// `$arg_to_fmt` are the formatted arguments, /// which must implement the [`PanicFmt`](crate::fmt::PanicFmt) trait. /// /// /// If the `$Type =>` syntax is used, this calls the `to_panicvals` /// method on `$arg_to_fmt`.
/// If the `$Type =>` syntax is *not* used, this calls the `to_panicval` /// method on `$arg_to_fmt`. /// /// These are the signatures of those methods: /// ```rust /// # use const_panic::{FmtArg, PanicFmt, PanicVal}; /// # struct Foo; /// # /// # impl PanicFmt for Foo { /// # type This = Self; /// # type Kind = const_panic::IsCustomType; /// # const PV_COUNT: usize = 1; /// # } /// # impl Foo { /// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'_>; ::PV_COUNT] /// # { loop{} } /// # } /// ``` /// ```rust /// # use const_panic::{FmtArg, PanicVal}; /// # struct Bar; /// # /// # impl Bar { /// const fn to_panicval(&self, f: FmtArg) -> PanicVal<'_> /// # { loop{} } /// # } /// ``` /// /// ### Parsing limitation /// /// Because of limitations of `macro_rules!` macros, /// you'll sometimes need to wrap `$arg_to_fmt` arguments in parentheses to fix /// this error: /// ```text /// error: expected type, found `foo` /// ``` /// /// #[doc = formatting_docs!(" - `open`: increments `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`] before formatting the argument, and uses Display formatting for that argument. - `close`: decrements `$fmtarg`'s indentation by [`fmt::INDENTATION_STEP`] before formatting the argument, and uses Display formatting for that argument. [`fmt::INDENTATION_STEP`]: crate::fmt::INDENTATION_STEP ")] /// /// /// # Examples /// /// [Struct](#struct-formatting) and [Enum](#enum-formatting) Formatting examples below. /// /// ### Basic /// /// ```rust /// use const_panic::{ArrayString, FmtArg, flatten_panicvals}; /// /// /// assert_eq!( /// ArrayString::<999>::from_panicvals( /// // Formatting literals /// &flatten_panicvals!(FmtArg::DEBUG; 100u8, "hello") /// ).unwrap(), /// "100hello" /// ); /// /// assert_eq!( /// ArrayString::<999>::from_panicvals( /// // Formatting non-literals. /// // `"foo"` is considered a non-literal, because it's inside other tokens. /// &flatten_panicvals!(FmtArg::ALT_DEBUG; ("foo"), [100u8, 200]) /// ).unwrap(), /// concat!( /// "\"foo\"[\n", /// " 100,\n", /// " 200,\n", /// "]", /// ) /// ); /// /// assert_eq!( /// ArrayString::<999>::from_panicvals(& /// // Alternate-Debug Formatting composite types. /// flatten_panicvals!( /// FmtArg::ALT_DEBUG; /// Foo => Foo(3, "5"), /// ", ", /// Bar => Bar{x: "hello"} /// ) /// ).unwrap(), /// concat!( /// "Foo(\n", /// " 3,\n", /// " \"5\",\n", /// "), Bar {\n", /// " x: \"hello\",\n", /// "}", /// ) /// ); /// /// assert_eq!( /// ArrayString::<999>::from_panicvals(& /// // Overriding the formatting of arguments. /// // /// // The `open` and `close` overrides are demonstrated in the /// // struct and enum examples. /// flatten_panicvals!( /// FmtArg::DEBUG; /// Foo => display: Foo(3, "5"), /// debug: ", ", /// Bar => Bar{x: "hello"} /// ) /// ).unwrap(), /// r#"Foo(3, 5)", "Bar { x: "hello" }"# /// ); /// /// /// /// struct Foo(u32, &'static str); /// /// const_panic::impl_panicfmt!{ /// struct Foo(u32, &'static str); /// } /// /// struct Bar { /// x: &'static str, /// } /// /// const_panic::impl_panicfmt!{ /// struct Bar { /// x: &'static str, /// } /// } /// /// ``` /// /// ### Struct formatting /// /// Implementing panic formatting for braced and tuple structs. /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg, PanicFmt, ComputePvCount}, /// ArrayString, PanicVal, /// flatten_panicvals, /// }; /// /// fn main(){ /// let foo = Foo { /// x: &[3, 5, 8, 13], /// y: 21, /// z: Bar(false, true), /// }; /// /// assert_eq!( /// ArrayString::<100>::from_panicvals(&foo.to_panicvals(FmtArg::DEBUG)).unwrap(), /// "Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }", /// ); /// assert_eq!( /// ArrayString::<200>::from_panicvals(&foo.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(), /// concat!( /// "Foo {\n", /// " x: [\n", /// " 3,\n", /// " 5,\n", /// " 8,\n", /// " 13,\n", /// " ],\n", /// " y: 21,\n", /// " z: Bar(\n", /// " false,\n", /// " true,\n", /// " ),\n", /// "}", /// ), /// ); /// } /// /// /// /// struct Foo<'a> { /// x: &'a [u8], /// y: u8, /// z: Bar, /// } /// /// struct Bar(bool, bool); /// /// /// impl PanicFmt for Foo<'_> { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// // `ComputePvCount` allows computing the length of the array of `PanicVal`s /// // returned by `Foo::to_panicvals` below. /// // /// // Note that ComputePvCount only calculates the correct number if you /// // follow the pattern in this example. /// const PV_COUNT: usize = ComputePvCount{ /// field_amount: 3, /// summed_pv_count: <&[u8]>::PV_COUNT /// + ::PV_COUNT /// + ::PV_COUNT, /// delimiter: fmt::TypeDelim::Braced, /// }.call(); /// } /// /// impl<'a> Foo<'a> { /// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'a>; Foo::PV_COUNT] { /// // These constants from `fmt` add newlines and padding /// // when the `fmtarg.is_alternate` flag is enabled, /// // to match the standard behavior for `Debug` formatting. /// flatten_panicvals! {fmtarg; /// "Foo", /// // the `open:` format override increments `fmtarg.indentation` /// // by `const_panic::fmt::INDENTATION_STEP` spaces. /// // The indentation field is used by these constants when the /// // `fmtarg.is_alternate` flag is enabled. /// open: fmt::OpenBrace, /// // fmt::COMMA_SEP must only be used between fields /// "x: ", &[u8] => self.x, fmt::COMMA_SEP, /// "y: ", u8 => self.y, fmt::COMMA_SEP, /// // fmt::COMMA_TERM must only be used after the last field /// "z: ", Bar => self.z, fmt::COMMA_TERM, /// // the `close:` format override decrements the indentation. /// close: fmt::CloseBrace, /// } /// } /// } /// /// impl PanicFmt for Bar { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// const PV_COUNT: usize = ComputePvCount{ /// field_amount: 2, /// summed_pv_count: ::PV_COUNT * 2, /// delimiter: fmt::TypeDelim::Tupled, /// }.call(); /// } /// /// impl Bar { /// const fn to_panicvals(&self, f: FmtArg) -> [PanicVal<'static>; Bar::PV_COUNT] { /// flatten_panicvals! {f; /// "Bar", /// open: fmt::OpenParen, /// // fmt::COMMA_SEP must only be used between fields /// self.0, fmt::COMMA_SEP, /// // fmt::COMMA_TERM must only be used after the last field /// self.1, fmt::COMMA_TERM, /// close: fmt::CloseParen, /// } /// } /// } /// ``` /// /// ### Enum Formatting /// /// This example demonstrates formatting of generic enum types. /// /// ```rust /// use const_panic::{ /// fmt::{self, FmtArg, PanicFmt, ComputePvCount}, /// ArrayString, PanicVal, /// flatten_panicvals, /// }; /// /// fn main() { /// let up: Qux = Qux::Up; /// // Debug formatting the Up variant /// assert_eq!( /// ArrayString::<100>::from_panicvals(&up.to_panicvals(FmtArg::DEBUG)).unwrap(), /// "Up", /// ); /// /// /// let down: Qux = Qux::Down { x: 21, y: 34, z: 55 }; /// // Debug formatting the Down variant /// assert_eq!( /// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::DEBUG)).unwrap(), /// "Down { x: 21, y: 34, z: 55 }", /// ); /// // Alternate-Debug formatting the Down variant /// assert_eq!( /// ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(), /// concat!( /// "Down {\n", /// " x: 21,\n", /// " y: 34,\n", /// " z: 55,\n", /// "}", /// ) /// ); /// /// /// let left: Qux = Qux::Left(89); /// // Debug formatting the Left variant /// assert_eq!( /// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::DEBUG)).unwrap(), /// "Left(89)", /// ); /// // Alternate-Debug formatting the Left variant /// assert_eq!( /// ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(), /// concat!( /// "Left(\n", /// " 89,\n", /// ")", /// ) /// ); /// } /// /// enum Qux { /// Up, /// Down { x: T, y: T, z: T }, /// Left(u64), /// } /// /// /// impl PanicFmt for Qux { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// const PV_COUNT: usize = { /// // `ComputePvCount` computes the length of the array of `PanicVal`s /// // produced by each variant. /// // /// // `slice_max_usize` returns the maximum usize in a slice. /// // In this case, to return the longest array produced by the variants. /// const_panic::utils::slice_max_usize(&[ /// ComputePvCount{ /// field_amount: 0, /// summed_pv_count: 0, /// delimiter: fmt::TypeDelim::Braced, /// }.call(), /// ComputePvCount{ /// field_amount: 3, /// summed_pv_count: ::PV_COUNT * 3, /// delimiter: fmt::TypeDelim::Braced, /// }.call(), /// ComputePvCount{ /// field_amount: 1, /// summed_pv_count: ::PV_COUNT, /// delimiter: fmt::TypeDelim::Tupled, /// }.call(), /// ]) /// }; /// } /// /// // Because of limitations of stable const evaluation, /// // you have to use macros to implement the `to_panicvals` method /// // for more than one concrete type (ignoring lifetimes). /// // /// // This macro implements panic formatting for /// // - `Qux` /// // - `Qux` /// // - `Qux` /// const_panic::inline_macro! { /// (u8), /// (u16), /// (u32); /// /// ($T:ty) => /// /// impl Qux<$T> { /// pub const fn to_panicvals( /// &self, /// fmtarg: FmtArg, /// ) -> [PanicVal<'static>; >::PV_COUNT] { /// match self { /// Self::Up => /// // The `>::PV_COUNT` argument tells `flatten_panicvals` to /// // create an array of that length. /// // Variants that would otherwise produce shorter arrays /// // pad that array with trailing `PanicVal::EMPTY`. /// flatten_panicvals! {fmtarg, >::PV_COUNT; /// "Up" /// }, /// Self::Down{x, y, z} => /// // These constants from `fmt` add newlines and padding /// // when the `fmtarg.is_alternate` flag is enabled, /// // to match the standard behavior for `Debug` formatting. /// flatten_panicvals! {fmtarg, >::PV_COUNT; /// "Down", /// // the `open:` format override increments `fmtarg.indentation` /// // by `const_panic::fmt::INDENTATION_STEP` spaces. /// // The indentation field is used by these constants when the /// // `fmtarg.is_alternate` flag is enabled. /// open: fmt::OpenBrace, /// // fmt::COMMA_SEP must only be used between fields /// "x: ", x, fmt::COMMA_SEP, /// "y: ", y, fmt::COMMA_SEP, /// // fmt::COMMA_TERM must only be used after the last field /// "z: ", z, fmt::COMMA_TERM, /// close: fmt::CloseBrace, /// }, /// Self::Left(x) => flatten_panicvals! {fmtarg, >::PV_COUNT; /// "Left", /// open: fmt::OpenParen, /// x, fmt::COMMA_TERM, /// close: fmt::CloseParen, /// }, /// } /// } /// } /// } /// ``` /// /// /// [`PanicVal`]: crate::PanicVal /// #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[macro_export] macro_rules! flatten_panicvals { ($fmtargs:expr $(, $length:expr)?; $($args:tt)* ) => {{ let mut fmtargs: $crate::FmtArg = $fmtargs; $crate::__to_pvf_inner!(fmtargs [$(length($length))?][$($args)* ,]) }}; } #[doc(hidden)] #[macro_export] macro_rules! __to_pvf_inner { ( $fmtargs:ident [ $(length($expected_length:expr))? $((($len:expr, $kind:ident $args:tt), $fmt_override:tt, $reff:expr))* ] [$(,)*] ) => ({ const __ADDED_UP_LEN_SDOFKE09F__: $crate::__::usize = 0 $( + $len )*; const __LEN_SDOFKE09F__: $crate::__::usize = $crate::__to_pvf_used_length!(__ADDED_UP_LEN_SDOFKE09F__, $($expected_length)?); $( const _: () = $crate::__::assert_flatten_panicvals_length( $expected_length, __ADDED_UP_LEN_SDOFKE09F__, ); )? $crate::__::flatten_panicvals::<__LEN_SDOFKE09F__>(&[ $( $crate::__to_pvf_kind!($fmtargs $kind $args, $fmt_override, $reff) ),* ]) }); // Had to add these workarounds // to avoid getting stuck being parsed as a type in the `$ty:ty =>` branch. ($fmtargs:ident $prev:tt [$tt:tt, $($rem:tt)*]) => { $crate::__to_pvf_expr!{ $fmtargs $prev (1, single()) [$tt, $($rem)*] } }; ($fmtargs:ident $prev:tt [&$tt:tt, $($rem:tt)*]) => { $crate::__to_pvf_expr!{ $fmtargs $prev (1, single()) [&$tt, $($rem)*] } }; ($fmtargs:ident $prev:tt [$ty:ty => $($rem:tt)*]) => { $crate::__to_pvf_expr!{ $fmtargs $prev (<$ty as $crate::__::PanicFmt>::PV_COUNT, many($ty)) [$($rem)*] } }; ($fmtargs:ident $prev:tt [$($rem:tt)*]) => { $crate::__to_pvf_expr!{ $fmtargs $prev (1, single()) [$($rem)*] } }; } #[doc(hidden)] #[macro_export] macro_rules! __to_pvf_expr { ($fmtargs:ident [$($prev:tt)*] $other:tt [$kw:tt: $reff:expr, $($rem:tt)*]) => { $crate::__to_pvf_inner!{ $fmtargs [$($prev)* ($other, $kw, $reff)] [$($rem)*] } }; ($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:literal, $($rem:tt)*])=>{ $crate::__to_pvf_inner!{ $fmtargs [$($prev)* ($other, display, $reff)] [$($rem)*] } }; ($fmtargs:ident [$($prev:tt)*] $other:tt [$reff:expr, $($rem:tt)*]) => { $crate::__to_pvf_inner!{ $fmtargs [$($prev)* ($other, _, $reff)] [$($rem)*] } }; ($fmtargs:ident [$($prev:tt)*] $other:tt [$($rem:tt)*]) => { $crate::__::compile_error!(concat!( "expected expression, found:", stringify!($($rem)*) )) }; } #[doc(hidden)] #[macro_export] macro_rules! __to_pvf_kind { ($fmtargs:ident single (), $fmt_override:tt, $reff:tt) => { &match &$reff { reff => [$crate::__::PanicFmt::PROOF .infer(reff) .coerce(reff) .to_panicval($crate::__set_fmt_from_kw!($fmt_override, $fmtargs))], } }; ($fmtargs:ident many ($ty:ty), $fmt_override:tt, $reff:tt) => { $crate::__::panicvals_id::<{ <$ty as $crate::__::PanicFmt>::PV_COUNT }>(&match &$reff { reff => <$ty as $crate::__::PanicFmt>::PROOF .coerce(reff) .to_panicvals($crate::__set_fmt_from_kw!($fmt_override, $fmtargs)), }) }; } #[doc(hidden)] #[macro_export] macro_rules! __to_pvf_used_length { ( $added_up:expr, $expected_length:expr ) => { $crate::utils::max_usize($added_up, $expected_length) }; ( $added_up:expr, ) => { $added_up }; } /// Helper macro for defining and using a `macro_rules!` macro inline. /// /// The reason this was defined is to work around a limitation in stable const-eval, /// where you can't call methods on generic types. /// /// # Example /// /// Implementing panic formatting for a generic struct with [`flatten_panicvals`]. /// /// ```rust /// use const_panic::{ /// fmt::{self, ComputePvCount, FmtArg}, /// ArrayString, PanicFmt, PanicVal, flatten_panicvals, inline_macro, /// }; /// /// // Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DEBUG; Foo(10, 20)), /// "Foo(10, 20)" /// ); /// /// // Alternate-Debug formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::ALT_DEBUG; Foo(false, true)), /// concat!( /// "Foo(\n", /// " false,\n", /// " true,\n", /// ")", /// ), /// ); /// /// // Display formatting /// assert_eq!( /// const_panic::concat_!(FmtArg::DISPLAY; Foo("hmm", "what?")), /// "Foo(hmm, what?)" /// ); /// /// /// /// /// struct Foo(T, T); /// /// impl PanicFmt for Foo /// where /// T: PanicFmt, /// { /// type This = Self; /// type Kind = const_panic::IsCustomType; /// /// const PV_COUNT: usize = ComputePvCount{ /// field_amount: 2, /// summed_pv_count: T::PV_COUNT * 2, /// delimiter: fmt::TypeDelim::Tupled, /// }.call(); /// } /// /// // Because of limitations of stable const evaluation, /// // this macro only implements panic formatting for /// // - `Foo` /// // - `Foo` /// // - `Foo<&str>` /// inline_macro!{ /// (bool), (u8), (&str); /// ($T:ty)=> /// impl Foo<$T> { /// const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::<$T>::PV_COUNT] { /// flatten_panicvals! {fmtarg; /// "Foo", /// open: fmt::OpenParen, /// $T => self.0, fmt::COMMA_SEP, /// $T => self.1, fmt::COMMA_TERM, /// close: fmt::CloseParen, /// } /// } /// } /// } /// /// ``` #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] #[macro_export] macro_rules! inline_macro{ ( $(($($args:tt)*)),* $(,)?; ($($params:tt)*) => $($code:tt)* ) => { macro_rules! __dummy__ { ($($params)*) => {$($code)*} } $( __dummy__!{ $($args)* } )* } } const_panic-0.2.13/src/macros/unwrapping.rs000064400000000000000000000116001046102023000170200ustar 00000000000000/// Gets the value in the `Some` variant. /// /// # Panics /// /// Panics if `$opt` is a None. /// /// # Example /// /// ```rust /// use const_panic::unwrap_some; /// /// const SUM: u8 = unwrap_some!(add_up(&[3, 5, 8, 13])); /// /// assert_eq!(SUM, 29); /// /// /// const fn add_up(mut slice: &[u8]) -> Option { /// let mut sum = 0u8; /// /// while let [x, ref rem @ ..] = *slice { /// match sum.checked_add(x) { /// Some(x) => sum = x, /// None => return None, /// } /// slice = rem; /// } /// /// Some(sum) /// } /// /// ``` /// /// /// ### Error /// /// This is what the compile-time error looks like when attempting to unwrap a `None`: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/unwrapping.rs:10:17 /// | /// 6 | const SUM: u8 = unwrap_some!(add_up(&[3, 5, 8, 13, 250])); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// invoked `unwrap_some` macro with a `None` value', src/macros/unwrapping.rs:6:17 /// | /// = note: this error originates in the macro `unwrap_some` (in Nightly builds, run with -Z macro-backtrace for more info) /// /// ``` /// #[macro_export] macro_rules! unwrap_some { ($opt:expr) => { match $opt { $crate::__::Some(x) => x, $crate::__::None => $crate::concat_panic(&[&[$crate::PanicVal::write_str( "\ninvoked `unwrap_some` macro with a `None` value", )]]), } }; } /// Gets the value in the `Ok` variant. /// /// # Panics /// /// This panics if `$res` is an `Err`, including the debug-formatted error in the panic message. /// /// # Example /// /// The struct formatting below requires the `"non_basic"` feature (enabled by default) /// #[cfg_attr(feature = "non_basic", doc = "```rust")] #[cfg_attr(not(feature = "non_basic"), doc = "```ignore")] /// use const_panic::unwrap_ok; /// /// const SUM: u64 = unwrap_ok!(add_up_evens(&[2, 4, 8, 16])); /// /// assert_eq!(SUM, 30); /// /// const fn add_up_evens(slice: &[u8]) -> Result { /// let mut sum = 0u64; /// let mut i = 0; /// /// while i < slice.len() { /// let x = slice[i]; /// /// if x % 2 == 1 { /// return Err(OddError{at: i, number: x}); /// } /// /// sum += x as u64; /// i += 1; /// } /// /// Ok(sum) /// } /// /// /// struct OddError { /// at: usize, /// number: u8, /// } /// /// // You can also use `#[derive(PanicFmt))]` with the "derive" feature /// const_panic::impl_panicfmt!{ /// struct OddError { /// at: usize, /// number: u8, /// } /// } /// /// ``` /// /// ### Error /// /// This is what the compile-time error looks like when attempting to unwrap an `Err`: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/unwrapping.rs:51:18 /// | /// 6 | const SUM: u64 = unwrap_ok!(add_up_evens(&[3, 5, 8, 13])); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// invoked `unwrap_ok` macro with an `Err` value: OddError { at: 0, number: 3 }', src/macros/unwrapping.rs:6:18 /// | /// ``` #[macro_export] macro_rules! unwrap_ok { ($res:expr) => { match $res { $crate::__::Ok(x) => x, $crate::__::Err(e) => $crate::concat_panic(&[ &[$crate::PanicVal::write_str( "\ninvoked `unwrap_ok` macro with an `Err` value: ", )], &$crate::coerce_fmt!(e).to_panicvals($crate::FmtArg::DEBUG), ]), } }; } /// Gets the value in the `Err` variant. /// /// # Panics /// /// This panics if `$res` is an `Ok`, including the debug-formatted value in the panic message. /// /// # Example /// /// ```rust /// use const_panic::unwrap_err; /// /// type Res = Result; /// /// const ERR: &str = unwrap_err!(Res::Err("this is an error")); /// /// assert_eq!(ERR, "this is an error"); /// /// ``` /// /// ### Error /// /// This is what the compile-time error looks like when attempting to unwrap an `Ok`: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/unwrapping.rs:174:19 /// | /// 8 | const ERR: &str = unwrap_err!(Res::Ok(1234)); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// invoked `unwrap_err` macro with an `Ok` value: 1234', src/macros/unwrapping.rs:8:19 /// | /// ``` #[macro_export] macro_rules! unwrap_err { ($res:expr) => { match $res { $crate::__::Ok(x) => $crate::concat_panic(&[ &[$crate::PanicVal::write_str( "\ninvoked `unwrap_err` macro with an `Ok` value: ", )], &$crate::coerce_fmt!(x).to_panicvals($crate::FmtArg::DEBUG), ]), $crate::__::Err(e) => e, } }; } const_panic-0.2.13/src/macros.rs000064400000000000000000000315561046102023000146420ustar 00000000000000#[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", )) }; } const_panic-0.2.13/src/panic_val.rs000064400000000000000000000264511046102023000153100ustar 00000000000000use crate::{ fmt::{FmtArg, FmtKind, NumberFmt}, utils::{string_cap, Packed, PreFmtString, RangedBytes, Sign, TailShortString, WasTruncated}, }; #[cfg(feature = "non_basic")] use crate::{ array_string::TinyString, fmt::{IsLast, ShortString}, }; /// An opaque enum of the values that this crate knows how to format, /// along with some formatting metadata. /// /// This has constructor functions to make a `PanicVal` from: /// - `bool` /// - Integers /// - `&str` /// - Arrays/Slices of primitives (with the "non_basic" feature, enabled by default) /// - [`ShortString`](crate::fmt::ShortString) /// (with the "non_basic" feature, enabled by default) /// #[derive(Copy, Clone)] pub struct PanicVal<'a> { pub(crate) var: PanicVariant<'a>, } #[derive(Copy, Clone)] pub(crate) enum PanicVariant<'a> { Str(StrFmt, Packed<&'a str>), #[cfg(feature = "non_basic")] ShortString(StrFmt, TinyString<{ string_cap::TINY }>), PreFmt(PreFmtString), Int(IntVal), #[cfg(feature = "non_basic")] Slice(crate::slice_stuff::Slice<'a>), } pub(crate) enum PanicClass<'a> { PreFmt(RangedBytes<&'a [u8]>), Int(IntVal), #[cfg(feature = "non_basic")] Slice(crate::slice_stuff::Slice<'a>), } #[derive(Copy, Clone)] pub(crate) struct StrFmt { pub(crate) leftpad: u8, pub(crate) rightpad: u8, pub(crate) fmt_kind: FmtKind, } impl StrFmt { const DISPLAY: Self = Self { leftpad: 0, rightpad: 0, fmt_kind: FmtKind::Display, }; pub const fn new(fmtarg: FmtArg) -> Self { Self { leftpad: 0, rightpad: 0, fmt_kind: fmtarg.fmt_kind, } } } impl<'a> PanicVal<'a> { /// A `PanicVal` that formats to nothing. pub const EMPTY: Self = PanicVal::write_str(""); /// How many spaces are printed before this pub const fn leftpad(&self) -> u8 { use self::PanicVariant as PV; match self.var { PV::Str(strfmt, ..) => strfmt.leftpad, #[cfg(feature = "non_basic")] PV::ShortString(strfmt, ..) => strfmt.leftpad, _ => 0, } } /// How many spaces are printed after this pub const fn rightpad(&self) -> u8 { use self::PanicVariant as PV; match self.var { PV::Str(strfmt, ..) => strfmt.rightpad, #[cfg(feature = "non_basic")] PV::ShortString(strfmt, ..) => strfmt.rightpad, _ => 0, } } } macro_rules! mutate_strfmt { ($self:ident, |$strfmt:ident| $mutator:expr) => { match $self.var { PanicVariant::Str(mut $strfmt, str) => { $mutator; PanicVal { var: PanicVariant::Str($strfmt, str), } } #[cfg(feature = "non_basic")] PanicVariant::ShortString(mut $strfmt, str) => { $mutator; PanicVal { var: PanicVariant::ShortString($strfmt, str), } } var => PanicVal { var }, } }; } impl<'a> PanicVal<'a> { /// Sets the amount of spaces printed before this to `fmtarg.indentation`. /// /// Note that only strings can be padded. pub const fn with_leftpad(self, fmtarg: FmtArg) -> Self { mutate_strfmt! {self, |strfmt| strfmt.leftpad = fmtarg.indentation} } /// Sets the amount of spaces printed after this to `fmtarg.indentation`. /// /// Note that only strings can be padded. pub const fn with_rightpad(self, fmtarg: FmtArg) -> Self { mutate_strfmt! {self, |strfmt| strfmt.rightpad = fmtarg.indentation} } /// Constructs a PanicVal which outputs the contents of `string` verbatim. /// /// Equivalent to `PanicVal::from_str(string, FmtArg::DISPLAY)` pub const fn write_str(string: &'a str) -> Self { PanicVal { var: PanicVariant::Str(StrFmt::DISPLAY, Packed(string)), } } /// Constructs a PanicVal from a [`ShortString`], which outputs the string verbatim. #[cfg(feature = "non_basic")] pub const fn write_short_str(string: ShortString) -> Self { Self { var: PanicVariant::ShortString(StrFmt::DISPLAY, string.to_compact()), } } /// Constructs a `PanicVal` usable as a separator between fields or elements. /// /// This is sensitive to the [`fmtarg.is_alternate`] flag, /// for more details on that you can look at the docs for /// [`Separator::to_panicval`](crate::fmt::Separator#method.to_panicval) /// /// # Panics /// /// This panics if `string.len()` is greater than 12. /// /// [`fmtarg.is_alternate`]: crate::FmtArg#structfield.is_alternate #[cfg(feature = "non_basic")] pub const fn from_element_separator( separator: &str, is_last_field: IsLast, fmtarg: FmtArg, ) -> Self { let (concat, rightpad) = match (is_last_field, fmtarg.is_alternate) { (IsLast::No, false) => (ShortString::concat(&[separator, " "]), 0), (IsLast::Yes, false) => (ShortString::new(""), 0), (IsLast::No, true) => (ShortString::concat(&[separator, "\n"]), fmtarg.indentation), (IsLast::Yes, true) => (ShortString::concat(&[separator, "\n"]), 0), }; let strfmt = StrFmt { leftpad: 0, rightpad, fmt_kind: FmtKind::Display, }; Self { var: PanicVariant::ShortString(strfmt, concat.to_compact()), } } #[inline(always)] pub(crate) const fn __new(var: PanicVariant<'a>) -> Self { Self { var } } pub(crate) const fn to_class(&self) -> (StrFmt, PanicClass<'_>) { match &self.var { &PanicVariant::Str(strfmt, Packed(str)) => { let ranged = RangedBytes { start: 0, end: str.len(), bytes: str.as_bytes(), }; (strfmt, PanicClass::PreFmt(ranged)) } #[cfg(feature = "non_basic")] PanicVariant::ShortString(strfmt, str) => (*strfmt, PanicClass::PreFmt(str.ranged())), PanicVariant::PreFmt(str) => (StrFmt::DISPLAY, PanicClass::PreFmt(str.ranged())), PanicVariant::Int(int) => (StrFmt::DISPLAY, PanicClass::Int(*int)), #[cfg(feature = "non_basic")] PanicVariant::Slice(slice) => ( StrFmt::new(slice.fmtarg.unpack()), PanicClass::Slice(*slice), ), } } pub(crate) const fn to_class_truncated( &self, mut truncate_to: usize, ) -> (StrFmt, PanicClass<'_>, WasTruncated) { let (mut strfmt, class) = self.to_class(); if strfmt.leftpad as usize > truncate_to { return ( StrFmt { leftpad: strfmt.leftpad - truncate_to as u8, rightpad: 0, fmt_kind: FmtKind::Display, }, PanicClass::PreFmt(RangedBytes::EMPTY), WasTruncated::Yes(0), ); } else { truncate_to -= strfmt.leftpad as usize; }; let was_trunc: WasTruncated; let orig_len: usize; match class { PanicClass::PreFmt(str) => { was_trunc = if let PanicVariant::PreFmt(pfmt) = self.var { if pfmt.len() <= truncate_to { WasTruncated::No } else { WasTruncated::Yes(0) } } else { if let FmtKind::Display = strfmt.fmt_kind { crate::utils::truncated_str_len(str, truncate_to) } else { crate::utils::truncated_debug_str_len(str, truncate_to) } }; orig_len = str.len(); } PanicClass::Int(int) => { strfmt.fmt_kind = FmtKind::Display; was_trunc = if int.len() <= truncate_to { WasTruncated::No } else { WasTruncated::Yes(0) }; orig_len = int.len(); } #[cfg(feature = "non_basic")] PanicClass::Slice(_) => { was_trunc = WasTruncated::No; orig_len = 0; } } truncate_to -= was_trunc.get_length(orig_len); strfmt.rightpad = crate::utils::min_usize(strfmt.rightpad as usize, truncate_to) as u8; (strfmt, class, was_trunc) } } #[derive(Copy, Clone)] pub(crate) struct IntVal { sign: Sign, number_fmt: NumberFmt, is_alternate: bool, // the size of the integer in bits bits: u8, // the length of the integer in bytes, once written. len: u8, value: Packed, } impl IntVal { pub(crate) const fn from_u128(n: u128, bits: u8, f: FmtArg) -> PanicVal<'static> { Self::new(Sign::Positive, n, bits, f) } pub(crate) const fn from_i128(n: i128, bits: u8, f: FmtArg) -> PanicVal<'static> { let is_neg = if n < 0 { Sign::Negative } else { Sign::Positive }; Self::new(is_neg, n.unsigned_abs(), bits, f) } const fn new(sign: Sign, n: u128, bits: u8, fmtarg: FmtArg) -> PanicVal<'static> { use crate::int_formatting::compute_len; let len = compute_len(sign, n, bits, fmtarg); let this = IntVal { sign, number_fmt: fmtarg.number_fmt, is_alternate: fmtarg.is_alternate, bits, len, value: Packed(n), }; let var = if len as usize <= string_cap::PREFMT { PanicVariant::PreFmt(this.fmt::<{ string_cap::PREFMT }>()) } else { PanicVariant::Int(this) }; PanicVal { var } } pub(crate) const fn fmt(self) -> TailShortString { use crate::int_formatting::{fmt_binary, fmt_decimal, fmt_hexadecimal}; let IntVal { sign, number_fmt, is_alternate, len: _, bits, value: Packed(n), } = self; match number_fmt { NumberFmt::Decimal => fmt_decimal::(sign, n), NumberFmt::Binary => { let masked = apply_mask(sign, n, bits); fmt_binary::(masked, is_alternate) } NumberFmt::Hexadecimal => { let masked = apply_mask(sign, n, bits); fmt_hexadecimal::(masked, is_alternate) } } } pub(crate) const fn len(&self) -> usize { self.len as usize } } const fn apply_mask(sign: Sign, n: u128, bits: u8) -> u128 { if let Sign::Negative = sign { let mask: u128 = if bits == 128 { !0 } else { (1 << bits) - 1 }; (n as i128).wrapping_neg() as u128 & mask } else { n } } impl crate::PanicFmt for PanicVal<'_> { type This = Self; type Kind = crate::fmt::IsCustomType; const PV_COUNT: usize = 1; } impl<'a> PanicVal<'a> { /// Wraps this `PanicVal` in a single-element array. pub const fn to_panicvals(&self, _: FmtArg) -> [PanicVal<'a>; 1] { [*self] } /// Returns a copy of this `PanicVal`. pub const fn to_panicval(&self, _: FmtArg) -> PanicVal<'a> { *self } } const_panic-0.2.13/src/proc_macro_reexports/panicfmt_derive.rs000064400000000000000000000207701046102023000227500ustar 00000000000000/** Derives the [`PanicFmt`](trait@crate::PanicFmt) trait. This requires the `"derive"` feature, disabled by default. This generates a generic [`PanicFmt`](trait@crate::PanicFmt) impl, as well as one or more inherent `to_panicvals` method definitions [as described in the trait docs](trait@crate::PanicFmt#implementor). You can also use [`impl_panicfmt`] as an alternative that requires less time to compile from scratch, but requires repeating the type definition. [Jump straight to examples](#examples) # Limitations ### Type parameters Types with type parameters can't be generically formatted, to work around this you can use either or both of these attributes: - `#[pfmt(ignore(T))]`: if the type parameter(s) are only used in marker types (eg: `PhantomData`). - `#[pfmt(impl Foo)]`: to implement panic formatting with concrete type arguments (this attribute can be used multiple times to add impls). This limitation is caused by: - the lack of trait bound support in stable const fns. - the need to [have a concrete type argument](#concrete-pv-count) [example below](#type-parameter-example) ### Const parameters Const parameters must not affect the value of the `PanicFmt::PV_COUNT` of this type, since the const parameter [must be replaceable with a concrete value](#concrete-pv-count).
Note that arrays have a `PV_COUNT` of `1` for all lengths. ### Concrete `Self` type for `PanicFmt::PV_COUNT` The `to_panicvals` method that this macro generates roughly returns a ```text [PanicVal<'_>; ::PV_COUNT] ``` Because of limitations in stable const generics, the generic arguments of `Self` in the above code must be replaced with concrete arguments, requiring: - Lifetime arguments to be replaced with `'_` - Type arguments to be replaced with concrete types (usually `()` or the concrete types used in [`#[pfmt(impl ....)]`](#pfmt-impl-attr) attributes) - Const arguments to be replaced with concrete values (usually the default value for the type) # Attributes ### Container attributes Attributes used above the type definition. ### `#[pfmt(crate = foo::bar)]` Replaces the path to `const_panic` with `foo::bar` [example](#crate-example) ### `#[pfmt(debug_print)]`:
For diagnostics, causes the derive macro to panic with the code generated by it. ### `#[pfmt(display_fmt = fn_expression)]`: Calls the provided function when the deriving type is attempted to be Display-formatted. [example below](#display_fmt-example) ### `#[pfmt(panicvals_lower_bound = usize_expression)]`: Tells the derive the minimum amount of [`PanicVal`]s that this type needs, generally only useful in combination with the [`display_fmt`](#display_fmt-attr) attribute. ### `#[pfmt(ignored(T, C))]` Accepts the names of type and const parameters, replacing the generic arguments in [`here`](#concrete-pv-count) with a concrete value. For type parameters, this replaces the type parameter with `()` unless overriden, and also tells the derive not to require `T: PanicFmt` in the `PanicFmt` implementation for the deriving type (since the type parameter is not formatted). Const parameters are ignored by default, replacing them with the default value for that type [^1] The concrete value for each generic parameter can be overriden with `T = value` examples: - `#[pfmt(ignored(T))]` - `#[pfmt(ignored(T = u16))]` - `#[pfmt(ignored(T = u32, C))]` - `#[pfmt(ignored(T, C = 100))]` - `#[pfmt(ignored(U = str, A = false))]` ([more conplete example](#phantom-type-parameter-example)) [^1]: a private trait is used to get the default value for const parameters. ### `#[pfmt(impl Foo)]` Tells the derive to generate an inherent `to_panicvals` method for the type in the attribute (it must be the deriving type with concrete enough generic arguments). examples: - `#[pfmt(impl Foo)]` - `#[pfmt(impl Foo)]`: this also requires a [`#[pfmt(ignored(T))]`](#pfmt-ignored-attr) attribute ([more conplete example](#type-parameter-example)) # Examples ### Basic struct ```rust use const_panic::{FmtArg, PanicFmt}; assert_eq!( const_panic::concat_!(Foo { x: 3, y: &[3, 5, 8] }), "Foo { x: 3, y: [3, 5, 8] }", ); #[derive(PanicFmt)] struct Foo<'a> { x: u32, y: &'a [u8], } ``` ### Basic enum ```rust use const_panic::{FmtArg, PanicFmt}; assert_eq!(const_panic::concat_!(Foo::Bar), "Bar"); assert_eq!( const_panic::concat_!(Foo::Baz("hello", true)), "Baz(\"hello\", true)", ); #[derive(PanicFmt)] enum Foo { Bar, Baz(&'static str, bool), } ``` ### Type parameters This example demonstrates support for types with type parameters. ```rust use const_panic::{FmtArg, PanicFmt}; use std::marker::PhantomData; { const WITH_INT: Foo<&str, u8> = Foo { value: 100u8, _marker: PhantomData, }; assert_eq!( const_panic::concat_!(WITH_INT), "Foo { value: 100, _marker: PhantomData }", ); } { const WITH_STR: Foo = Foo { value: "hello", _marker: PhantomData, }; assert_eq!( const_panic::concat_!(WITH_STR), r#"Foo { value: "hello", _marker: PhantomData }"#, ); } #[derive(Debug, PanicFmt)] // Tells the derive that the `A` type parameter is not formatted, // removing the `A: PanicFmt` bound in `impl PanicFmt for Foo`, // and using `()` as the `A` type parmeter for // ` as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method. #[pfmt(ignore(A))] // Defines a `to_panicvals` method for `Foo` #[pfmt(impl Foo)] // Defines a `to_panicvals` method for `Foo` #[pfmt(impl Foo)] pub struct Foo { value: B, _marker: PhantomData, } ``` ### Phantom Type parameters This example demonstrates how type parameters can be ignored with `#[pfmt(ignore(...))]`. ```rust use const_panic::{FmtArg, PanicFmt}; use std::marker::PhantomData; { const WITH_INT: Foo = Foo{ value: 5, _marker: PhantomData, }; assert_eq!( const_panic::concat_!(WITH_INT), "Foo { value: 5, _marker: PhantomData }", ); } { const WITH_STR: Foo = Foo { value: 8, _marker: PhantomData, }; assert_eq!( const_panic::concat_!(WITH_STR), r#"Foo { value: 8, _marker: PhantomData }"#, ); } #[derive(Debug, PanicFmt)] // Tells the derive that the `A` and `B` type parameters are not formatted, // removing the `A: PanicFmt` and `B: PanicFmt` bounds in the `PanicFmt` impl for `Foo`, // and using `()` and `u8` as the `A` and `B` type parameters for // ` as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method. #[pfmt(ignore(A, B = u8))] pub struct Foo { value: u32, _marker: PhantomData<(PhantomData, B)>, } ``` ### Const-generic struct ```rust use const_panic::{FmtArg, PanicFmt}; assert_eq!(const_panic::concat_!(Foo([])), "Foo([])"); assert_eq!(const_panic::concat_!(Foo([3, 5, 8])), "Foo([3, 5, 8])"); #[derive(PanicFmt)] struct Foo([u8; LEN]); ``` ### Display formatting This example demonstrates the [`display_fmt` attribute](#display_fmt-attr) ```rust use const_panic::{FmtArg, PanicFmt, PanicVal}; assert_eq!(const_panic::concat_!(debug: Foo([3, 5, 8])), "Foo([3, 5, 8])"); assert_eq!(const_panic::concat_!(display: Foo([3, 5, 8])), "3 5 8"); #[derive(PanicFmt)] #[pfmt(display_fmt = Self::display_fmt)] // need this attribute to output more PanicVals in Display formatting than in Debug formatting. #[pfmt(panicvals_lower_bound = 10)] struct Foo([u8; 3]); impl Foo { const fn display_fmt(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::PV_COUNT] { let [a, b, c] = self.0; const_panic::flatten_panicvals!{fmtarg, Foo::PV_COUNT; a, " ", b, " ", c } } } ``` ### Crate renaming This example demonstrates how the `const_panic` crate can be renamed, passing the new name to the derive macro. ```rust # extern crate const_panic as cpanic; # extern crate std as const_panic; # use cpanic::{FmtArg, PanicFmt};; assert_eq!(cpanic::concat_!(Foo(Some(13))), "Foo(Some(13))"); #[derive(PanicFmt)] #[pfmt(crate = cpanic)] struct Foo(Option); ``` */ #[cfg_attr(feature = "docsrs", doc(cfg(feature = "derive")))] pub use const_panic_proc_macros::PanicFmt;const_panic-0.2.13/src/slice_stuff.rs000064400000000000000000000136111046102023000156540ustar 00000000000000use crate::{ fmt::{FmtArg, PackedFmtArg, PanicFmt}, panic_val::{PanicVal, PanicVariant}, utils::Packed, StdWrapper, }; macro_rules! impl_panicfmt_array { ($(($variant:ident, $panicval_ctor:ident, $ty:ty)),* $(,)*) => { #[derive(Copy, Clone)] #[repr(packed)] pub(crate) struct Slice<'s> { pub(crate) fmtarg: PackedFmtArg, pub(crate) vari: SliceV<'s>, } #[repr(u8)] #[derive(Copy, Clone)] pub(crate) enum SliceV<'s> { $( $variant(Packed<&'s [$ty]>), )* } impl<'s> Slice<'s> { // length in elements pub(crate) const fn arr_len(self) -> usize { match self.vari { $( SliceV::$variant(Packed(arr)) => arr.len(), )* } } } impl<'s> SliceV<'s> { const fn get(self, index: usize, fmtarg: FmtArg) -> PanicVal<'s> { match self { $( SliceV::$variant(Packed(arr)) => { let elem: &'s <$ty as PanicFmt>::This = &arr[index]; StdWrapper(elem).to_panicval(fmtarg) }, )* } } } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] impl<'s> PanicVal<'s> { $( /// Constructs a `PanicVal` from a slice. pub const fn $panicval_ctor(this: &'s [$ty], mut fmtarg: FmtArg) -> PanicVal<'s> { fmtarg = fmtarg.indent(); if this.is_empty() { fmtarg = fmtarg.set_alternate(false); } PanicVal::__new( PanicVariant::Slice(Slice{ fmtarg: fmtarg.pack(), vari: SliceV::$variant(Packed(this)), }) ) } )* } $( impl<'s> PanicFmt for [$ty] { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } impl<'s, const LEN: usize> PanicFmt for [$ty; LEN] { type This = Self; type Kind = crate::fmt::IsStdType; const PV_COUNT: usize = 1; } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] impl<'s> StdWrapper<&'s [$ty]> { /// Converts the slice to a single-element `PanicVal` array. pub const fn to_panicvals(self: Self, f:FmtArg) -> [PanicVal<'s>;1] { [PanicVal::$panicval_ctor(self.0, f)] } /// Converts the slice to a `PanicVal`. pub const fn to_panicval(self: Self, f:FmtArg) -> PanicVal<'s> { PanicVal::$panicval_ctor(self.0, f) } } #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] impl<'s, const LEN: usize> StdWrapper<&'s [$ty; LEN]> { /// Converts the array to a single-element `PanicVal` array. pub const fn to_panicvals(self: Self, f:FmtArg) -> [PanicVal<'s>;1] { [PanicVal::$panicval_ctor(self.0, f)] } /// Converts the array to a `PanicVal`. pub const fn to_panicval(self: Self, f:FmtArg) -> PanicVal<'s> { PanicVal::$panicval_ctor(self.0, f) } } )* }; } impl_panicfmt_array! { (U8, from_slice_u8, u8), (U16, from_slice_u16, u16), (U32, from_slice_u32, u32), (U64, from_slice_u64, u64), (U128, from_slice_u128, u128), (Usize, from_slice_usize, usize), (I8, from_slice_i8, i8), (I16, from_slice_i16, i16), (I32, from_slice_i32, i32), (I64, from_slice_i64, i64), (I128, from_slice_i128, i128), (Isize, from_slice_isize, isize), (Bool, from_slice_bool, bool), (Char, from_slice_char, char), (Str, from_slice_str, &'s str), } #[derive(Copy, Clone)] pub(crate) struct SliceIter<'s> { slice: SliceV<'s>, fmtarg: FmtArg, state: IterState, arr_len: u32, } #[derive(Copy, Clone, PartialEq, Eq)] struct IterState(u32); #[allow(non_upper_case_globals)] impl IterState { const Start: Self = Self(u32::MAX - 1); const End: Self = Self(u32::MAX); } impl<'s> Slice<'s> { pub(crate) const fn iter<'b>(&'b self) -> SliceIter<'s> { SliceIter { slice: self.vari, fmtarg: self.fmtarg.unpack(), state: IterState::Start, arr_len: self.arr_len() as u32, } } } impl<'s> SliceIter<'s> { pub(crate) const fn next(mut self) -> ([PanicVal<'s>; 2], Option) { let fmtarg = self.fmtarg; let ret = match self.state { IterState::Start => { self.state = if self.arr_len == 0 { IterState::End } else { IterState(0) }; [crate::fmt::OpenBracket.to_panicval(fmtarg), PanicVal::EMPTY] } IterState::End => { let close_brace = crate::fmt::CloseBracket.to_panicval(fmtarg.unindent()); return ([close_brace, PanicVal::EMPTY], None); } IterState(x) => { let comma = if x + 1 == self.arr_len { self.state = IterState::End; crate::fmt::COMMA_TERM } else { self.state = IterState(x + 1); crate::fmt::COMMA_SEP } .to_panicval(fmtarg); [self.slice.get(x as usize, fmtarg), comma] } }; (ret, Some(self)) } } const_panic-0.2.13/src/test_utils.rs000064400000000000000000000042641046102023000155510ustar 00000000000000use core::{ cmp::PartialEq, fmt::{self, Debug}, }; pub struct TestString { pub(crate) len: usize, pub(crate) buffer: [u8; LEN], } impl TestString { pub fn get(&self) -> &[u8] { &self.buffer[..self.len] } pub fn as_str(&self) -> &str { core::str::from_utf8(self.get()).unwrap() } } impl Debug for TestString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match core::str::from_utf8(self.get()) { Ok(x) => Debug::fmt(x, f), Err(_) => f .debug_struct("TestString") .field("len", &self.len) .field("buffer", &self.buffer) .finish(), } } } impl PartialEq for TestString { fn eq(&self, str: &str) -> bool { core::str::from_utf8(self.get()).map_or(false, |this| this == str) } } impl PartialEq<&str> for TestString { fn eq(&self, str: &&str) -> bool { self == *str } } #[doc(hidden)] #[macro_export] macro_rules! concat_fmt { ($length:expr, $max_len:expr; $($args:tt)*) => ( $crate::__concat_func_setup!{ (|args| $crate::for_tests::format_panic_message::<1024>(args, $length, $max_len)) [] [$($args)*,] } ) } // workaround for PhantomData changing to printing type argument pub struct MyPhantomData(core::marker::PhantomData); impl Copy for MyPhantomData {} impl Clone for MyPhantomData { fn clone(&self) -> Self { *self } } impl core::fmt::Debug for MyPhantomData { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.write_str("PhantomData") } } impl crate::PanicFmt for MyPhantomData { type This = Self; type Kind = crate::IsCustomType; const PV_COUNT: usize = 1; } impl MyPhantomData { pub const NEW: Self = Self(core::marker::PhantomData); pub const fn to_panicvals(self, _: crate::FmtArg) -> [crate::PanicVal<'static>; 1] { [crate::PanicVal::write_str("PhantomData")] } } const_panic-0.2.13/src/utils/non_basic_utils.rs000064400000000000000000000045511046102023000176640ustar 00000000000000use crate::{FmtArg, PanicVal}; /// For coercing a `&[PanicVal<'_>; LEN]` into a `&[PanicVal<'_>]`. pub const fn panicvals_id<'a, 'b, const LEN: usize>( array: &'b [PanicVal<'a>; LEN], ) -> &'b [PanicVal<'a>] { array } /// Flattens a `&[&[PanicVal<'a>]]` into a `[PanicVal<'a>; LEN]`. /// /// If `LEN` is greater than the amount of `PanicVal`s in the slices, /// this fills the remaining array with [`PanicVal::EMPTY`]. /// /// # Panics /// /// Panics if the amount of `PanicVal`s in the slices is greater than `LEN`. /// pub const fn flatten_panicvals<'a, const LEN: usize>( mut input: &[&[PanicVal<'a>]], ) -> [PanicVal<'a>; LEN] { let mut out = [PanicVal::EMPTY; LEN]; let mut len = 0usize; while let [mut outer, ref rinput @ ..] = *input { while let [arg, ref router @ ..] = *outer { out[len] = arg; len += 1; outer = router; } input = rinput } out } /// Gets the maximum value between `l` and `r` /// /// # Example /// /// ```rust /// use const_panic::utils::max_usize; /// /// assert_eq!(max_usize(5, 3), 5); /// assert_eq!(max_usize(5, 8), 8); /// /// ``` pub const fn max_usize(l: usize, r: usize) -> usize { if l > r { l } else { r } } /// Gets the maximum value in `slice`, returns `0` if the slice is empty. /// /// # Example /// /// ```rust /// use const_panic::utils::slice_max_usize; /// /// assert_eq!(slice_max_usize(&[]), 0); /// assert_eq!(slice_max_usize(&[3]), 3); /// assert_eq!(slice_max_usize(&[5, 3]), 5); /// assert_eq!(slice_max_usize(&[5, 8, 3]), 8); /// assert_eq!(slice_max_usize(&[5, 13, 8, 3]), 13); /// /// ``` pub const fn slice_max_usize(mut slice: &[usize]) -> usize { let mut max = 0; while let [x, ref rem @ ..] = *slice { max = max_usize(max, x); slice = rem; } max } #[doc(hidden)] #[track_caller] pub const fn assert_flatten_panicvals_length(expected_larger: usize, actual_value: usize) { if actual_value > expected_larger { crate::concat_panic(&[&[ PanicVal::write_str("length passed to flatten_panicvals macro ("), PanicVal::from_usize(expected_larger, FmtArg::DISPLAY), PanicVal::write_str(") is smaller than the computed length ("), PanicVal::from_usize(actual_value, FmtArg::DISPLAY), PanicVal::write_str(")"), ]]); } } const_panic-0.2.13/src/utils/utils_1_64_tests.rs000064400000000000000000000062361046102023000176260ustar 00000000000000use super::{tail_byte_array, StartAndBytes}; #[test] fn tail_byte_array_eq_gt_tests() { assert_eq!( tail_byte_array::<0>(0, &[3]), StartAndBytes { bytes: [], start: 0 } ); assert_eq!( tail_byte_array::<0>(0, &[3, 5]), StartAndBytes { bytes: [], start: 0 } ); assert_eq!( tail_byte_array::<1>(0, &[3, 5]), StartAndBytes { bytes: [0], start: 1 } ); assert_eq!( tail_byte_array::<1>(1, &[3, 5]), StartAndBytes { bytes: [3], start: 0 } ); assert_eq!( tail_byte_array::<2>(0, &[3, 5]), StartAndBytes { bytes: [0, 0], start: 2 } ); assert_eq!( tail_byte_array::<2>(1, &[3, 5]), StartAndBytes { bytes: [0, 3], start: 1 } ); assert_eq!( tail_byte_array::<2>(2, &[3, 5]), StartAndBytes { bytes: [3, 5], start: 0 } ); assert_eq!( tail_byte_array::<1>(0, &[]), StartAndBytes { bytes: [0], start: 1 } ); assert_eq!( tail_byte_array::<2>(0, &[]), StartAndBytes { bytes: [0, 0], start: 2 } ); assert_eq!( tail_byte_array::<1>(1, &[3]), StartAndBytes { bytes: [3], start: 0 } ); assert_eq!( tail_byte_array::<2>(1, &[3]), StartAndBytes { bytes: [0, 3], start: 1 } ); assert_eq!( tail_byte_array::<3>(1, &[3]), StartAndBytes { bytes: [0, 0, 3], start: 2 } ); assert_eq!( tail_byte_array::<2>(1, &[3, 5]), StartAndBytes { bytes: [0, 3], start: 1 } ); assert_eq!( tail_byte_array::<3>(1, &[3, 5]), StartAndBytes { bytes: [0, 0, 3], start: 2 } ); assert_eq!( tail_byte_array::<4>(1, &[3, 5]), StartAndBytes { bytes: [0, 0, 0, 3], start: 3 } ); assert_eq!( tail_byte_array::<2>(2, &[3, 5]), StartAndBytes { bytes: [3, 5], start: 0 } ); assert_eq!( tail_byte_array::<3>(2, &[3, 5]), StartAndBytes { bytes: [0, 3, 5], start: 1 } ); assert_eq!( tail_byte_array::<4>(2, &[3, 5]), StartAndBytes { bytes: [0, 0, 3, 5], start: 2 } ); } #[test] #[should_panic] fn tail_byte_array_smaller_test_0() { let _: StartAndBytes<0> = tail_byte_array(2, &[1]); } #[test] #[should_panic] fn tail_byte_array_smaller_test_1() { let _: StartAndBytes<1> = tail_byte_array(3, &[1, 2]); } #[test] #[should_panic] fn tail_byte_array_smaller_test_2() { let _: StartAndBytes<1> = tail_byte_array(3, &[1, 2, 3]); } #[test] #[should_panic] fn tail_byte_array_smaller_test_3() { let _: StartAndBytes<2> = tail_byte_array(3, &[1, 2, 3, 4, 5, 6]); } const_panic-0.2.13/src/utils.rs000064400000000000000000000153651046102023000145160ustar 00000000000000//! Utility functions use crate::debug_str_fmt::ForEscaping; #[cfg(feature = "rust_1_64")] #[cfg(test)] mod utils_1_64_tests; #[cfg(feature = "non_basic")] mod non_basic_utils; #[cfg(feature = "non_basic")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))] pub use self::non_basic_utils::*; /// Computes the minimum of `l` and `r` pub const fn min_usize(l: usize, r: usize) -> usize { if l < r { l } else { r } } /// Computes the maximum of `l` and `r` pub const fn max_usize(l: usize, r: usize) -> usize { if l > r { l } else { r } } #[derive(Copy, Clone)] pub(crate) struct TailShortString { start: u8, buffer: [u8; LEN], } pub(crate) type PreFmtString = TailShortString<{ string_cap::PREFMT }>; pub(crate) mod string_cap { /// The capacity of a [`ShortString`](crate::fmt::ShortString). #[cfg(feature = "non_basic")] pub const TINY: usize = 16; // the TailShortString that's stored in PanicVal pub(crate) const PREFMT: usize = 21; // length of string to alternate binary format a 64 bit integer pub(crate) const MEDIUM: usize = 66; // length of string to alternate binary format a 128 bit integer pub(crate) const LARGE: usize = 130; } impl TailShortString { /// /// # Safety /// /// `buffer` must be valid utf8 starting from the `start` index. #[inline(always)] pub(crate) const unsafe fn new(start: u8, buffer: [u8; LEN]) -> Self { Self { start, buffer } } pub(crate) const fn len(&self) -> usize { LEN - self.start as usize } pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> { RangedBytes { start: self.start as usize, end: LEN, bytes: &self.buffer, } } } //////////////////////////////////////////////////////// #[repr(packed)] #[derive(Copy)] pub(crate) struct Packed(pub(crate) T); impl Clone for Packed { fn clone(&self) -> Self { *self } } //////////////////////////////////////////////////////// #[derive(Copy, Clone)] pub(crate) struct RangedBytes { pub(crate) start: usize, pub(crate) end: usize, pub(crate) bytes: B, } impl RangedBytes { pub(crate) const fn len(&self) -> usize { self.end - self.start } } impl RangedBytes<&'static [u8]> { pub const EMPTY: Self = RangedBytes { start: 0, end: 0, bytes: &[], }; } //////////////////////////////////////////////////////// #[derive(Copy, Clone)] pub(crate) enum Sign { Positive, Negative = 1, } #[derive(Copy, Clone)] pub(crate) enum WasTruncated { Yes(usize), No, } impl WasTruncated { pub(crate) const fn get_length(self, len: usize) -> usize { match self { WasTruncated::Yes(x) => x, WasTruncated::No => len, } } } const fn is_char_boundary(b: u8) -> bool { (b as i8) >= -0x40 } // truncates a utf8-encoded string to the character before the `truncate_to` index // pub(crate) const fn truncated_str_len( ranged: RangedBytes<&[u8]>, truncate_to: usize, ) -> WasTruncated { if ranged.len() <= truncate_to { WasTruncated::No } else { let mut i = ranged.start + truncate_to; while i != ranged.start { // if it's a non-continuation byte, break if is_char_boundary(ranged.bytes[i]) { break; } i -= 1; } WasTruncated::Yes(i - ranged.start) } } pub(crate) const fn truncated_debug_str_len( ranged: RangedBytes<&[u8]>, truncate_to: usize, ) -> WasTruncated { let blen = ranged.end; // `* 4` because the longest escape is written like `\xNN` which is 4 bytes // `+ 2` for the quote characters if blen * 4 + 2 <= truncate_to { WasTruncated::No } else if truncate_to == 0 { WasTruncated::Yes(0) } else { let mut i = ranged.start; // = 1 for opening quote char let mut fmtlen = 1; loop { let next_i = next_char_boundary(ranged, min_usize(i + 1, ranged.end)); let mut j = i; while j < next_i { fmtlen += ForEscaping::byte_len(ranged.bytes[j]); j += 1; } if fmtlen > truncate_to { break; } else if next_i == ranged.end { i = next_i; break; } else { i = next_i; } } if i == blen && fmtlen < truncate_to { WasTruncated::No } else { WasTruncated::Yes(i - ranged.start) } } } const fn next_char_boundary(ranged: RangedBytes<&[u8]>, mut i: usize) -> usize { while i < ranged.end && !is_char_boundary(ranged.bytes[i]) { i += 1; } i } #[cfg_attr(feature = "test", derive(Debug, PartialEq))] pub(crate) struct StartAndBytes { pub start: u8, pub bytes: [u8; LEN], } #[track_caller] pub(crate) const fn tail_byte_array( len: usize, input: &[u8], ) -> StartAndBytes { assert!(len <= TO); let mut bytes = [0u8; TO]; let start = TO - len; let mut i = start; let mut j = 0; while j < len { bytes[i] = input[j]; i += 1; j += 1; } assert!(start < 256); StartAndBytes { start: start as u8, bytes, } } //////////////////////////////////////////////////////////////////////////////// /// Const equivalent of `&buffer[..upto]` with saturating indexing. /// /// "saturating indexing" means that if `upto > buffer.len()`, /// then this returns all of `buffer` instead of panicking. /// /// # Example /// /// ```rust /// use const_panic::utils::bytes_up_to; /// /// const BYTES: &[u8] = &[3, 5, 8, 13, 21, 34, 55, 89]; /// /// const SLICE: &[u8] = bytes_up_to(BYTES, 4); /// assert_eq!(SLICE, &[3, 5, 8, 13][..]); /// /// const WHOLE: &[u8] = bytes_up_to(BYTES, usize::MAX); /// assert_eq!(WHOLE, &[3, 5, 8, 13, 21, 34, 55, 89][..]); /// /// ``` pub const fn bytes_up_to(buffer: &[u8], upto: usize) -> &[u8] { if upto > buffer.len() { return buffer; } #[cfg(not(feature = "rust_1_64"))] { let mut to_truncate = buffer.len() - upto; let mut out: &[u8] = buffer; while to_truncate != 0 { if let [rem @ .., _] = out { out = rem; } to_truncate -= 1; } if out.len() != upto { panic!("BUG!") } out } #[cfg(feature = "rust_1_64")] { // SAFETY: the above conditional ensures that `upto` doesn't // create a partially-dangling slice unsafe { core::slice::from_raw_parts(buffer.as_ptr(), upto) } } } const_panic-0.2.13/src/wrapper.rs000064400000000000000000000021071046102023000150240ustar 00000000000000use crate::PanicFmt; /// A wrapper type used to define methods for std types. /// /// Std types are coerced to this type through the approach used in the /// [`coerce_fmt`] macro. /// /// # Example /// /// Formatting std types with this type's `to_panicvals` methods, /// without using macros. /// #[cfg_attr(feature = "non_basic", doc = "```rust")] #[cfg_attr(not(feature = "non_basic"), doc = "```ignore")] /// use const_panic::{ArrayString, FmtArg, StdWrapper}; /// /// assert_eq!( /// ArrayString::<99>::from_panicvals( /// &StdWrapper("hello").to_panicvals(FmtArg::DEBUG) /// ).unwrap(), /// r#""hello""# /// ); /// /// assert_eq!( /// ArrayString::<99>::from_panicvals( /// &StdWrapper(&[3u8, 5, 8]).to_panicvals(FmtArg::ALT_DEBUG) /// ).unwrap(), /// "[\n 3,\n 5,\n 8,\n]" /// ); /// /// ``` #[derive(Copy, Clone)] #[repr(transparent)] pub struct StdWrapper(pub T); impl PanicFmt for StdWrapper where T: PanicFmt, { type This = Self; type Kind = crate::fmt::IsCustomType; const PV_COUNT: usize = T::PV_COUNT; }