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