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 | |
355 | include! {"const_debug_derive.rs" } |
356 | |
357 | #[macro_use ] |
358 | mod macros; |
359 | |
360 | mod formatting; |
361 | |
362 | #[cfg (feature = "assertc" )] |
363 | mod equality; |
364 | |
365 | #[doc (hidden)] |
366 | #[cfg (feature = "assertcp" )] |
367 | #[macro_use ] |
368 | pub mod for_assert_macros; |
369 | |
370 | mod char_encoding; |
371 | |
372 | mod pargument; |
373 | |
374 | mod const_generic_concatcp; |
375 | |
376 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "fmt" )))] |
377 | #[cfg (feature = "fmt" )] |
378 | pub mod utils; |
379 | |
380 | #[doc (hidden)] |
381 | #[cfg (any(feature = "fmt" , feature = "assertcp" ))] |
382 | mod slice_cmp; |
383 | |
384 | #[doc (hidden)] |
385 | pub mod __hidden_utils; |
386 | |
387 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "fmt" )))] |
388 | #[cfg (feature = "fmt" )] |
389 | pub mod for_examples; |
390 | |
391 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "fmt" )))] |
392 | #[cfg (feature = "fmt" )] |
393 | pub mod marker_traits; |
394 | |
395 | #[cfg (feature = "__test" )] |
396 | pub mod test_utils; |
397 | |
398 | #[cfg (feature = "__test" )] |
399 | #[allow (missing_docs)] |
400 | pub mod doctests; |
401 | |
402 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "fmt" )))] |
403 | #[cfg (feature = "fmt" )] |
404 | pub mod fmt; |
405 | |
406 | #[cfg (feature = "fmt" )] |
407 | #[doc (hidden)] |
408 | pub mod msg; |
409 | |
410 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "fmt" )))] |
411 | #[cfg_attr (not(feature = "fmt" ), doc(hidden))] |
412 | pub mod wrapper_types; |
413 | |
414 | #[doc (hidden)] |
415 | pub mod __ascii_case_conv; |
416 | |
417 | #[doc (hidden)] |
418 | pub mod __str_methods; |
419 | |
420 | pub use __str_methods::SplicedStr; |
421 | |
422 | pub use __ascii_case_conv::Case; |
423 | |
424 | #[cfg (feature = "fmt" )] |
425 | #[doc (no_inline)] |
426 | pub use crate::fmt::{Error, Formatter, FormattingFlags, Result, StrWriter, StrWriterMut}; |
427 | |
428 | #[cfg (feature = "fmt" )] |
429 | pub use crate::wrapper_types::ascii_str::AsciiStr; |
430 | |
431 | #[cfg (feature = "fmt" )] |
432 | pub use crate::wrapper_types::sliced::Sliced; |
433 | |
434 | #[cfg_attr (not(feature = "fmt" ), doc(hidden))] |
435 | pub use crate::wrapper_types::pwrapper::PWrapper; |
436 | |
437 | #[doc (hidden)] |
438 | #[allow (non_snake_case)] |
439 | pub mod __cf_osRcTFl4A { |
440 | pub use crate::*; |
441 | } |
442 | |
443 | #[doc (hidden)] |
444 | pub 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" ))] |
495 | identity! { |
496 | #[doc = include_str!("../../README.md" )] |
497 | pub struct ReadmeTest; |
498 | } |
499 | |
500 | #[cfg (all(test, not(feature = "__test" )))] |
501 | compile_error! { "tests must be run with the \"__test \" feature" } |
502 | |