Skip to main content

const_format/
lib.rs

1//! Compile-time string formatting.
2//!
3//! This crate provides types and macros for formatting strings at compile-time.
4//!
5//! # Rust versions
6//!
7//! There are some features that require a variety of Rust versions,
8//! the sections below describe the features that are available for each version.
9//!
10//! ### Rust 1.71.0
11//!
12//! These macros are available in Rust 1.71.0:
13//!
14//! - [`concatcp`]:
15//! Concatenates `integers`, `bool`, `char`, and `&str` constants into a `&'static str` constant.
16//!
17//! - [`formatcp`]:
18//! [`format`]-like formatting which takes `integers`, `bool`, `char`, and `&str` constants,
19//! and emits a `&'static str` constant.
20//!
21//! - [`str_get`]:
22//! Indexes a `&'static str` constant, returning `None` when the index is out of bounds.
23//!
24//! - [`str_index`]:
25//! Indexes a `&'static str` constant.
26//!
27//! - [`str_repeat`]:
28//! Creates a `&'static str` by repeating a `&'static str` constant `times` times.
29//!
30//! - [`str_splice`]:
31//! Replaces a substring in a `&'static str` constant.
32//!
33//! - [`map_ascii_case`]:
34//! Converts a `&'static str` constant to a different casing style,
35//! determined by a [`Case`] argument.
36//!
37//! - [`str_replace`]:
38//! Replaces all the instances of a pattern in a `&'static str` constant with
39//! another `&'static str` constant.
40//!
41//! - [`str_split`]:
42//! Splits a string constant
43//!
44//! The `"assertcp"` feature enables the [`assertcp`], [`assertcp_eq`],
45//! and [`assertcp_ne`] macros.
46//! These macros are like the standard library assert macros,
47//! but evaluated at compile-time,
48//! with the limitation that they can only have primitive types as arguments
49//! (just like [`concatcp`] and [`formatcp`]).
50//!
51//! ### Rust 1.83.0
52//!
53//! By enabling the "fmt" feature, you can use a [`std::fmt`]-like API.
54//!
55//! This requires Rust 1.83.0, because it uses mutable references in const fn.
56//!
57//! All the other features of this crate are implemented on top of the [`const_format::fmt`] API:
58//!
59//! - [`concatc`]:
60//! Concatenates many standard library and user defined types into a `&'static str` constant.
61//!
62//! - [`formatc`]:
63//! [`format`]-like macro that can format many standard library and user defined types into
64//! a `&'static str` constant.
65//!
66//! - [`writec`]:
67//! [`write`]-like macro that can format many standard library and user defined types
68//! into a type that implements [`WriteMarker`].
69//!
70//! The `"derive"` feature enables the [`ConstDebug`] macro,
71//! and the `"fmt"` feature.<br>
72//! [`ConstDebug`] derives the [`FormatMarker`] trait,
73//! and implements an inherent `const_debug_fmt` method for compile-time debug formatting.
74//!
75//! The `"assertc"` feature enables the [`assertc`], [`assertc_eq`], [`assertc_ne`] macros,
76//! and the `"fmt"` feature.<br>
77//! These macros are like the standard library assert macros, but evaluated at compile-time.
78//!
79//! # Examples
80//!
81//! ### Concatenation of primitive types
82//!
83//! ```rust
84//! use const_format::concatcp;
85//!
86//! const NAME: &str = "Bob";
87//! const FOO: &str = concatcp!(NAME, ", age ", 21u8,"!");
88//!
89//! assert_eq!(FOO, "Bob, age 21!");
90//! ```
91//!
92//! ### Formatting primitive types
93//!
94//! ```rust
95//! use const_format::formatcp;
96//!
97//! const NAME: &str = "John";
98//!
99//! const FOO: &str = formatcp!("{NAME}, age {}!", compute_age(NAME));
100//!
101//! assert_eq!(FOO, "John, age 24!");
102//!
103//! # const fn compute_age(s: &str) -> usize { s.len() * 6 }
104//!
105//! ```
106//!
107//! ### Formatting custom types
108//!
109//! This example demonstrates how you can use the [`ConstDebug`] derive macro,
110//! and then format the type into a `&'static str` constant.
111//!
112//! This example requires the `"derive"` feature
113//! and Rust 1.83.0, because it uses `&mut` in a const context.
114//!
115#![cfg_attr(feature = "derive", doc = "```rust")]
116#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
117//!
118//! use const_format::{ConstDebug, formatc};
119//!
120//! #[derive(ConstDebug)]
121//! struct Message{
122//!     ip: [Octet; 4],
123//!     value: &'static str,
124//! }
125//!
126//! #[derive(ConstDebug)]
127//! struct Octet(u8);
128//!
129//! const MSG: Message = Message{
130//!     ip: [Octet(127), Octet(0), Octet(0), Octet(1)],
131//!     value: "Hello, World!",
132//! };
133//!
134//! const FOO: &str = formatc!("{:?}", MSG);
135//!
136//! assert_eq!(
137//!     FOO,
138//!     "Message { ip: [Octet(127), Octet(0), Octet(0), Octet(1)], value: \"Hello, World!\" }"
139//! );
140//!
141//! ```
142//!
143//! ### Formatted const assertions
144//!
145//! This example demonstrates how you can use the [`assertcp_ne`] macro to
146//! do compile-time inequality assertions with formatted error messages.
147//!
148//! This requires the `"assertcp"` feature.
149//!
150#![cfg_attr(feature = "assertcp", doc = "```compile_fail")]
151#![cfg_attr(not(feature = "assertcp"), doc = "```ignore")]
152//! use const_format::assertcp_ne;
153//!
154//! macro_rules! check_valid_pizza{
155//!     ($user:expr, $topping:expr) => {
156//!         assertcp_ne!(
157//!             $topping,
158//!             "pineapple",
159//!             "You can't put pineapple on pizza, {}",
160//!             $user,
161//!         );
162//!     }
163//! }
164//!
165//! check_valid_pizza!("John", "salami");
166//! check_valid_pizza!("Dave", "sausage");
167//! check_valid_pizza!("Bob", "pineapple");
168//!
169//! # fn main(){}
170//! ```
171//!
172//! This is the compiler output:
173//!
174//! ```text
175//! error[E0080]: evaluation of constant value failed
176//!   --> src/lib.rs:178:27
177//!    |
178//! 20 | check_valid_pizza!("Bob", "pineapple");
179//!    |                           ^^^^^^^^^^^ the evaluated program panicked at '
180//! assertion failed: `(left != right)`
181//!  left: `"pineapple"`
182//! right: `"pineapple"`
183//! You can't put pineapple on pizza, Bob
184//! ', src/lib.rs:20:27
185//!
186//!
187//! ```
188//!
189//! <div id="macro-limitations"></div>
190//!
191//! # Limitations
192//!
193//! All of the macros from `const_format` have these limitations:
194//!
195//! - The formatting macros that expand to
196//! `&'static str`s can only use constants from concrete types,
197//! so while a `Type::<u8>::FOO` argument would be fine,
198//! `Type::<T>::FOO` would not be (`T` being a type parameter).
199//!
200//! - Integer arguments must have a type inferrable from context,
201//! [more details in the Integer arguments section](#integer-args).
202//!
203//! - They cannot be used places that take string literals.
204//! So `#[doc = "foobar"]` cannot be replaced with `#[doc = concatcp!("foo", "bar") ]`.
205//!
206//! <span id="integer-args"></span>
207//!
208//! ### Integer arguments
209//!
210//! Integer arguments must have a type inferrable from context.
211//! so if you only pass an integer literal it must have a suffix.
212//!
213//! Example of what does compile:
214//!
215//! ```rust
216//! const N: u32 = 1;
217//! assert_eq!(const_format::concatcp!(N + 1, 2 + N), "23");
218//!
219//! assert_eq!(const_format::concatcp!(2u32, 2 + 1u8, 3u8 + 1), "234");
220//! ```
221//!
222//! Example of what does not compile:
223//! ```compile_fail
224//! assert_eq!(const_format::concatcp!(1 + 1, 2 + 1), "23");
225//! ```
226//!
227//! # Renaming crate
228//!
229//! All function-like macros from `const_format` can be used when the crate is renamed.
230//!
231//! The [`ConstDebug`] derive macro has the `#[cdeb(crate = "foo::bar")]` attribute to
232//! tell it where to find the `const_format` crate.
233//!
234//! Example of renaming the `const_format` crate in the Cargo.toml file:
235//! ```toml
236//! [dependencies]
237//! cfmt = {version = "0.*", package = "const_format"}
238//! ```
239//!
240//! # Cargo features
241//!
242//! - `"fmt"`: Enables the [`std::fmt`]-like API and `"rust_1_83"` feature,
243//! requires Rust 1.83.0 because it uses mutable references in const fn.<br>
244//! This feature includes the [`formatc`]/[`writec`] formatting macros.
245//!
246//! - `"derive"`: requires Rust 1.83.0, implies the `"fmt"` feature,
247//! provides the [`ConstDebug`] derive macro to format user-defined types at compile-time.<br>
248//! This implicitly uses the `syn` crate, so clean compiles take a bit longer than without the feature.
249//!
250//! - `"assertc"`: requires Rust 1.83.0, implies the `"fmt"` feature,
251//! enables the [`assertc`], [`assertc_eq`], and [`assertc_ne`] assertion macros.<br>
252//! This feature was previously named `"assert"`,
253//! but it was renamed to avoid confusion with the `"assertcp"` feature.
254//!
255//! - `"assertcp"`:
256//! Enables the [`assertcp`], [`assertcp_eq`], and [`assertcp_ne`] assertion macros.
257//!
258//! - `"rust_1_83"`:
259//! Makes macros that evaluate to a value become compatible with [inline const patterns].
260//!
261//! # No-std support
262//!
263//! `const_format` is unconditionally `#![no_std]`, it can be used anywhere Rust can be used.
264//!
265//! # Minimum Supported Rust Version
266//!
267//! `const_format` requires Rust 1.71.0.
268//!
269//! Features that require newer versions of Rust, or the nightly compiler,
270//! need to be explicitly enabled with cargo features.
271//!
272//!
273//! [`assertc`]: ./macro.assertc.html
274//!
275//! [`assertc_eq`]: ./macro.assertc_eq.html
276//!
277//! [`assertc_ne`]: ./macro.assertc_ne.html
278//!
279//! [`assertcp`]: ./macro.assertcp.html
280//!
281//! [`assertcp_eq`]: ./macro.assertcp_eq.html
282//!
283//! [`assertcp_ne`]: ./macro.assertcp_ne.html
284//!
285//! [`concatcp`]: ./macro.concatcp.html
286//!
287//! [`formatcp`]: ./macro.formatcp.html
288//!
289//! [`format`]: https://doc.rust-lang.org/std/macro.format.html
290//!
291//! [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
292//!
293//! [`const_format::fmt`]: ./fmt/index.html
294//!
295//! [`concatc`]: ./macro.concatc.html
296//!
297//! [`formatc`]: ./macro.formatc.html
298//!
299//! [`writec`]: ./macro.writec.html
300//!
301//! [`write`]: https://doc.rust-lang.org/std/macro.write.html
302//!
303//! [`Formatter`]: ./fmt/struct.Formatter.html
304//!
305//! [`StrWriter`]: ./fmt/struct.StrWriter.html
306//!
307//! [`ConstDebug`]: ./derive.ConstDebug.html
308//!
309//! [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html
310//!
311//! [`WriteMarker`]: ./marker_traits/trait.WriteMarker.html
312//!
313//! [`map_ascii_case`]: ./macro.map_ascii_case.html
314//!
315//! [`Case`]: ./enum.Case.html
316//!
317//!
318//! [`str_get`]: ./macro.str_get.html
319//!
320//! [`str_index`]: ./macro.str_index.html
321//!
322//! [`str_repeat`]: ./macro.str_repeat.html
323//!
324//! [`str_splice`]: ./macro.str_splice.html
325//!
326//! [`str_replace`]: ./macro.str_replace.html
327//!
328//! [`str_split`]: ./macro.str_split.html
329//!
330//! [`str::replace`]: https://doc.rust-lang.org/std/primitive.str.html#method.replace
331//!
332//! [inline const patterns]: https://doc.rust-lang.org/1.83.0/unstable-book/language-features/inline-const-pat.html
333//!
334#![no_std]
335#![cfg_attr(feature = "__docsrs", feature(doc_cfg))]
336#![deny(rust_2018_idioms)]
337// This lint is silly
338#![allow(clippy::blacklisted_name)]
339// This lint is silly
340#![allow(clippy::needless_doctest_main)]
341#![deny(clippy::missing_safety_doc)]
342#![deny(clippy::shadow_unrelated)]
343#![deny(clippy::wildcard_imports)]
344// All The methods that take self by value are for small Copy types
345#![allow(clippy::wrong_self_convention)]
346#![allow(clippy::init_numbered_fields)]
347#![deny(missing_docs)]
348
349include! {"const_debug_derive.rs"}
350
351#[macro_use]
352mod macros;
353
354mod formatting;
355
356#[cfg(feature = "assertc")]
357mod equality;
358
359#[doc(hidden)]
360#[cfg(feature = "assertcp")]
361#[macro_use]
362pub mod for_assert_macros;
363
364mod char_encoding;
365
366mod pargument;
367
368mod const_generic_concatcp;
369
370#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
371#[cfg(feature = "fmt")]
372pub mod utils;
373
374#[doc(hidden)]
375#[cfg(any(feature = "fmt", feature = "assertcp"))]
376mod slice_cmp;
377
378#[doc(hidden)]
379pub mod __hidden_utils;
380
381#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
382#[cfg(feature = "fmt")]
383pub mod for_examples;
384
385#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
386#[cfg(feature = "fmt")]
387pub mod marker_traits;
388
389#[cfg(feature = "__test")]
390pub mod test_utils;
391
392#[cfg(feature = "__test")]
393#[allow(missing_docs)]
394pub mod doctests;
395
396#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
397#[cfg(feature = "fmt")]
398pub mod fmt;
399
400#[cfg(feature = "fmt")]
401#[doc(hidden)]
402pub mod msg;
403
404#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
405#[cfg_attr(not(feature = "fmt"), doc(hidden))]
406pub mod wrapper_types;
407
408#[doc(hidden)]
409pub mod __ascii_case_conv;
410
411#[doc(hidden)]
412pub mod __str_methods;
413
414pub use __str_methods::SplicedStr;
415
416pub use __ascii_case_conv::Case;
417
418#[cfg(feature = "fmt")]
419#[doc(no_inline)]
420pub use crate::fmt::{Error, Formatter, FormattingFlags, Result, StrWriter, StrWriterMut};
421
422#[cfg(feature = "fmt")]
423pub use crate::wrapper_types::ascii_str::AsciiStr;
424
425#[cfg(feature = "fmt")]
426pub use crate::wrapper_types::sliced::Sliced;
427
428#[cfg_attr(not(feature = "fmt"), doc(hidden))]
429pub use crate::wrapper_types::pwrapper::PWrapper;
430
431#[doc(hidden)]
432#[allow(non_snake_case)]
433pub mod __cf_osRcTFl4A {
434    pub use crate::*;
435}
436
437#[doc(hidden)]
438pub mod pmr {
439    pub use {bool, str, u8, usize};
440
441    pub use const_format_proc_macros::{__concatcp_impl, __formatcp_impl, respan_to};
442
443    #[cfg(feature = "fmt")]
444    pub use const_format_proc_macros::{__formatc_if_impl, __formatc_impl, __writec_impl};
445
446    #[cfg(feature = "assertcp")]
447    pub use const_format_proc_macros::__formatcp_if_impl;
448
449    pub use core::{
450        cmp::Reverse,
451        convert::identity,
452        mem::transmute,
453        num::Wrapping,
454        ops::Range,
455        option::Option::{self, None, Some},
456        result::Result::{self, Err, Ok},
457    };
458
459    pub use crate::const_generic_concatcp::__priv_concatenate;
460
461    #[cfg(feature = "assertcp")]
462    pub use crate::for_assert_macros::{assert_, ConcatArgsIf};
463
464    #[cfg(feature = "fmt")]
465    pub use crate::{
466        fmt::{ComputeStrLength, Error, Formatter, StrWriter, StrWriterMut, ToResult},
467        marker_traits::{
468            FormatMarker, IsAFormatMarker, IsAWriteMarker, IsNotStdKind, IsStdKind, WriteMarker,
469        },
470    };
471
472    pub use crate::{
473        formatting::{
474            hex_as_ascii, ForEscaping, Formatting, FormattingFlags, HexFormatting, LenAndArray,
475            NumberFormatting, StartAndArray, FOR_ESCAPING,
476        },
477        pargument::{PArgument, PConvWrapper, PVariant},
478        wrapper_types::PWrapper,
479    };
480
481    #[doc(hidden)]
482    #[repr(transparent)]
483    pub struct __AssertStr {
484        pub x: &'static str,
485    }
486
487    #[doc(hidden)]
488    #[repr(transparent)]
489    pub struct __AssertType<T> {
490        pub x: T,
491    }
492}
493
494#[cfg(all(feature = "__test", feature = "derive", feature = "assertcp"))]
495identity! {
496    #[doc = include_str!("../../README.md")]
497    pub struct ReadmeTest;
498}
499
500#[cfg(all(test, not(feature = "__test")))]
501compile_error! { "tests must be run with the \"__test\" feature" }