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