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