const_format-0.2.32/.cargo_vcs_info.json0000644000000001520000000000100136140ustar { "git": { "sha1": "cc41c595d721f42b02e23d970fb40b17615f0aa6" }, "path_in_vcs": "const_format" }const_format-0.2.32/Cargo.toml0000644000000036700000000000100116220ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.57.0" name = "const_format" version = "0.2.32" authors = ["rodrimati1992 "] include = [ "Cargo.toml", "src/**/*.rs", "../README.md", "LICENSE-ZLIB.md", ] description = "Compile-time string formatting" documentation = "https://docs.rs/const_format/" readme = "README.md" keywords = [ "no-std", "format", "concat", ] categories = [ "no-std", "text-processing", ] license = "Zlib" repository = "https://github.com/rodrimati1992/const_format_crates/" resolver = "1" [package.metadata.docs.rs] features = [ "all", "__docsrs", ] [dependencies.const_format_proc_macros] version = "=0.2.32" [dependencies.konst] version = "0.2.13" optional = true default-features = false [dev-dependencies.arrayvec] version = "0.5.1" default_features = false [dev-dependencies.fastrand] version = "1.3.5" default_features = false [features] __debug = ["const_format_proc_macros/debug"] __docsrs = [] __only_new_tests = ["__test"] __test = [] all = [ "fmt", "derive", "rust_1_64", "assert", ] assert = ["assertc"] assertc = [ "fmt", "assertcp", ] assertcp = ["rust_1_51"] const_generics = ["rust_1_51"] constant_time_as_str = ["fmt"] default = [] derive = [ "fmt", "const_format_proc_macros/derive", ] fmt = ["rust_1_64"] more_str_macros = ["rust_1_64"] nightly_const_generics = ["const_generics"] rust_1_51 = [] rust_1_64 = [ "rust_1_51", "konst", "konst/rust_1_64", ] const_format-0.2.32/Cargo.toml.orig000064400000000000000000000031101046102023000152700ustar 00000000000000[package] name = "const_format" version = "0.2.32" authors = ["rodrimati1992 "] rust-version = "1.57.0" edition = "2021" license = "Zlib" description = "Compile-time string formatting" documentation = "https://docs.rs/const_format/" readme="../README.md" keywords = ["no-std", "format", "concat"] categories = ["no-std", "text-processing"] repository = "https://github.com/rodrimati1992/const_format_crates/" include = [ "Cargo.toml", "src/**/*.rs", "../README.md", "LICENSE-ZLIB.md", ] [features] default = [] const_generics = ["rust_1_51"] nightly_const_generics = ["const_generics"] rust_1_51 = [] rust_1_64 = ["rust_1_51", "konst", "konst/rust_1_64"] fmt = ["rust_1_64"] derive = ["fmt", "const_format_proc_macros/derive"] # soft-deprecated, use assertc instead. assert = ["assertc"] assertc = ["fmt", "assertcp"] assertcp = ["rust_1_51"] constant_time_as_str = ["fmt"] more_str_macros = ["rust_1_64"] # enables all the features, requires (potentially) the latest nightly all = [ "fmt", "derive", "rust_1_64", "assert", ] ############## ### "private" features # __debug = ["const_format_proc_macros/debug"] __test = [] __only_new_tests = ["__test"] __docsrs = [] [dependencies.const_format_proc_macros] version = "=0.2.32" path = "../const_format_proc_macros" [dependencies.konst] version = "0.2.13" default-features = false optional = true [dev-dependencies] fastrand = {version = "1.3.5", default_features = false} arrayvec = {version = "0.5.1", default_features = false} [package.metadata.docs.rs] features = ["all", "__docsrs"] const_format-0.2.32/LICENSE-ZLIB.md000064400000000000000000000015271046102023000145550ustar 00000000000000Copyright (c) 2020 Matias Rodriguez. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.const_format-0.2.32/README.md000064400000000000000000000244611046102023000136740ustar 00000000000000 [![Rust](https://github.com/rodrimati1992/const_format_crates/workflows/Rust/badge.svg)](https://github.com/rodrimati1992/const_format_crates/actions) [![crates-io](https://img.shields.io/crates/v/const_format.svg)](https://crates.io/crates/const_format) [![api-docs](https://docs.rs/const_format/badge.svg)](https://docs.rs/const_format/*) Compile-time string formatting. This crate provides types and macros for formatting strings at compile-time. # Rust versions There are some features that require a variety of stable Rust versions and others that require Rust nightly, the sections below describe the features that are available for each version. ### Rust 1.57.0 These macros are available in Rust 1.57.0: - [`concatcp`]: Concatenates `integers`, `bool`, `char`, and `&str` constants into a `&'static str` constant. - [`formatcp`]: [`format`]-like formatting which takes `integers`, `bool`, `char`, and `&str` constants, and emits a `&'static str` constant. - [`str_get`]: Indexes a `&'static str` constant, returning `None` when the index is out of bounds. - [`str_index`]: Indexes a `&'static str` constant. - [`str_repeat`]: Creates a `&'static str` by repeating a `&'static str` constant `times` times. - [`str_splice`]: Replaces a substring in a `&'static str` constant. - [`map_ascii_case`]: Converts a `&'static str` constant to a different casing style, determined by a [`Case`] argument. - [`str_replace`]: Replaces all the instances of a pattern in a `&'static str` constant with another `&'static str` constant. The `"assertcp"` feature enables the [`assertcp`], [`assertcp_eq`], and [`assertcp_ne`] macros. These macros are like the standard library assert macros, but evaluated at compile-time, with the limitation that they can only have primitive types as arguments (just like [`concatcp`] and [`formatcp`]). ### Rust 1.64.0 The `"rust_1_64"` feature enables these macros: - [`str_split`]: splits a string constant ### Rust nightly By enabling the "fmt" feature, you can use a [`std::fmt`]-like API. This requires the nightly compiler, because it uses mutable references in const fn, which have not been stabilized as of writing these docs. All the other features of this crate are implemented on top of the [`const_format::fmt`] API: - [`concatc`]: Concatenates many standard library and user defined types into a `&'static str` constant. - [`formatc`]: [`format`]-like macro that can format many standard library and user defined types into a `&'static str` constant. - [`writec`]: [`write`]-like macro that can format many standard library and user defined types into a type that implements [`WriteMarker`]. The `"derive"` feature enables the [`ConstDebug`] macro, and the `"fmt"` feature.
[`ConstDebug`] derives the [`FormatMarker`] trait, and implements an inherent `const_debug_fmt` method for compile-time debug formatting. The `"assertc"` feature enables the [`assertc`], [`assertc_eq`], [`assertc_ne`] macros, and the `"fmt"` feature.
These macros are like the standard library assert macros, but evaluated at compile-time. # Examples ### Concatenation of primitive types ```rust use const_format::concatcp; const NAME: &str = "Bob"; const FOO: &str = concatcp!(NAME, ", age ", 21u8,"!"); assert_eq!(FOO, "Bob, age 21!"); ``` ### Formatting primitive types ```rust use const_format::formatcp; const NAME: &str = "John"; const FOO: &str = formatcp!("{NAME}, age {}!", compute_age(NAME)); assert_eq!(FOO, "John, age 24!"); const fn compute_age(s: &str) -> usize { s.len() * 6 } ``` ### Formatting custom types This example demonstrates how you can use the [`ConstDebug`] derive macro, and then format the type into a `&'static str` constant. This example requires Rust nightly, and the `"derive"` feature. ```rust #![feature(const_mut_refs)] use const_format::{ConstDebug, formatc}; #[derive(ConstDebug)] struct Message{ ip: [Octet; 4], value: &'static str, } #[derive(ConstDebug)] struct Octet(u8); const MSG: Message = Message{ ip: [Octet(127), Octet(0), Octet(0), Octet(1)], value: "Hello, World!", }; const FOO: &str = formatc!("{:?}", MSG); assert_eq!( FOO, "Message { ip: [Octet(127), Octet(0), Octet(0), Octet(1)], value: \"Hello, World!\" }" ); ``` ### Formatted const assertions This example demonstrates how you can use the [`assertcp_ne`] macro to do compile-time inequality assertions with formatted error messages. This requires the `"assertcp"` feature. ```rust, compile_fail use const_format::assertcp_ne; macro_rules! check_valid_pizza{ ($user:expr, $topping:expr) => { assertcp_ne!( $topping, "pineapple", "You can't put pineapple on pizza, {}", $user, ); } } check_valid_pizza!("John", "salami"); check_valid_pizza!("Dave", "sausage"); check_valid_pizza!("Bob", "pineapple"); ``` This is the compiler output: ```text error[E0080]: evaluation of constant value failed --> src/lib.rs:178:27 | 20 | check_valid_pizza!("Bob", "pineapple"); | ^^^^^^^^^^^ the evaluated program panicked at ' assertion failed: `(left != right)` left: `"pineapple"` right: `"pineapple"` You can't put pineapple on pizza, Bob ', src/lib.rs:20:27 ```
# Limitations All of the macros from `const_format` have these limitations: - The formatting macros that expand to `&'static str`s can only use constants from concrete types, so while a `Type::::FOO` argument would be fine, `Type::::FOO` would not be (`T` being a type parameter). - Integer arguments must have a type inferrable from context, [more details in the Integer arguments section](#integer-args). - They cannot be used places that take string literals. So `#[doc = "foobar"]` cannot be replaced with `#[doc = concatcp!("foo", "bar") ]`. ### Integer arguments Integer arguments must have a type inferrable from context. so if you only pass an integer literal it must have a suffix. Example of what does compile: ```rust const N: u32 = 1; assert_eq!(const_format::concatcp!(N + 1, 2 + N), "23"); assert_eq!(const_format::concatcp!(2u32, 2 + 1u8, 3u8 + 1), "234"); ``` Example of what does not compile: ```rust,compile_fail assert_eq!(const_format::concatcp!(1 + 1, 2 + 1), "23"); ``` # Plans None right now. # Renaming crate All function-like macros from `const_format` can be used when the crate is renamed. The [`ConstDebug`] derive macro has the `#[cdeb(crate = "foo::bar")]` attribute to tell it where to find the `const_format` crate. Example of renaming the `const_format` crate in the Cargo.toml file: ```toml [dependencies] cfmt = {version = "0.*", package = "const_format"} ``` # Cargo features - `"fmt"`: Enables the [`std::fmt`]-like API, requires Rust nightly because it uses mutable references in const fn.
This feature includes the [`formatc`]/[`writec`] formatting macros. - `"derive"`: requires Rust nightly, implies the `"fmt"` feature, provides the [`ConstDebug`] derive macro to format user-defined types at compile-time.
This implicitly uses the `syn` crate, so clean compiles take a bit longer than without the feature. - `"assertc"`: requires Rust nightly, implies the `"fmt"` feature, enables the [`assertc`], [`assertc_eq`], and [`assertc_ne`] assertion macros.
This feature was previously named `"assert"`, but it was renamed to avoid confusion with the `"assertcp"` feature. - `"assertcp"`: Enables the [`assertcp`], [`assertcp_eq`], and [`assertcp_ne`] assertion macros. - `"rust_1_64"`: Enables the [`str_split`] macro. Allows the `as_bytes_alt` methods and `slice_up_to_len_alt` methods to run in constant time, rather than linear time (proportional to the truncated part of the slice). # No-std support `const_format` is unconditionally `#![no_std]`, it can be used anywhere Rust can be used. # Minimum Supported Rust Version `const_format` requires Rust 1.57.0. Features that require newer versions of Rust, or the nightly compiler, need to be explicitly enabled with cargo features. [`assertc`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertc.html [`assertc_eq`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertc_eq.html [`assertc_ne`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertc_ne.html [`assertcp`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertcp.html [`assertcp_eq`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertcp_eq.html [`assertcp_ne`]: https://docs.rs/const_format/0.2.*/const_format/macro.assertcp_ne.html [`concatcp`]: https://docs.rs/const_format/0.2.*/const_format/macro.concatcp.html [`formatcp`]: https://docs.rs/const_format/0.2.*/const_format/macro.formatcp.html [`format`]: https://doc.rust-lang.org/std/macro.format.html [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html [`const_format::fmt`]: https://docs.rs/const_format/0.2.*/const_format/fmt/index.html [`concatc`]: https://docs.rs/const_format/0.2.*/const_format/macro.concatc.html [`formatc`]: https://docs.rs/const_format/0.2.*/const_format/macro.formatc.html [`writec`]: https://docs.rs/const_format/0.2.*/const_format/macro.writec.html [`write`]: https://doc.rust-lang.org/std/macro.write.html [`Formatter`]: https://docs.rs/const_format/0.2.*/const_format/fmt/struct.Formatter.html [`StrWriter`]: https://docs.rs/const_format/0.2.*/const_format/fmt/struct.StrWriter.html [`ConstDebug`]: https://docs.rs/const_format/0.2.*/const_format/derive.ConstDebug.html [`FormatMarker`]: https://docs.rs/const_format/0.2.*/const_format/marker_traits/trait.FormatMarker.html [`WriteMarker`]: https://docs.rs/const_format/0.2.*/const_format/marker_traits/trait.WriteMarker.html [`map_ascii_case`]: https://docs.rs/const_format/0.2.*/const_format/macro.map_ascii_case.html [`Case`]: https://docs.rs/const_format/0.2.*/const_format/enum.Case.html [`str_get`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_get.html [`str_index`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_index.html [`str_repeat`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_repeat.html [`str_splice`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_splice.html [`str_replace`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_replace.html [`str_split`]: https://docs.rs/const_format/0.2.*/const_format/macro.str_split.html [`str::replace`]: https://doc.rust-lang.org/std/primitive.str.html#method.replace const_format-0.2.32/src/__ascii_case_conv/word_iterator.rs000064400000000000000000000117571046102023000220500ustar 00000000000000use core::fmt::{self, Debug}; macro_rules! for_range_inc { ($current:ident in $start:expr, $end:expr => $($code:tt)*) => { let mut $current = $start; let end = $end; while $current <= end { $($code)* $current+=1; } }; } use core::ops::Range; #[derive(Copy, Clone)] struct ByteKind(u8); impl Debug for ByteKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match () { _ if self.0 == Self::Other.0 => "Other", _ if self.0 == Self::Number.0 => "Number", _ if self.0 == Self::LowerCase.0 => "LowerCase", _ if self.0 == Self::UpperCase.0 => "UpperCase", _ if self.0 == Self::NonAscii.0 => "NonAscii", _ => unreachable!(), }) } } #[allow(non_upper_case_globals)] impl ByteKind { const Other: Self = Self(0b0001); const Number: Self = Self(0b0010); const LowerCase: Self = Self(0b0100); const UpperCase: Self = Self(0b1000); const Alphabetic: Self = Self(Self::LowerCase.0 | Self::UpperCase.0); // Assumes that non-ascii chars are mostly alphabetic, // this should work out fine most of the time. const NonAscii: Self = Self(0b1100); } impl ByteKind { #[allow(dead_code)] #[inline(always)] pub const fn eq(self, other: Self) -> bool { (self.0 & other.0) != 0 } #[inline(always)] pub const fn ne(self, other: Self) -> bool { (self.0 & other.0) == 0 } #[inline(always)] pub const fn is_alphabetic(self) -> bool { self.0 == Self::LowerCase.0 || self.0 == Self::UpperCase.0 } pub const fn is_end_of_word(mut self, prev: Self, other: Self) -> bool { if self.0 == Self::NonAscii.0 { self = prev; } if self.0 == Self::UpperCase.0 { other.ne(Self::Alphabetic) } else { self.ne(other) } } } #[derive(Debug, Copy, Clone)] pub(crate) struct WordIterator<'a> { bytes: &'a [u8], start: usize, } const BYTE_KIND: &[ByteKind; 256] = &{ let mut out = [ByteKind::NonAscii; 256]; // Make sure that this goes first for_range_inc! {i in 0, 127 => out[i as usize] = ByteKind::Other; } for_range_inc! {i in b'A', b'Z' => out[i as usize] = ByteKind::UpperCase; } for_range_inc! {i in b'a', b'z' => out[i as usize] = ByteKind::LowerCase; } for_range_inc! {i in b'0', b'9' => out[i as usize] = ByteKind::Number; } out }; impl<'a> WordIterator<'a> { pub(crate) const fn new(bytes: &'a [u8]) -> Self { Self { bytes, start: 0 } } const fn skip_same_kind(mut self, mut kind: ByteKind) -> (Self, ByteKind) { let orig_bytes_len = self.bytes.len(); let mut prev_kind = kind; while let [b, rem @ ..] = self.bytes { let next_kind = BYTE_KIND[*b as usize]; let cmp = kind.is_end_of_word(prev_kind, next_kind); if kind.is_alphabetic() { prev_kind = kind; } kind = next_kind; if cmp { break; } self.bytes = rem; } // Advance until a char boundary is found while let [b, rem @ ..] = self.bytes { if (*b as i8) >= -0x40 { break; } self.bytes = rem; } // Remember not to add return statements to the function self.start += orig_bytes_len - self.bytes.len(); (self, kind) } pub(crate) const fn next(self) -> Option<(Self, Range)> { let (this, fkind) = self.skip_same_kind(ByteKind::Other); if let [] = this.bytes { None } else { let (next, _) = this.skip_same_kind(fkind); let range = this.start..next.start; Some((next, range)) } } } #[cfg(test)] mod tests { use super::*; use arrayvec::ArrayVec; fn get_words(text: &str) -> ArrayVec<[&str; 20]> { let mut list = >::new(); let mut word_iter = WordIterator::new(text.as_bytes()); while let Some((niter, word_range)) = word_iter.next() { word_iter = niter; list.push(&text[word_range]); } list } #[test] fn test_word_iter() { assert_eq!( get_words("01934324ñmaniÑNnFooBar")[..], ["01934324", "ñmaniÑ", "Nn", "Foo", "Bar"], ); assert_eq!( get_words("01934 324 ñmani-嶲Nn____FOOOBar")[..], ["01934", "324", "ñmani", "嶲Nn", "FOOOBar"], ); assert_eq!(get_words(" 01934 1111 ")[..], ["01934", "1111"],); assert_eq!(get_words(" 嶲01934 ")[..], ["嶲", "01934"],); assert_eq!(get_words(" 嶲A01934 ")[..], ["嶲A", "01934"],); assert_eq!(get_words(" 嶲a01934 ")[..], ["嶲a", "01934"],); assert_eq!(get_words(" ñA01934 ")[..], ["ñA", "01934"],); assert_eq!(get_words(" ña01934 ")[..], ["ña", "01934"],); } } const_format-0.2.32/src/__ascii_case_conv.rs000064400000000000000000000124231046102023000171530ustar 00000000000000mod word_iterator; use word_iterator::WordIterator; /// The casing style of a string. /// /// You can pass this to [`map_ascii_case`] to determine the casing style of the /// returned `&'static str`. /// /// /// [`map_ascii_case`]: ./macro.map_ascii_case.html #[derive(Debug, Copy, Clone, PartialEq)] pub enum Case { /// Lowercase Lower, /// Uppercase Upper, /// Pascal case, eg: `FooBarBaz`. The first character is always uppercase. Pascal, /// Camel case, eg: `fooBarBaz`. The first character is always lowercase. Camel, /// Snake case, eg: `foo_bar_baz`. Also turns the string lowercase. Snake, /// Snake case, eg: `FOO_BAR_BAZ`. Also turns the string uppercase. UpperSnake, /// Kebab case, eg: `foo-bar-baz`. Also turns the string lowercase. Kebab, /// Kebab case, eg: `FOO-BAR-BAZ`. Also turns the string uppercase. UpperKebab, } macro_rules! if_next_word { ($word_iterator:ident, $word_range:ident => $then:block $(else $else:block)? ) => { #[allow(unused_mut)] if let Some((niter, mut $word_range)) = $word_iterator.next() { $word_iterator = niter; $then } $(else $else)? }; } macro_rules! while_next_word { ($word_iterator:ident, $word_range:ident => $then:block) => { #[allow(unused_mut)] while let Some((niter, mut $word_range)) = $word_iterator.next() { $word_iterator = niter; $then } }; } struct WordCountAndLength { /// The amount of words count: usize, /// The length of all words added up length: usize, } const fn words_count_and_length(bytes: &[u8]) -> WordCountAndLength { let mut count = 0; let mut length = 0; let mut word_iter = WordIterator::new(bytes); while_next_word! {word_iter, word_range => { count += 1; length += word_range.end - word_range.start; }} WordCountAndLength { count, length } } pub const fn size_after_conversion(case: Case, s: &str) -> usize { match case { Case::Upper | Case::Lower => s.len(), Case::Pascal | Case::Camel => { let wcl = words_count_and_length(s.as_bytes()); wcl.length } Case::Snake | Case::Kebab | Case::UpperSnake | Case::UpperKebab => { let wcl = words_count_and_length(s.as_bytes()); wcl.length + wcl.count.saturating_sub(1) } } } pub const fn convert_str(case: Case, s: &str) -> [u8; N] { let mut arr = [0; N]; let mut inp = s.as_bytes(); let mut o = 0; macro_rules! map_bytes { ($byte:ident => $e:expr) => { while let [$byte, rem @ ..] = inp { let $byte = *$byte; inp = rem; arr[o] = $e; o += 1; } }; } macro_rules! write_byte { ($byte:expr) => { arr[o] = $byte; o += 1; }; } macro_rules! write_range_from { ($range:expr, $from:expr, $byte:ident => $mapper:expr) => {{ let mut range = $range; while range.start < range.end { let $byte = $from[range.start]; arr[o] = $mapper; range.start += 1; o += 1; } }}; } macro_rules! write_snake_kebab_case { ($separator:expr, $byte_conversion:expr) => {{ let mut word_iter = WordIterator::new(inp); if_next_word! {word_iter, word_range => { write_range_from!(word_range, inp, byte => $byte_conversion(byte)); while_next_word!{word_iter, word_range => { write_byte!($separator); write_range_from!(word_range, inp, byte => $byte_conversion(byte)); }} }} }}; } macro_rules! write_pascal_camel_case { ($first_word_conv:expr) => {{ let mut word_iter = WordIterator::new(inp); if_next_word! {word_iter, word_range => { write_byte!($first_word_conv(inp[word_range.start])); word_range.start += 1; write_range_from!(word_range, inp, byte => lowercase_u8(byte)); while_next_word!{word_iter, word_range => { write_byte!(uppercase_u8(inp[word_range.start])); word_range.start += 1; write_range_from!(word_range, inp, byte => lowercase_u8(byte)); }} }} }}; } match case { Case::Upper => map_bytes!(b => uppercase_u8(b)), Case::Lower => map_bytes!(b => lowercase_u8(b)), Case::Snake => write_snake_kebab_case!(b'_', lowercase_u8), Case::UpperSnake => write_snake_kebab_case!(b'_', uppercase_u8), Case::Kebab => write_snake_kebab_case!(b'-', lowercase_u8), Case::UpperKebab => write_snake_kebab_case!(b'-', uppercase_u8), Case::Pascal => write_pascal_camel_case!(uppercase_u8), Case::Camel => write_pascal_camel_case!(lowercase_u8), } arr } const CASE_DIFF: u8 = b'a' - b'A'; const fn uppercase_u8(b: u8) -> u8 { if let b'a'..=b'z' = b { b - CASE_DIFF } else { b } } const fn lowercase_u8(b: u8) -> u8 { if let b'A'..=b'Z' = b { b + CASE_DIFF } else { b } } const_format-0.2.32/src/__hidden_utils.rs000064400000000000000000000010361046102023000165140ustar 00000000000000pub(crate) const fn max_usize(l: usize, r: usize) -> usize { if l > r { l } else { r } } pub(crate) const fn saturating_add(l: usize, r: usize) -> usize { let (sum, overflowed) = l.overflowing_add(r); if overflowed { usize::MAX } else { sum } } pub(crate) const fn is_char_boundary_no_len_check(str: &[u8], index: usize) -> bool { index == str.len() || (str[index] as i8) >= -0x40 } #[repr(C)] pub union PtrToRef<'a, T: ?Sized> { pub ptr: *const T, pub reff: &'a T, } const_format-0.2.32/src/__str_methods/pattern.rs000064400000000000000000000024331046102023000200530ustar 00000000000000use super::AsciiByte; pub(crate) struct PatternCtor(pub(crate) T); impl PatternCtor { pub(crate) const fn conv(self) -> Pattern { Pattern::AsciiByte(AsciiByte::new(self.0)) } } impl PatternCtor<&'static str> { pub(crate) const fn conv(self) -> Pattern { if let [b @ 0..=127] = *self.0.as_bytes() { Pattern::AsciiByte(AsciiByte::new(b)) } else { Pattern::Str(self.0) } } } impl PatternCtor { pub(crate) const fn conv(self) -> Pattern { let code = self.0 as u32; if let c @ 0..=127 = code { Pattern::AsciiByte(AsciiByte::new(c as u8)) } else { Pattern::Char(crate::char_encoding::char_to_display(self.0)) } } } #[derive(Copy, Clone)] pub(crate) enum Pattern { AsciiByte(AsciiByte), Str(&'static str), Char(crate::char_encoding::FmtChar), } pub(crate) enum PatternNorm<'a> { AsciiByte(AsciiByte), Str(&'a [u8]), } impl Pattern { pub(crate) const fn normalize(&self) -> PatternNorm<'_> { match self { Pattern::AsciiByte(ab) => PatternNorm::AsciiByte(*ab), Pattern::Str(str) => PatternNorm::Str(str.as_bytes()), Pattern::Char(char) => PatternNorm::Str(char.as_bytes()), } } } const_format-0.2.32/src/__str_methods/str_indexing.rs000064400000000000000000000127111046102023000210730ustar 00000000000000pub struct StrIndexArgsConv { pub str: &'static str, pub arg: T, } #[allow(non_snake_case)] pub const fn StrIndexArgsConv(str: &'static str, arg: T) -> StrIndexArgsConv { StrIndexArgsConv { str, arg } } pub struct StrIndexArgs { pub str: &'static str, pub index_validity: IndexValidity, pub used_rstart: usize, pub used_rlen: usize, pub used_rend: usize, } #[derive(Copy, Clone)] #[cfg_attr(test, derive(Debug, PartialEq))] pub enum IndexValidity { Valid, StartOob(usize), StartInsideChar(usize), EndOob(usize), EndInsideChar(usize), } impl IndexValidity { pub const fn is_valid(self) -> bool { matches!(self, Self::Valid) } pub const fn assert_valid(self) { match self { Self::Valid => (), Self::StartOob(index) => [/*start index is out of bounds*/][index], Self::StartInsideChar(index) => [/*start index is not on a char boundary*/][index], Self::EndOob(index) => [/*end index is out of bounds*/][index], Self::EndInsideChar(index) => [/*end index is not on a char boundary*/][index], } } } macro_rules! pass_range_types { ($macro:ident) => { const _: () = { use core::ops; #[allow(unused_imports)] use crate::__hidden_utils::{is_char_boundary_no_len_check, max_usize, saturating_add}; $macro! { fn(self, usize) { let mut end = saturating_add(self.arg, 1); let bytes = self.str.as_bytes(); if end < self.str.len() { while !is_char_boundary_no_len_check(bytes, end) { end = saturating_add(end, 1); } } self.arg .. end } fn(self, ops::Range) { let ops::Range{start, end} = self.arg; start .. max_usize(start, end) } fn(self, ops::RangeTo) { 0..self.arg.end } fn(self, ops::RangeFrom) { self.arg.start..self.str.len() } fn(self, ops::RangeInclusive) { let start = *self.arg.start(); start .. max_usize(saturating_add(*self.arg.end(), 1), start) } fn(self, ops::RangeToInclusive) { 0 .. saturating_add(self.arg.end, 1) } fn(self, ops::RangeFull) { 0 .. self.str.len() } } }; }; } pub(super) use pass_range_types; macro_rules! define_conversions { ( $( fn($self:ident, $ty:ty) $block:block )* ) => { $( impl StrIndexArgsConv<$ty> { pub const fn conv($self) -> StrIndexArgs { use crate::__hidden_utils::is_char_boundary_no_len_check; let range = $block; let str_len = $self.str.len(); let mut used_rstart = 0; let mut used_rend = str_len; let mut index_validity = IndexValidity::Valid; let bytes = $self.str.as_bytes(); if range.end > str_len { index_validity = IndexValidity::EndOob(range.end); } else if is_char_boundary_no_len_check(bytes, range.end) { used_rend = range.end; } else { index_validity = IndexValidity::EndInsideChar(range.end); }; if range.start > str_len { index_validity = IndexValidity::StartOob(range.start); } else if is_char_boundary_no_len_check(bytes, range.start) { used_rstart = range.start; } else { index_validity = IndexValidity::StartInsideChar(range.start); }; StrIndexArgs { str: $self.str, index_validity, used_rstart, used_rend, used_rlen: used_rend - used_rstart, } } } )* }; } pass_range_types! {define_conversions} #[cfg(test)] mod tests { use super::*; #[test] fn index_validity_test() { macro_rules! miv { ($str:expr, $range:expr) => { StrIndexArgsConv($str, $range).conv().index_validity }; } assert_eq!(miv!("効率的", 3), IndexValidity::Valid); assert_eq!(miv!("効率的", 6), IndexValidity::Valid); assert_eq!(miv!("効率的", 3..6), IndexValidity::Valid); assert_eq!(miv!("効率的", 4..6), IndexValidity::StartInsideChar(4)); assert_eq!(miv!("効率的", 3..5), IndexValidity::EndInsideChar(5)); assert_eq!(miv!("効率的", 7..9), IndexValidity::StartInsideChar(7)); assert_eq!(miv!("効率的", 100..9), IndexValidity::StartOob(100)); assert_eq!(miv!("効率的", 3..10), IndexValidity::EndOob(10)); assert_eq!(miv!("効率的", 9), IndexValidity::EndOob(10)); assert_eq!(miv!("効率的", 10), IndexValidity::StartOob(10)); assert_eq!(miv!("効率的", 100..900), IndexValidity::StartOob(100)); } } const_format-0.2.32/src/__str_methods/str_repeat.rs000064400000000000000000000014731046102023000205510ustar 00000000000000pub struct StrRepeatArgs { pub str: &'static str, pub str_len: usize, pub out_len: usize, pub overflowed_len: Option, pub repeat: usize, } #[allow(non_snake_case)] pub const fn StrRepeatArgs(str: &'static str, repeat: usize) -> StrRepeatArgs { let str_len = str.len(); let (mul, overflowed) = str_len.overflowing_mul(repeat); let (out_len, overflowed_len, repeat) = if overflowed { (str_len, Some(mul), 1) } else { (mul, None, repeat) }; StrRepeatArgs { str, str_len, out_len, overflowed_len, repeat, } } impl StrRepeatArgs { pub const fn assert_valid(&self) { if let Some(overflowed_len) = self.overflowed_len { [/* the returned string is too large */][overflowed_len] } } } const_format-0.2.32/src/__str_methods/str_replace.rs000064400000000000000000000063631046102023000207070ustar 00000000000000use super::{bytes_find, Pattern, PatternCtor, PatternNorm}; pub struct ReplaceInputConv(pub &'static str, pub T, pub &'static str); macro_rules! ctor { ($ty:ty) => { impl ReplaceInputConv<$ty> { pub const fn conv(self) -> ReplaceInput { ReplaceInput { str: self.0, pattern: PatternCtor(self.1).conv(), replaced_with: self.2, } } } }; } ctor! {u8} ctor! {&'static str} ctor! {char} pub struct ReplaceInput { str: &'static str, pattern: Pattern, replaced_with: &'static str, } impl ReplaceInput { pub const fn replace_length(&self) -> usize { str_replace_length(self.str, self.pattern, self.replaced_with) } pub const fn replace(&self) -> [u8; L] { str_replace(self.str, self.pattern, self.replaced_with) } } const fn str_replace_length(inp: &str, r: Pattern, replaced_with: &str) -> usize { let inp = inp.as_bytes(); let replaced_len = replaced_with.len(); let mut out_len = 0; match r.normalize() { PatternNorm::AsciiByte(byte) => { let byte = byte.get(); iter_copy_slice! {b in inp => out_len += if b == byte { replaced_len } else { 1 }; } } PatternNorm::Str(str) => { if str.is_empty() { return inp.len(); } let str_len = str.len(); let mut i = 0; while let Some(next_match) = bytes_find(inp, str, i) { out_len += (next_match - i) + replaced_len; i = next_match + str_len; } out_len += inp.len() - i; } } out_len } const fn str_replace(inp: &str, r: Pattern, replaced_with: &str) -> [u8; L] { let inp = inp.as_bytes(); let replaced_with_bytes = replaced_with.as_bytes(); let mut out = [0u8; L]; let mut out_i = 0; macro_rules! write_replaced { () => { iter_copy_slice! {b in replaced_with_bytes => out[out_i] = b; out_i += 1; } }; } macro_rules! write_byte { ($byte:expr) => { out[out_i] = $byte; out_i += 1; }; } match r.normalize() { PatternNorm::AsciiByte(byte) => { let byte = byte.get(); iter_copy_slice! {b in inp => if b == byte { write_replaced!{} } else { write_byte!{b} } } } PatternNorm::Str(str) => { if str.is_empty() { iter_copy_slice! {b in inp => write_byte!(b); } return out; } let str_len = str.len(); let mut i = 0; while let Some(next_match) = bytes_find(inp, str, i) { __for_range! {j in i..next_match => write_byte!(inp[j]); } write_replaced! {} i = next_match + str_len; } __for_range! {j in i..inp.len() => write_byte!(inp[j]); } } } out } const_format-0.2.32/src/__str_methods/str_splice.rs000064400000000000000000000042141046102023000205440ustar 00000000000000use super::str_indexing::{pass_range_types, IndexValidity, StrIndexArgs, StrIndexArgsConv}; pub struct StrSplceArgsConv { pub arg: T, pub str: &'static str, pub insert: &'static str, } #[allow(non_snake_case)] pub const fn StrSplceArgsConv( str: &'static str, arg: T, insert: &'static str, ) -> StrSplceArgsConv { StrSplceArgsConv { str, arg, insert } } pub struct StrSpliceArgs { pub str: &'static str, pub insert: &'static str, pub index_validity: IndexValidity, pub used_rstart: usize, pub used_rlen: usize, pub insert_len: usize, pub suffix_len: usize, pub out_len: usize, } /// The return value of [`str_splice`](./macro.str_splice.html) #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SplicedStr { /// A string that had `removed` replaced with some other string. pub output: &'static str, /// The part of the string that was removed. pub removed: &'static str, } #[repr(C, packed)] pub struct DecomposedString { pub prefix: P, pub middle: M, pub suffix: S, } macro_rules! define_conversions { ( $( fn($self:ident, $ty:ty) $block:block )* ) => { $( impl StrSplceArgsConv<$ty> { pub const fn conv(self) -> StrSpliceArgs { let StrIndexArgs{ str, index_validity, used_rstart, used_rend, used_rlen, } = StrIndexArgsConv{ arg: self.arg, str: self.str, }.conv(); StrSpliceArgs{ str, index_validity, used_rstart, used_rlen, insert: self.insert, insert_len: self.insert.len(), suffix_len: str.len() - used_rend, out_len: str.len() - used_rlen + self.insert.len(), } } } )* }; } pass_range_types! {define_conversions} const_format-0.2.32/src/__str_methods/str_split.rs000064400000000000000000000076441046102023000204320ustar 00000000000000use super::{Pattern, PatternCtor, PatternNorm}; use konst::slice::{bytes_find, bytes_find_skip}; pub struct SplitInputConv(pub &'static str, pub T); macro_rules! ctor { ($ty:ty) => { impl SplitInputConv<$ty> { pub const fn conv(self) -> SplitInput { SplitInput { str: self.0, pattern: PatternCtor(self.1).conv(), length: usize::MAX, } .compute_length() } } }; } ctor! {u8} ctor! {&'static str} ctor! {char} #[derive(Copy, Clone)] pub struct SplitInput { str: &'static str, pattern: Pattern, length: usize, } impl SplitInput { const fn compute_length(mut self) -> Self { self.length = count_splits(self); self } pub const fn split_it(self) -> [&'static str; LEN] { split_it(self) } pub const fn length(&self) -> usize { self.length } } pub const fn count_splits( SplitInput { mut str, pattern, .. }: SplitInput, ) -> usize { let mut count = 1; match pattern.normalize() { PatternNorm::AsciiByte(ascii_c) => { let mut bytes = str.as_bytes(); let ascii_c = ascii_c.get(); while let [byte, rem @ ..] = bytes { bytes = rem; if *byte == ascii_c { count += 1; } } } PatternNorm::Str(str_pat) => { if str_pat.is_empty() { let mut char_i = 0; count += 1; while let Some(next) = find_next_char_boundary(str, char_i) { char_i = next; count += 1; } } else { let mut str = str.as_bytes(); while let Some(next) = bytes_find_skip(str, str_pat) { str = next; count += 1; } } } } count } const fn find_u8(mut slice: &[u8], byte: u8) -> Option { let mut i = 0; while let [b, ref rem @ ..] = *slice { if byte == b { return Some(i); } slice = rem; i += 1; } None } const fn find_next_char_boundary(str: &str, mut index: usize) -> Option { if index == str.len() { None } else { loop { index += 1; if index == str.len() || (str.as_bytes()[index] as i8) >= -0x40 { break Some(index); } } } } pub const fn split_it(args: SplitInput) -> [&'static str; LEN] { let SplitInput { mut str, pattern, length: _, } = args; let mut out = [""; LEN]; let mut out_i = 0; macro_rules! write_out { ($string:expr) => { out[out_i] = $string; out_i += 1; }; } match pattern.normalize() { PatternNorm::AsciiByte(ascii_c) => { let ascii_c = ascii_c.get(); while let Some(found_at) = find_u8(str.as_bytes(), ascii_c) { write_out! {konst::string::str_up_to(str, found_at)} str = konst::string::str_from(str, found_at + 1); } } PatternNorm::Str(str_pat) => { if str_pat.is_empty() { out_i += 1; while let Some(next) = find_next_char_boundary(str, 0) { write_out! {konst::string::str_up_to(str, next)} str = konst::string::str_from(str, next); } } else { while let Some(found_at) = bytes_find(str.as_bytes(), str_pat, 0) { write_out! {konst::string::str_up_to(str, found_at)} str = konst::string::str_from(str, found_at + str_pat.len()); } } } } write_out! {str} assert!(out_i == LEN); out } const_format-0.2.32/src/__str_methods.rs000064400000000000000000000036141046102023000164000ustar 00000000000000mod str_replace; pub use self::str_replace::{ReplaceInput, ReplaceInputConv}; mod str_repeat; pub use str_repeat::StrRepeatArgs; mod str_splice; pub use str_splice::{DecomposedString, SplicedStr, StrSplceArgsConv, StrSpliceArgs}; mod str_indexing; pub use str_indexing::{IndexValidity, StrIndexArgs, StrIndexArgsConv}; #[cfg(feature = "rust_1_64")] mod str_split; #[cfg(feature = "rust_1_64")] pub use str_split::{SplitInput, SplitInputConv}; mod pattern; use pattern::{Pattern, PatternCtor, PatternNorm}; mod ascii_byte { #[derive(Copy, Clone)] pub struct AsciiByte(u8); impl AsciiByte { #[inline(always)] pub const fn new(byte: u8) -> Self { if byte > 127 { let byte = byte as usize; let _: () = [/* byte isn't valid ascii */][byte]; loop {} } Self(byte) } #[inline(always)] pub const fn get(self) -> u8 { self.0 } } } pub use ascii_byte::AsciiByte; // copied from the konst crate, if that implementation is wrong, this needs to be fixed const fn bytes_find(left: &[u8], right: &[u8], from: usize) -> Option { let mut matching = right; __for_range! {i in from..left.len() => match matching { [mb, m_rem @ ..] => { let b = left[i]; matching = if b == *mb { m_rem } else { match right { // For when the string is "lawlawn" and we are trying to find "lawn" [mb2, m_rem2 @ ..] if b == *mb2 => m_rem2, _ => right, } }; } [] => { return Some(i - right.len()) } } } if matching.is_empty() { Some(left.len() - right.len()) } else { None } } const_format-0.2.32/src/char_encoding/tests.rs000064400000000000000000000055421046102023000174760ustar 00000000000000use super::{char_debug_len, char_display_len, char_to_debug, char_to_display}; #[test] fn char_to_utf8_encoding_test() { for c in '\0'..=core::char::MAX { let mut utf8_std = [0u8; 4]; let utf8_std = c.encode_utf8(&mut utf8_std); let utf8_here = char_to_display(c); assert_eq!(utf8_here.len(), char_display_len(c)); assert_eq!(utf8_std.as_bytes(), utf8_here.as_bytes()); } } #[test] fn char_to_utf8_display_test() { for c in '\0'..=core::char::MAX { let mut utf8_std = [0u8; 4]; let utf8_std = c.encode_utf8(&mut utf8_std); let utf8_here = char_to_display(c); assert_eq!(utf8_here.len(), char_display_len(c)); assert_eq!(utf8_std.as_bytes(), utf8_here.as_bytes()); } } #[test] fn char_to_utf8_debug_test() { let first_escapes = [ ('\x00', r#"'\x00'"#), ('\x01', r#"'\x01'"#), ('\x02', r#"'\x02'"#), ('\x03', r#"'\x03'"#), ('\x04', r#"'\x04'"#), ('\x05', r#"'\x05'"#), ('\x06', r#"'\x06'"#), ('\x07', r#"'\x07'"#), ('\x08', r#"'\x08'"#), ('\t', r#"'\t'"#), ('\n', r#"'\n'"#), ('\x0B', r#"'\x0B'"#), ('\x0C', r#"'\x0C'"#), ('\r', r#"'\r'"#), ('\x0E', r#"'\x0E'"#), ('\x0F', r#"'\x0F'"#), ('\x10', r#"'\x10'"#), ('\x11', r#"'\x11'"#), ('\x12', r#"'\x12'"#), ('\x13', r#"'\x13'"#), ('\x14', r#"'\x14'"#), ('\x15', r#"'\x15'"#), ('\x16', r#"'\x16'"#), ('\x17', r#"'\x17'"#), ('\x18', r#"'\x18'"#), ('\x19', r#"'\x19'"#), ('\x1A', r#"'\x1A'"#), ('\x1B', r#"'\x1B'"#), ('\x1C', r#"'\x1C'"#), ('\x1D', r#"'\x1D'"#), ('\x1E', r#"'\x1E'"#), ('\x1F', r#"'\x1F'"#), ]; for (c, expected) in first_escapes.iter().copied() { let utf8_here = char_to_debug(c); assert_eq!(expected.as_bytes(), utf8_here.as_bytes(), "{:?}", c); assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); } let other_escapes = [('\'', r#"'\''"#), ('\"', r#"'\"'"#), ('\\', r#"'\\'"#)]; let mut buffer = arrayvec::ArrayString::<[u8; 12]>::new(); for c in '\x20'..=core::char::MAX { let utf8_here = char_to_debug(c); if let Some((_, expected)) = Some(c) .filter(|c| *c <= '\x7F') .and_then(|c| other_escapes.iter().copied().find(|x| x.0 == c)) { assert_eq!(expected.as_bytes(), utf8_here.as_bytes(), "{:?}", c); assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); } else { buffer.clear(); buffer.push('\''); buffer.push(c); buffer.push('\''); assert_eq!(buffer.as_bytes(), utf8_here.as_bytes(), "{:?}", c); assert_eq!(buffer.len(), char_debug_len(c), "{:?}", c); } } } const_format-0.2.32/src/char_encoding.rs000064400000000000000000000075231046102023000163350ustar 00000000000000use crate::formatting::{hex_as_ascii, HexFormatting}; #[cfg(any(test, feature = "fmt"))] pub(crate) const fn char_display_len(c: char) -> usize { match c as u32 { 0..=127 => 1, 0x80..=0x7FF => 2, 0x800..=0xFFFF => 3, 0x10000..=u32::MAX => 4, } } #[cfg(any(test, feature = "fmt"))] pub(crate) const fn char_debug_len(c: char) -> usize { let inner = match c { '\t' | '\r' | '\n' | '\\' | '\'' | '\"' => 2, '\x00'..='\x1F' => 4, _ => char_display_len(c), }; inner + 2 } const fn char_to_utf8(char: char) -> ([u8; 4], usize) { let u32 = char as u32; match u32 { 0..=127 => ([u32 as u8, 0, 0, 0], 1), 0x80..=0x7FF => { let b0 = 0b1100_0000 | (u32 >> 6) as u8; let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, 0, 0], 2) } 0x800..=0xFFFF => { let b0 = 0b1110_0000 | (u32 >> 12) as u8; let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, b2, 0], 3) } 0x10000..=u32::MAX => { let b0 = 0b1111_0000 | (u32 >> 18) as u8; let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8; let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; ([b0, b1, b2, b3], 4) } } } pub(crate) const fn char_to_display(char: char) -> FmtChar { let ([b0, b1, b2, b3], len) = char_to_utf8(char); FmtChar { encoded: [b0, b1, b2, b3, 0, 0], len: len as u8, } } pub(crate) const fn char_to_debug(c: char) -> FmtChar { let ([b0, b1, b2, b3], len) = match c { '\t' => (*br#"\t "#, 2), '\r' => (*br#"\r "#, 2), '\n' => (*br#"\n "#, 2), '\\' => (*br#"\\ "#, 2), '\'' => (*br#"\' "#, 2), '\"' => (*br#"\" "#, 2), '\x00'..='\x1F' => { let n = c as u8; ( [ b'\\', b'x', hex_as_ascii(n >> 4, HexFormatting::Upper), hex_as_ascii(n & 0b1111, HexFormatting::Upper), ], 4, ) } _ => char_to_utf8(c), }; let mut encoded = [b'\'', b0, b1, b2, b3, 0]; encoded[len + 1] = b'\''; FmtChar { encoded, len: (len as u8) + 2, } } #[derive(Copy, Clone)] pub struct FmtChar { encoded: [u8; 6], len: u8, } impl FmtChar { /// Array which contains the pre-len display/debug-formatted `char`, /// only `&self.encoded[][..self.len()]` should be copied. pub const fn encoded(&self) -> &[u8; 6] { &self.encoded } pub const fn len(&self) -> usize { self.len as usize } pub(crate) const fn as_bytes(&self) -> &[u8] { #[cfg(not(feature = "rust_1_64"))] { match self.len() { 1 => { let [ret @ .., _, _, _, _, _] = &self.encoded; ret } 2 => { let [ret @ .., _, _, _, _] = &self.encoded; ret } 3 => { let [ret @ .., _, _, _] = &self.encoded; ret } 4 => { let [ret @ .., _, _] = &self.encoded; ret } 5 => { let [ret @ .., _] = &self.encoded; ret } 6 => &self.encoded, x => [/*bug WTF*/][x], } } #[cfg(feature = "rust_1_64")] { ::konst::slice::slice_up_to(&self.encoded, self.len()) } } } #[cfg(all(test, not(miri)))] mod tests; const_format-0.2.32/src/const_debug_derive.rs000064400000000000000000000243621046102023000174040ustar 00000000000000/// Derives const debug formatting for a type. /// /// Derives the [`FormatMarker`] trait, and defines an `const_debug_fmt` inherent /// method to format a type at compile-time. /// /// # Features /// /// This derive macro is only available with the "derive" feature, /// and the nightly compiler, /// because at the time of writing these docs (2023-10-XX) mutable references in const fn /// require the unstable /// [`const_mut_refs`](https://github.com/rust-lang/rust/issues/57349) feature. /// /// # Limitations /// /// Compile-time formatting currently imposes these limitations on users, /// this derive macro has some mitigations for some of them. /// /// ### Generic impls /// /// Because the formatting of custom types is implemented with duck typing, /// it's not possible to format generic types, instead you must do either of these: /// /// - Provide all the implementations ahead of time, what the [`impls attribute`] is for. /// /// - Provide a macro that formats the type. /// The `call_debug_fmt` macro is a version of this that formats generic std types, /// then it can be used to format fields of the type with the /// [`#[cdeb(with_macro = "....")]`](#cdeb_with_macro) attribute. /// /// These are the things that this macro does to mitigate the limitations: /// /// - Allows users to provide a function/macro/wrapper to format a field. /// /// - Automatically detect some builtin/standard library types that are generic. /// /// - Allow users to ignore a field. /// /// # Container Attributes /// /// These attributes go on the type itself, rather than the fields. /// /// ### `#[cdeb(debug_print)]` /// /// Panics with the output of the expanded derive. /// /// ### `#[cdeb(impls(....))]` /// /// Allows users to implement const debug formatting for multiple different /// concrete instances of the type. /// /// When this attribute is used it disables the default implementation /// that uses the type parameters generically. /// /// Example: /// /// ```rust /// # #![feature(const_mut_refs)] /// #[derive(const_format::ConstDebug)] /// #[cdeb(impls( /// "Foo", /// " Foo", /// " Foo where T: 'static", /// ))] /// struct Foo(A, *const B); /// ``` /// /// In this example, there's exactly three impls of /// the `const_debug_fmt` method and [`FormatMarker`] trait. /// /// ### `#[cdeb(crate = "foo::bar")]` /// /// The path to the `const_format` crate, useful if you want to reexport the ConstDebug macro, /// or rename the `const_format` crate in the Cargo.toml . /// /// Example of renaming the `const_format` crate in the Cargo.toml file: /// ```toml /// cfmt = {version = "0.*", package = "const_format"} /// ``` /// /// # Field attributes /// /// ### `#[cdeb(ignore)]` /// /// Ignores the field, pretending that it doesn't exist. /// /// ### `#[cdeb(with = "module::function")]` /// /// Uses the function at the passed-in path to format the field. /// /// The function is expected to have this signature: /// ```ignored /// const fn(&FieldType, &mut const_format::Formatter<'_>) -> Result<(), const_format::Error> /// ``` /// /// /// /// ### `#[cdeb(with_macro = "module::the_macro")]` /// /// Uses the macro at the passed-in path to format the field. /// /// The macro is expected to be callable like a function with this signature: /// ```ignored /// const fn(&FieldType, &mut const_format::Formatter<'_>) -> Result<(), const_format::Error> /// ``` /// /// ### `#[cdeb(with_wrapper = "module::Wrapper")]` /// /// Uses the wrapper type to print the field. /// /// The wrapper is expected to wrap a reference to the field type, /// to have an implementation of the [`FormatMarker`] trait, /// and have a method with this signature: /// ```ignored /// const fn const_debug_fmt( /// self, /// &mut const_format::Formatter<'_>, /// ) -> Result<(), const_format::Error> /// ``` /// (`self` can be taken by reference or by value) /// /// ### `#[cdeb(is_a(....))]` /// /// Gives the derive macro a hint of what the type is. /// /// For standard library types, /// this is necessary if you're using a type alias, since the derive macro detects /// those types syntactically. /// /// These are the valid ways to use this attribute: /// /// - `#[cdeb(is_a(array))]`/`#[cdeb(is_a(slice))]`: /// Treats the field as being a slice/array, /// printing the elements of std or user-defined type with const debug formatting. /// /// - `#[cdeb(is_a(Option))]`/`#[cdeb(is_a(option))]`: /// Treats the field as being an Option, /// printing the contents of std or user-defined type with const debug formatting. /// /// - `#[cdeb(is_a(newtype))]`: /// Treats the field as being being a single field tuple struct, /// using the identifier of the field type as the name of the struct, /// then printing the single field of std or user-defined type with const debug formatting. /// /// - `#[cdeb(is_a(non_std))]`/`#[cdeb(is_a(not_std))]`: /// This acts as an opt-out for the automatic detection of std types, /// most likely needed for types named `Option`. /// /// # Examples /// /// ### Basic /// /// This example demonstrates using the derive without using any helper attributes. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ConstDebug, formatc}; /// /// use std::cmp::Ordering; /// /// const E_FOO: &str = formatc!("{:?}", Enum::Foo); /// const E_BAR: &str = formatc!("{:?}", Enum::Bar(10)); /// const E_BAZ: &str = formatc!("{:?}", Enum::Baz{order: Ordering::Less}); /// /// const S_UNIT: &str = formatc!("{:?}", Unit); /// const S_BRACED: &str = formatc!("{:?}", Braced{is_true: false, optional: Some(Unit)}); /// /// assert_eq!(E_FOO, "Foo"); /// assert_eq!(E_BAR, "Bar(10)"); /// assert_eq!(E_BAZ, "Baz { order: Less }"); /// /// assert_eq!(S_UNIT, "Unit"); /// assert_eq!(S_BRACED, "Braced { is_true: false, optional: Some(Unit) }"); /// /// /// #[derive(ConstDebug)] /// enum Enum { /// Foo, /// Bar(u32), /// Baz{ /// order: Ordering, /// }, /// } /// /// #[derive(ConstDebug)] /// struct Unit; /// /// #[derive(ConstDebug)] /// struct Braced { /// is_true: bool, /// optional: Option, /// } /// /// ``` /// /// ### Generic type /// /// This example demonstrates the `#[cdeb(impls)]` attribute, /// a workaround for deriving this trait for generic types, /// specifying a list of impls of types that unconditionally implement const debug formatting /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ConstDebug, formatc}; /// /// use std::marker::PhantomData; /// /// /// const S_U32: &str = formatc!("{:?}", Foo(10)); /// /// const S_STR: &str = formatc!("{:?}", Foo("hello")); /// /// const S_PHANTOM: &str = formatc!("{:?}", Foo(PhantomData::<()>)); /// /// assert_eq!(S_U32, r#"Foo(10)"#); /// assert_eq!(S_STR, r#"Foo("hello")"#); /// assert_eq!(S_PHANTOM, r#"Foo(PhantomData)"#); /// /// /// // This type implements const debug formatting three times: /// // - `Foo` /// // - `Foo<&str>` /// // - `Foo>`: with a generic `T` /// #[derive(ConstDebug)] /// #[cdeb(impls( /// "Foo", /// "Foo<&str>", /// " Foo>", /// ))] /// struct Foo(T); /// /// ``` /// /// ### `is_a` attributes /// /// This example demonstrates when you would use the `is_a` attributes. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ConstDebug, formatc}; /// /// use std::{ /// cmp::Ordering, /// marker::PhantomData, /// num::Wrapping, /// }; /// /// const STRUCT: &Struct = &Struct { /// arr: [Ordering::Less, Ordering::Equal, Ordering::Greater, Ordering::Less], /// opt: Some(Unit), /// wrap: Wrapping(21), /// not_option: Option(PhantomData), // This is not the standard library `Option` /// }; /// /// const S_STRUCT: &str = formatc!("{STRUCT:#?}"); /// /// const EXPECTED: &str = "\ /// Struct { /// arr: [ /// Less, /// Equal, /// Greater, /// Less, /// ], /// opt: Some( /// Unit, /// ), /// wrap: Wrapping( /// 21, /// ), /// not_option: Option( /// PhantomData, /// ), /// }"; /// /// fn main(){ /// assert_eq!(S_STRUCT, EXPECTED); /// } /// /// #[derive(ConstDebug)] /// struct Struct { /// // `Ordering` implements const debug formatting, /// // but `[Ordering; 4]` does not, so this attribute is required for the /// // derive macro to generate code to format this array field. /// #[cdeb(is_a(array))] /// arr: Array, /// /// // Attribute is required to tell the derive macro that this is an /// // `Option` wrapping a user-defined type, /// // since `Option` doesn't implement const debug formatting. /// #[cdeb(is_a(option))] /// opt: Opt, /// /// // Attribute is required because `Wrapping` is a newtype struct /// // that doesn't implement const debug formatting, /// // so the derive generates code to format it. /// #[cdeb(is_a(newtype))] /// wrap: Wrapping, /// /// // Attribute is required for the field to be treated as a user-defined type, /// // otherwise it'd be assumed to be `Option` from the standard library. /// #[cdeb(is_a(not_std))] /// not_option: Option, /// /// } /// /// type Array = [Ordering; 4]; /// /// type Opt = std::option::Option; /// /// #[derive(ConstDebug)] /// struct Unit; /// /// #[derive(ConstDebug)] /// struct Option(PhantomData); /// /// ``` /// /// [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html /// [`impls attribute`]: #cdebimpls /// /// /// /// /// ### Renamed import /// /// This example demonstrates that you can use all the macros when the `const_format` /// crate is renamed. /// /// ```rust /// #![feature(const_mut_refs)] /// # extern crate self as const_format; /// # extern crate const_format as cfmt; /// # fn main() { /// use cfmt::{ /// for_examples::Unit, /// ConstDebug, formatc, /// }; /// /// #[derive(ConstDebug)] /// #[cdeb(crate = "cfmt")] /// struct Foo { /// bar: &'static str, /// baz: Unit /// } /// /// const TEXT: &str = formatc!("{:?}", Foo{ bar: "hello", baz: Unit }); /// /// assert_eq!(TEXT, r#"Foo { bar: "hello", baz: Unit }"#); /// /// # } /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "derive")))] #[cfg(feature = "derive")] pub use const_format_proc_macros::ConstDebug; const_format-0.2.32/src/const_generic_concatcp.rs000064400000000000000000000013471046102023000202440ustar 00000000000000//! Reimplements some stuff from concatcp to be const generic instead of macro generated use crate::pmr::{LenAndArray, PArgument, PVariant}; #[doc(hidden)] pub const fn __priv_concatenate(input: &[PArgument]) -> LenAndArray<[u8; LEN]> { let mut out = LenAndArray { len: 0, array: [0u8; LEN], }; crate::__for_range! { outer_i in 0..input.len() => let current = &input[outer_i]; match current.elem { PVariant::Str(s) => crate::__write_pvariant!(str, current, s => out), PVariant::Int(int) => crate::__write_pvariant!(int, current, int => out), PVariant::Char(c) => crate::__write_pvariant!(char, current, c => out), } } out } const_format-0.2.32/src/doctests.rs000064400000000000000000000104431046102023000153750ustar 00000000000000//! This module tests for errors that happen in the expanded code, //! errors detectable by the macro itself are tested in the proc macro crate. #![allow(non_camel_case_types)] /// /// ```rust /// /// struct Foo(T); /// /// const_format::impl_fmt!{ /// impl[T,] Foo /// where[T: 'static,]; /// /// fn foo(){} /// /// } /// ``` /// /// ```compile_fail /// /// struct Foo(T); /// /// const_format::impl_fmt!{ /// impl[T,] Foo /// where[asodkaspodaoskd,]; /// /// fn foo(){} /// } /// ``` /// /// ```compile_fail /// /// struct Foo(T); /// /// const_format::impl_fmt!{ /// impl[T,] Foo /// where[T: T]; /// /// fn foo(){} /// } /// ``` /// #[cfg(feature = "fmt")] pub struct ImplFmtWhereClause; /// /// ```rust /// #![feature(const_mut_refs)] /// /// #[derive(const_format::ConstDebug)] /// struct Foo(*const T) /// where T: 'static; /// /// fn main(){} /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// #[derive(const_format::ConstDebug)] /// struct Foo(*const T) /// where AAAA: AAAA; /// /// fn main(){} /// ``` /// #[cfg(feature = "derive")] pub struct ConstDebugWhereClause; /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::StrWriterMut; /// /// let mut len = 0; /// let mut buffer = [0; 128]; /// /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); /// /// writer.write_str("hello").unwrap(); /// /// assert_eq!(writer.as_bytes(), b"hello") /// /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// use const_format::StrWriterMut; /// /// let mut len = 0; /// let mut buffer = [0; 128]; /// /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); /// /// writer.write_str("hello").unwrap(); /// /// assert_eq!(writer.as_str(), "hello") /// /// ``` /// #[cfg(feature = "fmt")] pub struct AsStr_For_StrWriterMut_NoEncoding; /// ```rust /// #![feature(const_mut_refs)] /// /// const_format::assertc!(true, "foo"); /// /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// const_format::assertc!(false, "foo"); /// /// ``` /// /// # With a Formatting argument /// /// ```rust /// #![feature(const_mut_refs)] /// /// const_format::assertc!( /// true, /// "{foo}\n{foo:#?}\n{}", /// |fmt| { const_format::call_debug_fmt!(array, [100u8], fmt ) }, /// foo = |fmt| { const_format::call_debug_fmt!(array, [(), ()], fmt ) }, /// ); /// /// const_format::assertc!( /// true, /// "{foo}\n{foo:#?}\n{}", /// |fmt| const_format::call_debug_fmt!(array, [100u8], fmt ), /// foo = |fmt| const_format::call_debug_fmt!(array, [(), ()], fmt ), /// ); /// /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// const_format::assertc!( /// false, /// "{foo}\n{foo:#?}\n{}", /// |fmt| { const_format::call_debug_fmt!(array, [100u8], fmt ) }, /// foo = |fmt| { const_format::call_debug_fmt!(array, [(), ()], fmt ) }, /// ); /// /// const_format::assertc!( /// false, /// "{foo}\n{foo:#?}\n{}", /// |fmt| const_format::call_debug_fmt!(array, [100u8], fmt ), /// foo = |fmt| const_format::call_debug_fmt!(array, [(), ()], fmt ), /// ); /// /// ``` /// #[cfg(feature = "assertc")] pub struct Assert; /// # assert_eq /// /// ```rust /// #![feature(const_mut_refs)] /// /// const_format::assertc_eq!(0u8, 0u8, "foo"); /// /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// const_format::assertc_eq!(0u8, 10u8, "foo"); /// /// ``` /// /// # assert_ne /// /// ```rust /// #![feature(const_mut_refs)] /// /// const_format::assertc_ne!(0u8, 10u8, "foo"); /// /// ``` /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// const_format::assertc_ne!(0u8, 0u8, "foo"); /// /// ``` /// #[cfg(feature = "assertc")] pub struct AssertCmp; /// ```rust /// const_format::assertcp!(true, "foo"); /// ``` /// /// ```compile_fail /// const_format::assertcp!(false, "foo"); /// ``` /// #[cfg(feature = "assertcp")] pub struct AssertCP; /// # assert_eq /// /// ```rust /// const_format::assertcp_eq!(0u8, 0u8, "foo"); /// ``` /// /// ```compile_fail /// const_format::assertcp_eq!(0u8, 10u8, "foo"); /// ``` /// /// # assert_ne /// /// ```rust /// const_format::assertcp_ne!(0u8, 10u8, "foo"); /// ``` /// /// ```compile_fail /// const_format::assertcp_ne!(0u8, 0u8, "foo"); /// ``` /// #[cfg(feature = "assertcp")] pub struct AssertCPCmp; const_format-0.2.32/src/equality.rs000064400000000000000000000123411046102023000154010ustar 00000000000000#![allow(missing_docs, unused_variables)] use crate::wrapper_types::PWrapper; use core::{ cmp::Ordering, marker::{PhantomData, PhantomPinned}, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }, ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, sync::atomic::Ordering as AtomicOrdering, }; //////////////////////////////////////////////////////////////////////////////// macro_rules! slice_of_const_eq {($($elem:ty),* $(,)?) => ( $( impl PWrapper<&[$elem]> { /// This method is only available with the "assert" feature. pub const fn const_eq(&self, other: &[$elem]) -> bool { if self.0.len() != other.len() { return false; } __for_range!{i in 0..self.0.len() => if !PWrapper(self.0[i]).const_eq(&other[i]) { return false } } true } } )* )} slice_of_const_eq! { &str, } macro_rules! slice_of_equal_op_impl {($($elem:ty),* $(,)?) => ( $( impl PWrapper<&[$elem]> { /// This method is only available with the "assert" feature. pub const fn const_eq(&self, other: &[$elem]) -> bool { if self.0.len() != other.len() { return false; } __for_range!{i in 0..self.0.len() => if self.0[i] != other[i] { return false } } true } } )* )} slice_of_equal_op_impl! { bool, char, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, } //////////////////////////////////////////////////////////////////////////////// macro_rules! impl_eq_for_option_prim { ( (l=$l:ident, r=$r:ident) $( impl[$($impl_:tt)*] $type:ty = $comparison:expr; )* ) => ( $( impl<$($impl_)*> PWrapper> { /// This method is only available with the "assert" feature. pub const fn const_eq(&self, other:&Option<$type>) -> bool { match (self.0, other) { (Some($l), Some($r)) => $comparison, (None, None) => true, _ => false, } } } )* ) } impl_eq_for_option_prim! { (l=l, r=r) impl[] u8 = l == *r; impl[] i8 = l == *r; impl[] u16 = l == *r; impl[] i16 = l == *r; impl[] u32 = l == *r; impl[] i32 = l == *r; impl[] u64 = l == *r; impl[] i64 = l == *r; impl[] u128 = l == *r; impl[] i128 = l == *r; impl[] usize = l == *r; impl[] isize = l == *r; impl[] bool = l == *r; impl[] char = l == *r; impl[] &str = crate::slice_cmp::str_eq(l, r); } macro_rules! impl_eq_for_option { ( (l=$l:ident, r=$r:ident) $( impl[$($impl_:tt)*] $type:ty = $comparison:expr; )* ) => ( $( impl<$($impl_)*> PWrapper<$type> { /// This method is only available with the "assert" feature. pub const fn const_eq(&self, $r:&$type) -> bool { let $l = self.0; $comparison } } )* impl_eq_for_option_prim! { (l=$l, r=$r) $( impl[$($impl_)*] $type = $comparison; )* } ) } impl_eq_for_option! { (l=l, r=r) impl[] NonZeroU8 = l.get() == r.get(); impl[] NonZeroI8 = l.get() == r.get(); impl[] NonZeroU16 = l.get() == r.get(); impl[] NonZeroI16 = l.get() == r.get(); impl[] NonZeroU32 = l.get() == r.get(); impl[] NonZeroI32 = l.get() == r.get(); impl[] NonZeroU64 = l.get() == r.get(); impl[] NonZeroI64 = l.get() == r.get(); impl[] NonZeroU128 = l.get() == r.get(); impl[] NonZeroI128 = l.get() == r.get(); impl[] NonZeroUsize = l.get() == r.get(); impl[] NonZeroIsize = l.get() == r.get(); } macro_rules! impl_equality { ( (l=$l:ident, r=$r:ident) $( impl[$($impl_:tt)*] $type:ty = $comparison:expr ;)* ) => ( $( impl<$($impl_)*> PWrapper<$type> { /// This method is only available with the "assert" feature. #[inline(always)] pub const fn const_eq(&self, $r: &$type) -> bool { let $l = &self.0; $comparison } } )* ) } impl_equality! { (l=l, r=r) impl[T: ?Sized,] PhantomData = true; impl[] PhantomPinned = true; impl[] () = true; impl[] Ordering = *l as u8 == *r as u8; impl[] AtomicOrdering = *l as u8 == *r as u8; impl[] Range = l.start == r.start && l.end == r.end; impl[] RangeInclusive = *l.start() == *r.start() && *l.end() == *r.end(); impl[] RangeFrom = l.start == r.start; impl[] RangeFull = true; impl[] RangeTo = l.end == r.end; impl[] RangeToInclusive = l.end == r.end; } const_format-0.2.32/src/fmt/error.rs000064400000000000000000000052201046102023000154610ustar 00000000000000// <_< clippy you silly #![allow(clippy::enum_variant_names)] use core::fmt::{self, Display}; /// An error while trying to write into a StrWriter. #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Error { /// Attempted to write something into the buffer when there isn't enough space to write it. NotEnoughSpace, /// For compatibility with [`NotAsciiError`](../wrapper_types/struct.NotAsciiError.html) NotAscii, /// Attempted to index a string arguent by an range where one of the bounds /// was not on a char boundary. NotOnCharBoundary, } impl Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { Self::NotEnoughSpace => { fmt.write_str("The was not enough space to write the formatted output") } Self::NotAscii => fmt.write_str("Attempted to write non-ascii text"), Self::NotOnCharBoundary => { fmt.write_str("Attempted to index a byte that's not on a char boundary.") } } } } macro_rules! index_vars{ ($self:ident, $index:ident; $($variant:ident),* $(,)? ) => ( enum Index{ $($variant,)* } let $index = match &$self { $(Error::$variant{..} => 3300 + Index::$variant as usize,)* }; ) } impl Error { /// For panicking at compile-time, with a compile-time error that says what the error is. #[track_caller] pub const fn unwrap(&self) -> T { index_vars! { self,i; NotEnoughSpace, NotAscii, NotOnCharBoundary, }; match self { Error::NotEnoughSpace => ["The was not enough space to write the formatted output"][i], Error::NotAscii => ["Attempted to write non-ascii text"][i], Error::NotOnCharBoundary => { ["Attempted to index a byte that's not on a char boundary."][i] } }; loop {} } } //////////////////////////////////////////////////////////////////////////////// /// The return type of most formatting functions pub type Result = core::result::Result; //////////////////////////////////////////////////////////////////////////////// /// For converting types to [`const_format::Result`] /// /// [`const_format::Result`]: ./type.Result.html pub struct ToResult(pub T); impl ToResult<()> { /// #[inline(always)] pub const fn to_result(self) -> Result { Ok(()) } } impl ToResult { /// #[inline(always)] pub const fn to_result(self) -> Result { self.0 } } const_format-0.2.32/src/fmt/formatter.rs000064400000000000000000001317161046102023000163450ustar 00000000000000use crate::{ fmt::{Error, FormattingFlags, NoEncoding, StrWriter, StrWriterMut}, utils::saturate_range, wrapper_types::{AsciiStr, PWrapper}, }; use core::ops::Range; //////////////////////////////////////////////////////////////////////////////// /// For computing how long a formatted string would be. /// /// This is what the [`formatc`] macro uses to precalculate the length of its returned `&str`. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::fmt::{ComputeStrLength, Error, Formatter, FormattingFlags, StrWriter}; /// use const_format::{try_, writec, unwrap}; /// /// const fn write_sum(mut f: Formatter<'_>) -> Result<(), Error> { /// let l = 7u8; /// let r = 8u8; /// writec!(f, "{} + {} = {}", l, r, l + r) /// } /// /// const LEN: usize = { /// let mut computer = ComputeStrLength::new(); /// unwrap!(write_sum(computer.make_formatter(FormattingFlags::NEW))); /// computer.len() /// }; /// /// // The type annotation coerces a `&mut StrWriter<[u8; LEN]>` /// // to a `&mut StrWriter<[u8]>` (the type parameter defaults to `[u8]`) /// let writer: &mut StrWriter = &mut StrWriter::new([0; LEN]); /// /// write_sum(writer.make_formatter(FormattingFlags::NEW)).unwrap(); /// /// assert_eq!(writer.as_str(), "7 + 8 = 15"); /// assert_eq!(writer.len(), LEN); /// assert_eq!(writer.capacity(), LEN); /// /// ``` /// /// [`formatc`]: ../macro.formatc.html /// /// pub struct ComputeStrLength { len: usize, } impl ComputeStrLength { /// Constructs a ComputeStrLength of length 0. pub const fn new() -> Self { Self { len: 0 } } /// Constructs a `Formatter`, /// which instead of writing to a buffer it adds the computed length into this. pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { Formatter { margin: 0, flags, writer: WriterBackend::Length(self), } } /// Adds `len` to the calculated length. pub const fn add_len(&mut self, len: usize) { self.len += len; } /// The length of the string when formatted. pub const fn len(&self) -> usize { self.len } /// Whether the length of the computed string is zero. pub const fn is_empty(&self) -> bool { self.len == 0 } /// For borrowing this mutably in macros,just takes and returns a `&mut Self`. #[inline(always)] pub const fn borrow_mutably(&mut self) -> &mut Self { self } } //////////////////////////////////////////////////////////////////////////////// enum WriterBackend<'w> { Str(StrWriterMut<'w, NoEncoding>), Length(&'w mut ComputeStrLength), } //////////////////////////////////////////////////////////////////////////////// /// A handle for writing formatted output. /// /// `Formatter` writes utf8 encoded text, it can't be used to write arbitrary bytes. /// /// # FormattingFlags /// /// Types can change how they're formatted based on the value returned by `.flags()`, /// for more details on that you can read the documentation for [`FormattingFlags`]. /// /// # Construction /// /// This type can be constructed in these ways: /// /// - From a pair of mutable reference to a [`StrWriter`] and a [`FormattingFlags`], /// with the [`from_sw`] constructor. /// /// - From a pair of [`StrWriterMut`] and [`FormattingFlags`], /// with the [`from_sw_mut`] constructor. /// /// - From a [`ComputeStrLength`], by calling its /// [`make_formatter`](./struct.ComputeStrLength.html#method.make_formatter) method. /// This allows computing the length of formatted output without writing to anything. /// /// - From a triple of `[u8]` and `usize` mutable references, and a [`FormattingFlags`], /// with the [`from_custom_cleared`] constructor, /// or the [`from_custom`] constructor. /// /// # Errors /// /// The `write_*` methods can only return an `Error::NotEnoughSpace`, /// when they do, the formatter was not written to, so you can try again with a shorter input. /// /// In the case of the `debug_*` methods / the `Debug*` structs, /// they can return a `Error::NotEnoughSpace` when their `finish` method is called, /// not as soon as it happens. /// /// # Examples /// /// ### Display formatting /// /// This example demonstrates how you can do display formatting with a Formatter. /// /// If you want to write a braced struct/variant you can use [`DebugStruct`], /// or [`DebugTuple`] for tuple structs/variants. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter}; /// use const_format::{impl_fmt, try_}; /// /// struct Foo; /// /// impl_fmt!{ /// impl[] Foo; /// /// const fn const_display_fmt(&self, mut f: Formatter<'_>) -> Result<(), Error> { /// let string = "foo bar baz"; /// try_!(f.write_u8_display(100)); /// try_!(f.write_str(" ")); /// try_!(f.write_str_range(string, 4..7)); /// try_!(f.write_str("\n\n\n...figters")); /// Ok(()) /// } /// } /// /// // We have to coerce `&mut StrWriter<[u8; 256]>` to `&mut StrWriter` to call the /// // `make_formatter` method. /// let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); /// /// let flags = FormattingFlags::NEW.set_binary(); /// /// // The Display formatters from this crate don't care which NumberFormatting you pass, /// // they'll just write integers as decimal. /// Foo.const_display_fmt(writer.make_formatter(flags)); /// /// assert_eq!(writer.as_str(), "100 bar\n\n\n...figters"); /// ``` /// /// /// ### Writing to an array /// /// This example demonstrates how you can use a Formatter to write to a byte slice. /// /// You can use the [`from_custom`] constructor if you need to start writing from /// anywhere other than 0. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter}; /// use const_format::{impl_fmt, try_, writec}; /// /// const fn write_int(int: u32, buffer: &mut [u8]) -> Result { /// let mut len = 0; /// let mut f = Formatter::from_custom_cleared(buffer, &mut len, FormattingFlags::NEW); /// try_!(writec!(f, "{0},{0:x},{0:b}", int)); /// Ok(len) /// } /// /// let mut buffer = [0;64]; /// /// let written = write_int(17, &mut buffer).unwrap(); /// /// let string = std::str::from_utf8(&buffer[..written]) /// .expect("Formatter only writes valid UTF8"); /// /// assert_eq!(string, "17,11,10001"); /// /// ``` /// /// /// [`DebugStruct`]: crate::fmt::DebugStruct /// [`DebugTuple`]: crate::fmt::DebugTuple /// [`StrWriter`]: crate::fmt::StrWriter /// [`StrWriterMut`]: crate::fmt::StrWriterMut /// [`ComputeStrLength`]: crate::fmt::ComputeStrLength /// [`from_sw`]: #method.from_sw /// [`from_sw_mut`]: #method.from_sw_mut /// [`from_custom_cleared`]: #method.from_custom_cleared /// [`from_custom`]: #method.from_custom /// [`NumberFormatting`]: crate::fmt::NumberFormatting /// [`FormattingFlags`]: crate::fmt::FormattingFlags /// pub struct Formatter<'w> { margin: u16, flags: FormattingFlags, writer: WriterBackend<'w>, } const MARGIN_STEP: u16 = 4; impl<'w> Formatter<'w> { /// Constructs a `Formatter`. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter}; /// use const_format::try_; /// /// const fn inner(mut f: Formatter<'_>) -> Result<(), Error> { /// try_!(f.write_str_range("ABCDEF", 2..4)); /// try_!(f.write_str(" N")); /// try_!(f.write_ascii_repeated(b'o', 10)); /// Ok(()) /// } /// /// // We have to coerce `&mut StrWriter<[u8; 128]>` to `&mut StrWriter` to call the /// // `as_str` method. /// let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); /// inner(Formatter::from_sw(writer, FormattingFlags::NEW)).unwrap(); /// /// assert_eq!(writer.as_str(), "CD Noooooooooo"); /// /// ``` #[inline] pub const fn from_sw(writer: &'w mut StrWriter, flags: FormattingFlags) -> Self { Self { margin: 0, flags, // safety: // Formatter only writes valid utf8, which is valid for both // encoding type parameters that StrWriterMut can have(Utf8Encoding / NoEncoding). writer: WriterBackend::Str(unsafe { writer.as_mut().into_byte_encoding() }), } } /// Constructs a `Formatter`. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriterMut}; /// use const_format::try_; /// /// const fn inner(mut f: Formatter<'_>) -> Result<(), Error> { /// try_!(f.write_str_range("DVDVDVD", 2..5)); /// try_!(f.write_str(" N")); /// try_!(f.write_ascii_repeated(b'o', 10)); /// Ok(()) /// } /// /// let mut len = 0; /// let mut buffer = [0; 128]; /// /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// // We need to call `.reborrow()`, because otherwise the `StrWriterMut` is moved. /// inner(Formatter::from_sw_mut(writer.reborrow(), FormattingFlags::NEW)).unwrap(); /// /// assert_eq!(writer.as_str(), "DVD Noooooooooo"); /// /// ``` #[inline] pub const fn from_sw_mut( writer: StrWriterMut<'w, E>, flags: FormattingFlags, ) -> Self { Self { margin: 0, flags, // safety: // Formatter only writes valid utf8, which is valid for both // encoding type parameters that StrWriterMut can have(Utf8Encoding / NoEncoding). writer: WriterBackend::Str(unsafe { writer.into_byte_encoding() }), } } /// Construct a `Formatter` from a byte slice. /// /// `Formatter` only writes utf8, which means that if `&buffer[..length]` is valid utf8, /// then `buffer` will continue to be `utf8` after being written by the `Formatter`. /// /// # Example /// /// This example demonstrates how you can use a Formatter to write to a byte slice /// that had some text written to it already. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter}; /// use const_format::{impl_fmt, try_, writec}; /// /// /// /// /// # Safety /// /// /// /// `&buffer[..start]` must be valid utf8. /// const fn write_int( /// int: u32, /// buffer: &mut [u8], /// start: usize, /// ) -> Result { /// let mut len = start; /// let mut f = Formatter::from_custom(buffer, &mut len, FormattingFlags::NEW); /// try_!(writec!(f, "{0},{0:x},{0:b}", int)); /// Ok(len) /// } /// /// let start_str = "The number is "; /// let mut buffer = [0;64]; /// buffer[..start_str.len()].copy_from_slice(start_str.as_bytes()); /// /// let new_len = write_int(20, &mut buffer, start_str.len()).unwrap(); /// /// let string = std::str::from_utf8(&buffer[..new_len]) /// .expect("Formatter only writes valid UTF8"); /// /// assert_eq!(string, "The number is 20,14,10100"); /// /// ``` #[inline] pub const fn from_custom( buffer: &'w mut [u8], length: &'w mut usize, flags: FormattingFlags, ) -> Self { Self { margin: 0, flags, writer: WriterBackend::Str(StrWriterMut::from_custom(buffer, length)), } } /// Construct a `Formatter`from a byte slice. /// /// # Example /// /// For an example of using this method you can look at /// [the type level docs](#write_array_example) /// #[inline] pub const fn from_custom_cleared( buffer: &'w mut [u8], length: &'w mut usize, flags: FormattingFlags, ) -> Self { *length = 0; Self { margin: 0, flags, writer: WriterBackend::Str(StrWriterMut::from_custom(buffer, length)), } } /// Gets the formatting flags associated with this `Formatter`. #[inline(always)] pub const fn flags(&self) -> FormattingFlags { self.flags } /// Gets how much indentation a data structure is printed with. pub const fn margin(&self) -> usize { self.margin as usize } #[inline(always)] const fn increment_margin(&mut self) -> &mut Self { self.margin += 4; self } #[inline(always)] const fn decrement_margin(&mut self) { self.margin -= 4; } } impl<'w> Formatter<'w> { /// For borrowing this mutably in macros,just takes and returns a `&mut Self`. #[inline(always)] pub const fn borrow_mutably(&mut self) -> &mut Self { self } /// Constructs a reborrow of this formatter, using `flags` as the formatting flags. /// /// The return value inherits the margin from this Formatter. /// /// This method exists because the [`writec`] macro gets a formatter from any writer /// by calling a `make_formatter` method. /// /// # Example /// /// This example demonstrates how you can change the flags when writing a field. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, PWrapper}; /// use const_format::{coerce_to_fmt, formatc, impl_fmt, try_}; /// /// use std::ops::RangeInclusive; /// /// struct Foo{ /// x: u32, /// y: RangeInclusive, /// z: u32, /// } /// /// impl_fmt!{ /// impl Foo; /// /// pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let mut f = f.debug_struct("Foo"); /// try_!(PWrapper(self.x).const_debug_fmt(f.field("x"))); /// /// let mut fmt_y = f.field("y"); /// let flags = fmt_y.flags().set_binary(); /// try_!(coerce_to_fmt!(&self.y).const_debug_fmt(&mut fmt_y.make_formatter(flags))); /// /// try_!(PWrapper(self.z).const_debug_fmt(f.field("z"))); /// f.finish() /// } /// } /// /// const FOO: Foo = Foo { /// x: 15, /// y: 16..=31, /// z: 32, /// }; /// const S: &str = formatc!("{FOO:#?}"); /// /// const EXPECTED: &str = "\ /// Foo { /// x: 15, /// y: 0b10000..=0b11111, /// z: 32, /// }\ /// "; /// /// assert_eq!(S, EXPECTED); /// ``` /// /// [`writec`]: ../macro.writec.html /// pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { Formatter { margin: self.margin, flags, writer: match &mut self.writer { WriterBackend::Str(x) => WriterBackend::Str(x.reborrow()), WriterBackend::Length(x) => WriterBackend::Length(x), }, } } /// For debug writing a braced struct, or braced variant, /// taking its name as a parameter /// /// # Examples /// /// For examples of using this method, you can look at the docs for [`DebugStruct`] /// /// [`DebugStruct`]: ./struct.DebugStruct.html /// #[inline] pub const fn debug_struct(&mut self, name: &str) -> DebugStruct<'_, 'w> { let err = self.write_str(name); DebugStruct { fmt: self.increment_margin(), wrote_field: false, err, } } /// For debug writing a tuple struct, or tuple variant,taking its name as a parameter /// /// # Examples /// /// For examples of using this method, you can look at the docs for [`DebugTuple`] /// /// [`DebugTuple`]: ./struct.DebugTuple.html /// #[inline] pub const fn debug_tuple(&mut self, name: &str) -> DebugTuple<'_, 'w> { let err = self.write_str(name); DebugTuple { fmt: self.increment_margin(), wrote_field: false, err, } } /// For debug writing a list/array. /// /// # Examples /// /// For examples of using this method, you can look at the docs for [`DebugList`] /// /// [`DebugList`]: ./struct.DebugList.html /// #[inline] pub const fn debug_list(&mut self) -> DebugList<'_, 'w> { DebugList { fmt: self.increment_margin(), wrote_field: false, err: Ok(()), } } /// For debug writing a set. /// /// # Examples /// /// For examples of using this method, you can look at the docs for [`DebugSet`] /// /// [`DebugSet`]: ./struct.DebugSet.html /// #[inline] pub const fn debug_set(&mut self) -> DebugSet<'_, 'w> { DebugSet { fmt: self.increment_margin(), wrote_field: false, err: Ok(()), } } } //////////////////////////////////////////////////////////////////////////////// macro_rules! trys { ($e:expr,$self:ident) => { if let result @ Err(_) = $e { $self.err = result; } }; } const COLON_SPACE_LEN: usize = ": ".len(); const COMMA_SPACE_LEN: usize = ", ".len(); const COMMA_NL_LEN: usize = ",\n".len(); macro_rules! field_method_impl { ($ self: ident, $open_space:expr, $open_newline:expr; len(|$fmt_len:ident| $($write_name_len:tt)*) fmt(|$writer:ident| $($write_name_fmt:tt)*) ) => ({ match &mut $self.fmt.writer { WriterBackend::Length($fmt_len)=>{ let $fmt_len = &mut **$fmt_len; const OPEN_SPACE: usize = $open_space.len(); const OPEN_NEWLINE: usize = $open_newline.len(); let is_alternate = $self.fmt.flags.is_alternate(); $fmt_len.add_len(match ($self.wrote_field, is_alternate) { (false, false) => OPEN_SPACE, (false, true) => OPEN_NEWLINE + $self.fmt.margin as usize, (true , false) => COMMA_SPACE_LEN, (true , true) => COMMA_NL_LEN + $self.fmt.margin as usize, }); $($write_name_len)* } WriterBackend::Str($writer)=>{ let $writer = &mut *$writer; let is_alternate = $self.fmt.flags.is_alternate(); let sep = match ($self.wrote_field, is_alternate) { (false, false)=>$open_space, (false, true)=>$open_newline, (true, false)=>", ", (true, true)=>",\n", }; trys!($writer.write_str(sep), $self); if is_alternate { trys!($writer.write_ascii_repeated(b' ', $self.fmt.margin as usize), $self); } $($write_name_fmt)* } } $self.wrote_field = true; $self.fmt }) } macro_rules! finish_method_impl { ($self: ident, $close_token:expr, $space_close:expr) => {{ if let result @ Err(_) = $self.err { return result; } $self.fmt.decrement_margin(); if $self.wrote_field { match &mut $self.fmt.writer { WriterBackend::Length(fmt_len) => { let fmt_len = &mut **fmt_len; const CLOSE_TOKEN: usize = $close_token.len(); const SPACE_CLOSE: usize = $space_close.len(); if $self.fmt.flags.is_alternate() { fmt_len.add_len(COMMA_NL_LEN + $self.fmt.margin as usize + CLOSE_TOKEN); } else { fmt_len.add_len(SPACE_CLOSE); } Ok(()) } WriterBackend::Str(writer) => { let writer = &mut *writer; if $self.fmt.flags.is_alternate() { try_!(writer.write_str(",\n")); try_!(writer.write_ascii_repeated(b' ', $self.fmt.margin as usize)); writer.write_str($close_token) } else { writer.write_str($space_close) } } } } else { Ok(()) } }}; } //////////////////////////////////////////////////////////////////////////////// /// A helper struct for debug formatting a braced struct, or braced variant. /// /// # Example /// /// This example demonstrates how you can debug format a struct, /// and a braced variant. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter}; /// use const_format::{call_debug_fmt, coerce_to_fmt, formatc, impl_fmt, try_}; /// /// fn main() { /// const STRUC: &str = formatc!("{:?}", Foo { a: 5, b: [8, 13, 21], c: "34" }); /// const ENUM_: &str = formatc!("{:?}", Bar::Baz { d: false, e: None }); /// /// assert_eq!(STRUC, "Foo { a: 5, b: [8, 13, 21], c: \"34\" }"); /// assert_eq!(ENUM_, "Baz { d: false, e: None }"); /// } /// /// struct Foo{ /// a: u32, /// b: [u32; 3], /// c: &'static str, /// } /// /// enum Bar { /// Baz{ /// d: bool, /// e: Option, /// } /// } /// /// impl_fmt!{ /// impl Foo; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let mut f = f.debug_struct("Foo"); /// try_!(coerce_to_fmt!(&self.a).const_debug_fmt(f.field("a"))); /// try_!(coerce_to_fmt!(&self.b).const_debug_fmt(f.field("b"))); /// try_!(coerce_to_fmt!(&self.c).const_debug_fmt(f.field("c"))); /// f.finish() /// } /// } /// /// impl_fmt!{ /// impl Bar; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// match self { /// Bar::Baz{d, e} => { /// let mut f = f.debug_struct("Baz"); /// /// // This macro allows debug formatting some generic types that /// // don't have a const_debug_fmt fn, like Options which wrap non-std types. /// call_debug_fmt!(std, d, f.field("d")); /// call_debug_fmt!(Option, e, f.field("e")); /// /// f.finish() /// } /// } /// } /// } /// /// /// /// ``` pub struct DebugStruct<'f, 'w> { fmt: &'f mut Formatter<'w>, wrote_field: bool, err: Result<(), Error>, } impl<'f, 'w> DebugStruct<'f, 'w> { /// Adds a field to the formatted output. pub const fn field(&mut self, name: &str) -> &mut Formatter<'w> { field_method_impl!( self, " { ", " {\n"; len(|fmt_len| fmt_len.add_len(name.len() + COLON_SPACE_LEN); ) fmt(|writer| trys!(writer.write_str(name), self); trys!(writer.write_str(": "), self); ) ) } /// Finishes writing the struct/variant, /// and if anything went wrong in the `field` method,returns an error. pub const fn finish(self) -> Result<(), Error> { finish_method_impl!(self, "}", " }") } } //////////////////////////////////////////////////////////////////////////////// /// For debug formatting a tuple struct, or tuple variant. /// /// # Example /// /// This example demonstrates how you can debug format a tuple struct, /// and an enum of tuple variants. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter}; /// use const_format::{call_debug_fmt, coerce_to_fmt, formatc, impl_fmt, try_}; /// /// fn main() { /// const STRUC: &str = formatc!("{:?}", Foo(5, [8, 13, 21], "34")); /// const ENUM_: &str = formatc!("{:?}", Bar::Baz(false, None)); /// /// assert_eq!(STRUC, "Foo(5, [8, 13, 21], \"34\")"); /// assert_eq!(ENUM_, "Baz(false, None)"); /// } /// /// struct Foo(u32, [u32; 3], &'static str); /// /// enum Bar { /// Baz(bool, Option), /// } /// /// impl_fmt!{ /// impl Foo; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let mut f = f.debug_tuple("Foo"); /// try_!(coerce_to_fmt!(&self.0).const_debug_fmt(f.field())); /// try_!(coerce_to_fmt!(&self.1).const_debug_fmt(f.field())); /// try_!(coerce_to_fmt!(&self.2).const_debug_fmt(f.field())); /// f.finish() /// } /// } /// /// impl_fmt!{ /// impl Bar; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// match self { /// Bar::Baz(f0, f1) => { /// let mut f = f.debug_tuple("Baz"); /// /// // This macro allows debug formatting some generic types that /// // don't have a const_debug_fmt fn, like Options which wrap non-std types. /// call_debug_fmt!(std, f0, f.field()); /// call_debug_fmt!(Option, f1, f.field()); /// /// f.finish() /// } /// } /// } /// } /// /// /// /// ``` pub struct DebugTuple<'f, 'w> { fmt: &'f mut Formatter<'w>, wrote_field: bool, err: Result<(), Error>, } impl<'f, 'w> DebugTuple<'f, 'w> { /// Adds a field to the formatted output. pub const fn field(&mut self) -> &mut Formatter<'w> { field_method_impl!(self, "(", "(\n"; len(|fmt_len|) fmt(|writer|) ) } /// Finishes writing the tuple struct/variant, /// and if anything went wrong in the `field` method,returns an error. pub const fn finish(self) -> Result<(), Error> { finish_method_impl!(self, ")", ")") } } //////////////////////////////////////////////////////////////////////////////// macro_rules! finish_listset_method_impl { ($self: ident, $close_token:expr, $open_close:expr) => {{ if let result @ Err(_) = $self.err { return result; } match &mut $self.fmt.writer { WriterBackend::Length(fmt_len) => { let fmt_len = &mut **fmt_len; const CLOSE_TOKEN: usize = $close_token.len(); const OPEN_CLOSE: usize = $open_close.len(); $self.fmt.margin -= MARGIN_STEP; if $self.wrote_field { if $self.fmt.flags.is_alternate() { fmt_len.add_len(COMMA_NL_LEN + $self.fmt.margin as usize); } fmt_len.add_len(CLOSE_TOKEN); } else { fmt_len.add_len(OPEN_CLOSE); } Ok(()) } WriterBackend::Str(writer) => { let writer = &mut *writer; $self.fmt.margin -= MARGIN_STEP; let margin = $self.fmt.margin as usize; if $self.wrote_field { if $self.fmt.flags.is_alternate() { try_!(writer.write_str(",\n")); try_!(writer.write_ascii_repeated(b' ', margin)); } writer.write_str($close_token) } else { writer.write_str($open_close) } } } }}; } //////////////////////////////////////////////////////////////////////////////// /// For debug formatting a list/array. /// /// # Example /// /// This example demonstrates how you can debug format a custom type as a list. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter}; /// use const_format::{formatc, impl_fmt, try_}; /// /// use std::ops::Range; /// /// fn main() { /// const LIST: &str = formatc!("{:?}", RangeList(0..5)); /// /// assert_eq!(LIST, "[0, 1, 2, 3, 4]"); /// } /// /// struct RangeList(Range); /// /// impl_fmt!{ /// impl RangeList; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let mut f = f.debug_list(); /// let mut i = self.0.start; /// while i < self.0.end { /// try_!(f.entry().write_usize_display(i)); /// i+=1; /// } /// f.finish() /// } /// } /// /// ``` /// pub struct DebugList<'f, 'w> { fmt: &'f mut Formatter<'w>, wrote_field: bool, err: Result<(), Error>, } impl<'f, 'w> DebugList<'f, 'w> { /// Adds a list entry to the formatted output pub const fn entry(&mut self) -> &mut Formatter<'w> { field_method_impl!(self, "[", "[\n"; len(|fmt_len|) fmt(|writer|) ) } /// Finishes writing the list, /// and if anything went wrong in the `entry` method,returns an error. pub const fn finish(self) -> Result<(), Error> { finish_listset_method_impl!(self, "]", "[]") } } //////////////////////////////////////////////////////////////////////////////// /// For debug formatting a set. /// /// # Example /// /// This example demonstrates how you can debug format a custom type as a set. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter}; /// use const_format::{formatc, impl_fmt, try_}; /// /// use std::ops::Range; /// /// fn main() { /// const SET: &str = formatc!("{:?}", StrSet(&["foo", "bar", "baz"])); /// /// assert_eq!(SET, r#"{"foo", "bar", "baz"}"#); /// } /// /// struct StrSet(&'static [&'static str]); /// /// impl_fmt!{ /// impl StrSet; /// /// const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let mut f = f.debug_set(); /// let mut i = 0; /// while i < self.0.len() { /// try_!(f.entry().write_str_debug(self.0[i])); /// i+=1; /// } /// f.finish() /// } /// } /// /// ``` /// pub struct DebugSet<'f, 'w> { fmt: &'f mut Formatter<'w>, wrote_field: bool, err: Result<(), Error>, } impl<'f, 'w> DebugSet<'f, 'w> { /// Adds a set entry to the formatted output pub const fn entry(&mut self) -> &mut Formatter<'w> { field_method_impl!(self, "{", "{\n"; len(|fmt_len|) fmt(|writer|) ) } /// Finishes writing the set, /// and if anything went wrong in the `entry` method,returns an error. pub const fn finish(self) -> Result<(), Error> { finish_listset_method_impl!(self, "}", "{}") } } //////////////////////////////////////////////////////////////////////////////// macro_rules! delegate_write_methods { ( shared_attrs $shared_attrs:tt $( $(#[$attrs:meta])* fn $method:ident($($arg:ident: $arg_ty:ty ),* $(,)* ) length = $len:expr; )* ) => ( impl Formatter<'_>{ $( delegate_write_methods!{ @inner shared_attrs $shared_attrs $(#[$attrs])* fn $method($($arg: $arg_ty ),* ) length = $len; } )* } ); ( @inner shared_attrs ( $( #[$shared_attrs:meta] )* ) $(#[$attrs:meta])* fn $method:ident($($arg:ident: $arg_ty:ty ),* $(,)* ) length = $len:expr; ) => ( $( #[$shared_attrs] )* $(#[$attrs])* pub const fn $method(&mut self, $($arg: $arg_ty ),* ) -> Result<(), Error> { match &mut self.writer { WriterBackend::Length(fmt_len)=>{ fmt_len.add_len($len); Ok(()) } WriterBackend::Str(writer)=>{ writer.$method($($arg,)*) } } } ) } delegate_write_methods! { shared_attrs() /// Writes `&string[range]` into this Formatter. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_str_range("FOO BAR BAZ", 4..7); /// /// assert_eq!(writer.as_str(), "BAR"); /// /// ``` /// fn write_str_range(string: &str, range: Range) length = calculate_display_len(string.as_bytes(), &range); /// Writes `string` into this Formatter. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_str("FOO BAR BAZ"); /// /// assert_eq!(writer.as_str(), "FOO BAR BAZ"); /// /// ``` /// fn write_str(string: &str) length = string.len(); /// Writes `character` into this Formatter. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 4]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_char('a'); /// let _ = fmt.write_char('b'); /// let _ = fmt.write_char('c'); /// /// assert_eq!(writer.as_str(), "abc"); /// /// ``` /// fn write_char(character: char) length = crate::char_encoding::char_display_len(character); /// Writes `&ascii[range]` into this formatter. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter, ascii_str}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_ascii_range(ascii_str!("FOO BAR BAZ"), 4..7); /// /// assert_eq!(writer.as_str(), "BAR"); /// /// ``` /// fn write_ascii_range(ascii: AsciiStr<'_>, range: Range) length = calculate_display_len(ascii.as_bytes(), &range); /// Writes `ascii` into this formatter. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter, ascii_str}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_ascii(ascii_str!("FOO BAR BAZ")); /// /// assert_eq!(writer.as_str(), "FOO BAR BAZ"); /// /// ``` /// fn write_ascii(ascii: AsciiStr<'_>) length = ascii.len(); /// Writes the ascii `character` into this formatter `repeated` times. /// /// If `character` is greater than 127, /// this writes `character - 128` as an ascii character. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_ascii_repeated(b'A', 10); /// /// assert_eq!(writer.as_str(), "AAAAAAAAAA"); /// /// ``` /// fn write_ascii_repeated(character: u8,repeated: usize) length = repeated; /// Writes `string` into this formatter, with debug formatting. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_str_range_debug("FOO\nBAR\tBAZ", 3..8); /// /// assert_eq!(writer.as_str(), r#""\nBAR\t""#); /// /// ``` /// fn write_str_range_debug(string: &str, range: Range) length = calculate_display_len_debug_range(string.as_bytes(), &range); /// Writes `string` into this formatter, with debug formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_str_debug("FOO\nBAR\tBAZ"); /// /// assert_eq!(writer.as_str(), r#""FOO\nBAR\tBAZ""#); /// /// ``` /// fn write_str_debug(string: &str) length = PWrapper(string.as_bytes()).compute_utf8_debug_len(); /// Writes `character` into this Formatter, with debug formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_str(" "); /// let _ = fmt.write_char_debug('\\'); /// let _ = fmt.write_str(" "); /// let _ = fmt.write_char_debug('A'); /// let _ = fmt.write_str(" "); /// let _ = fmt.write_char_debug('0'); /// let _ = fmt.write_str(" "); /// let _ = fmt.write_char_debug('\''); /// let _ = fmt.write_str(" "); /// /// assert_eq!(writer.as_str(), r#" '\\' 'A' '0' '\'' "#); /// /// ``` /// fn write_char_debug(character: char) length = crate::char_encoding::char_debug_len(character); /// Writes `&ascii[range]` into this formatter, with debug formatting. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter, ascii_str}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_ascii_range_debug(ascii_str!("FOO\nBAR\tBAZ"), 3..8); /// /// assert_eq!(writer.as_str(), r#""\nBAR\t""#); /// /// ``` /// fn write_ascii_range_debug(ascii: AsciiStr<'_>,range: Range) length = calculate_display_len_debug_range(ascii.as_bytes(), &range); /// Writes `ascii` into this formatter, with debug formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter, ascii_str}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_ascii_debug(ascii_str!("FOO\nBAR\tBAZ")); /// /// assert_eq!(writer.as_str(), r#""FOO\nBAR\tBAZ""#); /// /// ``` /// fn write_ascii_debug(ascii: AsciiStr<'_>) length = PWrapper(ascii.as_bytes()).compute_utf8_debug_len(); /// Write `n` with display formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter, ascii_str}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 16]); /// let mut fmt = writer.make_formatter(FormattingFlags::NEW); /// /// let _ = fmt.write_u8_display(13); /// let _ = fmt.write_u8_display(21); /// let _ = fmt.write_u8_display(34); /// /// assert_eq!(writer.as_str(), "132134"); /// /// ``` /// fn write_u8_display(n: u8) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); } delegate_write_methods! { shared_attrs( /// Writes `n` with display formatting /// /// For an example, /// you can look at the one for the [`write_u8_display`] method. /// /// [`write_u8_display`]: #method.write_u8_display ) fn write_u16_display(n: u16) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_u32_display(n: u32) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_u64_display(n: u64) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_u128_display(n: u128) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_usize_display(n: usize) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_i8_display(n: i8) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_i16_display(n: i16) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_i32_display(n: i32) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_i64_display(n: i64) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_i128_display(n: i128) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); fn write_isize_display(n: isize) length = PWrapper(n).compute_display_len(FormattingFlags::NEW); } macro_rules! delegate_integer_debug_methods { ( shared_attrs $shared_attrs:tt $( $(#[$attrs:meta])* fn $method:ident($($arg:ident: $arg_ty:ty ),* $(,)* ) length = |$flags:ident| $len:expr; )* ) => ( impl Formatter<'_>{ $( delegate_integer_debug_methods!{ @inner shared_attrs $shared_attrs $(#[$attrs])* fn $method($($arg: $arg_ty ),*) length = |$flags| $len; } )* } ); ( @inner shared_attrs ( $( #[$shared_attrs:meta] )* ) $(#[$attrs:meta])* fn $method:ident($($arg:ident: $arg_ty:ty ),* $(,)* ) length = |$flags:ident| $len:expr; ) => ( $( #[$shared_attrs] )* $(#[$attrs])* pub const fn $method(&mut self, $($arg: $arg_ty ),* ) -> Result<(), Error> { let $flags = self.flags; match &mut self.writer { WriterBackend::Length(fmt_len)=>{ fmt_len.add_len($len); Ok(()) } WriterBackend::Str(writer)=>{ writer.$method($($arg,)* $flags) } } } ) } delegate_integer_debug_methods! { shared_attrs() /// Writes `n` with debug formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriter}; /// /// fn debug_fmt(writer: &mut StrWriter, flag: FormattingFlags) -> &str { /// writer.clear(); /// let mut fmt = Formatter::from_sw(writer, flag); /// let _ = fmt.write_u8_debug(63); /// writer.as_str() /// } /// /// let reg_flag = FormattingFlags::NEW.set_alternate(false); /// let alt_flag = FormattingFlags::NEW.set_alternate(true); /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// assert_eq!(debug_fmt(writer, reg_flag), "63" ); /// assert_eq!(debug_fmt(writer, reg_flag.set_hexadecimal()), "3F" ); /// assert_eq!(debug_fmt(writer, reg_flag.set_lower_hexadecimal()), "3f" ); /// assert_eq!(debug_fmt(writer, reg_flag.set_binary()), "111111" ); /// assert_eq!(debug_fmt(writer, alt_flag), "63" ); /// assert_eq!(debug_fmt(writer, alt_flag.set_hexadecimal()), "0x3F" ); /// assert_eq!(debug_fmt(writer, alt_flag.set_lower_hexadecimal()), "0x3f" ); /// assert_eq!(debug_fmt(writer, alt_flag.set_binary()), "0b111111"); /// /// ``` /// fn write_u8_debug(n: u8) length = |flags| PWrapper(n).compute_debug_len(flags); } delegate_integer_debug_methods! { shared_attrs( /// Writes `n` with debug formatting. /// /// For an example, /// you can look at the one for the [`write_u8_debug`] method. /// /// [`write_u8_debug`]: #method.write_u8_debug ) fn write_u16_debug(n: u16) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_u32_debug(n: u32) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_u64_debug(n: u64) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_u128_debug(n: u128) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_usize_debug(n: usize) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_i8_debug(n: i8) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_i16_debug(n: i16) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_i32_debug(n: i32) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_i64_debug(n: i64) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_i128_debug(n: i128) length = |flags| PWrapper(n).compute_debug_len(flags); fn write_isize_debug(n: isize) length = |flags| PWrapper(n).compute_debug_len(flags); } #[inline(always)] const fn calculate_display_len(b: &[u8], range: &Range) -> usize { let Range { start, end } = saturate_range(b, range); end - start } #[inline(always)] const fn calculate_display_len_debug_range(b: &[u8], range: &Range) -> usize { let Range { start, end } = saturate_range(b, range); PWrapper(b).compute_utf8_debug_len_in_range(start..end) } const_format-0.2.32/src/fmt/std_type_impls/ranges.rs000064400000000000000000000110521046102023000206460ustar 00000000000000use crate::{ fmt::{Error, Formatter}, marker_traits::{FormatMarker, IsAFormatMarker, IsStdKind}, wrapper_types::PWrapper, }; use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; //////////////////////////////////////////////////////////////////////////////// impl FormatMarker for Range { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker, T> { #[inline(always)] pub const fn coerce(self, range: &Range) -> PWrapper> { PWrapper(Range { start: range.start, end: range.end, }) } } impl PWrapper> { const RANGE: &'static str = ".."; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { try_!(PWrapper(self.0.start).const_debug_fmt(f)); try_!(PWrapper(Self::RANGE).const_display_fmt(f)); try_!(PWrapper(self.0.end).const_debug_fmt(f)); Ok(()) } } /////////////////////////////////////////////////////////////////////////////// impl FormatMarker for RangeFrom { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker, T> { #[inline(always)] pub const fn coerce(self, range: &RangeFrom) -> PWrapper> { PWrapper(RangeFrom { start: range.start }) } } impl PWrapper> { const RANGE: &'static str = ".."; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { try_!(PWrapper(self.0.start).const_debug_fmt(f)); try_!(PWrapper(Self::RANGE).const_display_fmt(f)); Ok(()) } } /////////////////////////////////////////////////////////////////////////////// impl FormatMarker for RangeTo { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker, T> { #[inline(always)] pub const fn coerce(self, range: &RangeTo) -> PWrapper> { PWrapper(RangeTo { end: range.end }) } } impl PWrapper> { const RANGE: &'static str = ".."; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { try_!(PWrapper(Self::RANGE).const_display_fmt(f)); try_!(PWrapper(self.0.end).const_debug_fmt(f)); Ok(()) } } /////////////////////////////////////////////////////////////////////////////// impl FormatMarker for RangeToInclusive { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker, T> { #[inline(always)] pub const fn coerce( self, range: &RangeToInclusive, ) -> PWrapper> { PWrapper(RangeToInclusive { end: range.end }) } } impl PWrapper> { const RANGE: &'static str = "..="; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { try_!(PWrapper(Self::RANGE).const_display_fmt(f)); try_!(PWrapper(self.0.end).const_debug_fmt(f)); Ok(()) } } /////////////////////////////////////////////////////////////////////////////// impl FormatMarker for RangeInclusive { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker, T> { #[inline(always)] pub const fn coerce(self, range: &RangeInclusive) -> PWrapper> { PWrapper(RangeInclusive::new(*range.start(), *range.end())) } } impl PWrapper> { const RANGE: &'static str = "..="; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { try_!(PWrapper(*self.0.start()).const_debug_fmt(f)); try_!(PWrapper(Self::RANGE).const_display_fmt(f)); try_!(PWrapper(*self.0.end()).const_debug_fmt(f)); Ok(()) } } /////////////////////////////////////////////////////////////////////////////// impl FormatMarker for RangeFull { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker { #[inline(always)] pub const fn coerce(self, _: &RangeFull) -> PWrapper { PWrapper(..) } } impl PWrapper { const RANGE: &'static str = ".."; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(Self::RANGE).const_display_fmt(f) } } const_format-0.2.32/src/fmt/std_type_impls.rs000064400000000000000000000145561046102023000174030ustar 00000000000000#![allow(missing_docs)] use crate::{ fmt::{Error, Formatter}, marker_traits::IsStdKind, wrapper_types::PWrapper, }; mod ranges; //////////////////////////////////////////////////////////////////////////////// impl PWrapper<&str> { pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str(self.0) } pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str_debug(self.0) } } impl PWrapper { pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str(if self.0 { "true" } else { "false" }) } #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { self.const_display_fmt(f) } } impl PWrapper { pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_char(self.0) } #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_char_debug(self.0) } } macro_rules! slice_of_std_impl {($($elem:ty),* $(,)?) => ( $( impl PWrapper<&[$elem]> { pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { let mut f = f.debug_list(); __for_range!{i in 0..self.0.len() => try_!(PWrapper(self.0[i]).const_debug_fmt(f.entry())); } f.finish() } } )* )} slice_of_std_impl! { &str, bool, char, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, } //////////////////////////////////////////////////////////////////////////////// use core::{ marker::{PhantomData, PhantomPinned}, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }, ptr::NonNull, }; impl_fmt! { is_std_type; impl[T,] Option>; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl[] Option; impl['a,] Option<&'a str>; pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self.0 { Some(x) => { let mut f = f.debug_tuple("Some"); try_!(PWrapper(x).const_debug_fmt(f.field())); f.finish() }, None => f.write_str("None"), } } } macro_rules! non_zero_impls { ($($ty:ident,)*) => ( $( std_kind_impl!{ impl[] $ty } impl PWrapper<$ty> { #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(self.0.get()).const_debug_fmt(f) } #[inline(always)] pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(self.0.get()).const_display_fmt(f) } } )* ) } non_zero_impls! { NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize, } std_kind_impl! { impl[T,] *mut T } // Unfortunately, can't print pointer addresses at compile-time. impl PWrapper<*mut T> { const PTR: &'static str = ""; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str(Self::PTR) } } std_kind_impl! { impl[T,] *const T } impl PWrapper<*const T> { #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(self.0 as *mut T).const_debug_fmt(f) } } std_kind_impl! { impl[T,] NonNull } impl PWrapper> { #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(self.0.as_ptr()).const_debug_fmt(f) } } macro_rules! impl_std_marker_type { ( $( impl[$($impl_:tt)*] $type:ty = $tyname:expr ;)* ) => ( $( std_kind_impl!{ impl[$($impl_)*] $type } impl<$($impl_)*> PWrapper<$type> { const NAME: &'static str = $tyname; #[inline(always)] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { PWrapper(Self::NAME).const_display_fmt(f) } } )* ) } impl_std_marker_type! { impl[T: ?Sized,] PhantomData = "PhantomData"; impl[] PhantomPinned = "PhantomPinned"; impl[] () = "()"; } //////////////////////////////////////////////////////////////////////////////// use core::{cmp::Ordering, sync::atomic::Ordering as AtomicOrdering}; impl_fmt! { is_std_type; impl AtomicOrdering; pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self.0 { AtomicOrdering::Relaxed => f.write_str("Relaxed"), AtomicOrdering::Release => f.write_str("Release"), AtomicOrdering::Acquire => f.write_str("Acquire"), AtomicOrdering::AcqRel => f.write_str("AcqRel"), AtomicOrdering::SeqCst => f.write_str("SeqCst"), _ => f.write_str(""), } } } impl_fmt! { is_std_type; impl Ordering; pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self.0 { Ordering::Less => f.write_str("Less"), Ordering::Equal => f.write_str("Equal"), Ordering::Greater => f.write_str("Greater"), } } } const_format-0.2.32/src/fmt/str_writer.rs000064400000000000000000000401051046102023000165350ustar 00000000000000use super::{Error, Formatter, FormattingFlags, StrWriterMut, Utf8Encoding}; use core::marker::PhantomData; //////////////////////////////////////////////////////////////////////////////// /// A wrapper over an array usable to build up a `&str` at compile-time. /// /// # Calling methods /// /// [Certain `StrWriter` methods](#certain-methods) require /// a `StrWriter<[u8]>` to be called, /// and since constructing `StrWriter` from an array produces a `StrWriter<[u8; N]>`, /// it must be cast to call them. /// /// `StrWriter`'s type parameter defaults to `[u8]`, /// so every instance of a `StrWriter` as a *type* is a `StrWriter<[u8]>`. /// /// Example of casting it: /// /// ```rust /// # use const_format::StrWriter; /// let writer: &mut StrWriter<[u8; 8]> = &mut StrWriter::new([0; 8]); /// /// // Casts `&StrWriter<[u8; 8]>` to `&StrWriter` /// writer.unsize(); /// /// // Casts `&StrWriter<[u8; 8]>` to `&StrWriter` /// writer.r(); /// /// // Coerces the `&mut StrWriter<[u8; 8]>` to `&mut StrWriter` /// let _writer: &mut StrWriter = writer; /// /// // Casts `&mut StrWriter<[u8; 8]>` to `StrWriterMut<'_>`, /// // which defines methods for mutating `StrWriter` /// let _writer = writer.as_mut(); /// /// # drop(writer); /// ``` /// /// # StrWriterMut /// /// `StrWriter` can be borrowed into a [`StrWriterMut`], /// which provides methods for writing a formatted string. /// /// Example: /// /// ```rust /// use const_format::StrWriter; /// /// let mut buffer: &mut StrWriter = &mut StrWriter::new([0; 100]); /// /// let mut writer = buffer.as_mut(); /// writer.write_str("Your password is: "); /// writer.write_str_debug("PASSWORD"); /// /// assert_eq!(writer.as_str(), r#"Your password is: "PASSWORD""#); /// /// ``` /// /// # Examples /// /// ### Formatting into associated constant /// /// This example shows how you can construct a formatted `&'static str` from associated constants. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, writec, unwrap}; /// /// trait Num { /// const V: u32; /// } /// /// struct Two; /// /// impl Num for Two { /// const V: u32 = 2; /// } /// /// struct Three; /// /// impl Num for Three { /// const V: u32 = 3; /// } /// /// struct Mul(L, R); /// /// const fn compute_str(l: u32, r: u32) -> StrWriter<[u8; 128]> { /// let mut writer = StrWriter::new([0; 128]); /// unwrap!(writec!(writer, "{} * {} == {}", l, r, l * r )); /// writer /// } /// /// impl Mul { /// const __STR: &'static StrWriter<[u8]> = &compute_str(L::V, R::V); /// const STR: &'static str = Self::__STR.as_str_alt(); /// } /// /// assert_eq!(Mul::::STR, "2 * 3 == 6"); /// assert_eq!(Mul::::STR, "3 * 3 == 9"); /// /// ``` /// /// [`StrWriterMut`]: ./struct.StrWriterMut.html /// #[derive(Debug, Copy, Clone)] pub struct StrWriter { pub(super) len: usize, pub(super) buffer: A, } impl StrWriter { /// Constructs a `StrWriter` from a `u8` array pub const fn new(array: A) -> Self { Self { len: 0, buffer: array, } } } impl StrWriter { /// Accesses the underlying buffer immutably. /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 7]); /// assert_eq!(buffer.buffer(), &[0; 7]); /// /// buffer.as_mut().write_str("foo")?; /// assert_eq!(buffer.buffer(), b"foo\0\0\0\0"); /// /// buffer.as_mut().write_str("bar")?; /// assert_eq!(buffer.buffer(), b"foobar\0"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn buffer(&self) -> &A { &self.buffer } /// How long the string this wrote is. /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// assert_eq!(buffer.len(), 0); /// /// buffer.as_mut().write_str("foo")?; /// assert_eq!(buffer.len(), 3); /// /// buffer.as_mut().write_str("bar")?; /// assert_eq!(buffer.len(), 6); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn len(&self) -> usize { self.len } /// Checks whether the string this wrote is empty. /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// assert!( buffer.is_empty() ); /// /// buffer.as_mut().write_str("foo")?; /// assert!( !buffer.is_empty() ); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn is_empty(&self) -> bool { self.len == 0 } } /// impl StrWriter { /// Gets the maximum length for a string written into this. /// /// Trying to write more that the capacity causes an error, /// returning back an `Err(Error::NotEnoughSpace)` /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter}; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// assert_eq!(buffer.capacity(), 64); /// /// buffer.as_mut().write_ascii_repeated(b'A', 64)?; /// assert_eq!(buffer.capacity(), 64); /// /// assert_eq!(buffer.as_mut().write_str("-").unwrap_err(), Error::NotEnoughSpace); /// assert_eq!(buffer.capacity(), 64); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn capacity(&self) -> usize { self.buffer.len() } /// Checks how many more bytes can be written. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter}; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// assert_eq!(buffer.remaining_capacity(), 64); /// /// buffer.as_mut().write_str("foo")?; /// assert_eq!(buffer.remaining_capacity(), 61); /// /// buffer.as_mut().write_ascii_repeated(b'a', 61)?; /// assert_eq!(buffer.remaining_capacity(), 0); /// /// assert_eq!(buffer.as_mut().write_str(" ").unwrap_err(), Error::NotEnoughSpace); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn remaining_capacity(&self) -> usize { self.buffer.len() - self.len } /// Truncates this `StrWriter` to `length`. /// /// If `length` is greater than the current length, this does nothing. /// /// # Errors /// /// Returns an `Error::NotOnCharBoundary` if `length` is not on a char boundary. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter}; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str("foo bâr baz"); /// assert_eq!(buffer.as_str(), "foo bâr baz"); /// /// assert_eq!(buffer.truncate(6).unwrap_err(), Error::NotOnCharBoundary); /// /// buffer.truncate(3)?; /// assert_eq!(buffer.as_str(), "foo"); /// /// buffer.as_mut().write_str("ooooooo"); /// assert_eq!(buffer.as_str(), "fooooooooo"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn truncate(&mut self, length: usize) -> Result<(), Error> { self.as_mut().truncate(length) } /// Truncates this `StrWriter` to length 0. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter}; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str("foo")?; /// assert_eq!(buffer.as_str(), "foo"); /// /// buffer.clear(); /// assert_eq!(buffer.as_str(), ""); /// assert!(buffer.is_empty()); /// /// buffer.as_mut().write_str("bar"); /// assert_eq!(buffer.as_str(), "bar"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn clear(&mut self) { self.len = 0; } /// Gets the written part of this `StrWriter` as a `&[u8]` /// /// The slice is guaranteed to be valid utf8, so this is mostly for convenience. /// /// ### Runtime /// /// If the "rust_1_64" feature is disabled, /// this takes time proportional to `self.capacity() - self.len()`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, StrWriterMut}; /// /// const fn slice() -> StrWriter<[u8; 64]> { /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// writer.write_str("Hello, World!"); /// buffer /// } /// /// const SLICE: &[u8] = { /// const PROM: &'static StrWriter = &slice(); /// PROM.as_bytes_alt() /// }; /// /// /// assert_eq!(SLICE, "Hello, World!".as_bytes()); /// /// ``` #[inline(always)] pub const fn as_bytes_alt(&self) -> &[u8] { crate::utils::slice_up_to_len_alt(&self.buffer, self.len) } /// Gets the written part of this `StrWriter` as a `&str` /// /// ### Runtime /// /// If the "rust_1_64" feature is disabled, /// this takes time proportional to `self.capacity() - self.len()`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::StrWriter; /// use const_format::{unwrap, writec}; /// /// /// const CAP: usize = 128; /// /// const __STR: &StrWriter = &{ /// let mut writer = StrWriter::new([0; CAP]); /// /// // Writing the array with debug formatting, and the integers with hexadecimal formatting. /// unwrap!(writec!(writer, "{:X}", [3u32, 5, 8, 13, 21, 34])); /// /// writer /// }; /// /// const STR: &str = __STR.as_str_alt(); /// /// fn main() { /// assert_eq!(STR, "[3, 5, 8, D, 15, 22]"); /// } /// ``` #[inline(always)] pub const fn as_str_alt(&self) -> &str { // All the methods that modify the buffer must ensure utf8 validity, // only methods from this module need to ensure this. unsafe { core::str::from_utf8_unchecked(self.as_bytes_alt()) } } conditionally_const! { feature = "rust_1_64"; /// Gets the written part of this `StrWriter` as a `&str` /// /// ### Constness /// /// This can be called in const contexts by enabling the "rust_1_64" feature. /// /// ### Alternative /// /// You can also use the [`as_str_alt`](Self::as_str_alt) method, /// which is always available, /// but takes linear time to run when the "rust_1_64" feature /// is disabled. /// #[inline(always)] pub fn as_str(&self) -> &str { // All the methods that modify the buffer must ensure utf8 validity, // only methods from this module need to ensure this. unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } } /// Gets the written part of this `StrWriter` as a `&[u8]` /// /// The slice is guaranteed to be valid utf8, so this is mostly for convenience. /// /// ### Constness /// /// This can be called in const contexts by enabling the "rust_1_64" feature. /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str("Hello, World!"); /// /// assert_eq!(buffer.as_bytes(), "Hello, World!".as_bytes()); /// /// ``` #[inline(always)] pub fn as_bytes(&self) -> &[u8] { crate::utils::slice_up_to_len(&self.buffer, self.len) } } /// Borrows this `StrWriter<[u8]>` into a `StrWriterMut`, /// most useful for calling the `write_*` methods. /// /// ```rust /// use const_format::StrWriter; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str_range("trust", 1..usize::MAX); /// /// assert_eq!(buffer.as_str(), "rust"); /// /// ``` #[inline(always)] pub const fn as_mut(&mut self) -> StrWriterMut<'_> { StrWriterMut { len: &mut self.len, buffer: &mut self.buffer, _encoding: PhantomData, } } /// Constructs a [`Formatter`] that writes into this `StrWriter`, /// which can be passed to debug and display formatting methods. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter, call_debug_fmt}; /// /// use std::ops::Range; /// /// const fn range_debug_fmt( /// slice: &[Range], /// f: &mut Formatter<'_> /// ) -> Result<(), Error> { /// // We need this macro to debug format arrays of non-primitive types /// // Also, it implicitly returns a `const_format::Error` on error. /// call_debug_fmt!(array, slice, f); /// Ok(()) /// } /// /// fn main() -> Result<(), Error> { /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 64]); /// /// range_debug_fmt( /// &[0..14, 14..31, 31..48], /// &mut buffer.make_formatter(FormattingFlags::new().set_binary()) /// )?; /// /// assert_eq!(buffer.as_str(), "[0..1110, 1110..11111, 11111..110000]"); /// /// Ok(()) /// } /// ``` /// /// [`Formatter`]: ./struct.Formatter.html #[inline(always)] pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { Formatter::from_sw_mut( StrWriterMut:: { len: &mut self.len, buffer: &mut self.buffer, _encoding: PhantomData, }, flags, ) } } impl StrWriter<[u8; N]> { /// Casts a `&StrWriter<[u8; N]>` to a `&StrWriter<[u8]>`, /// for calling methods defined on `StrWriter<[u8]>` (most of them). /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let mut buffer = StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str("Hello,"); /// buffer.as_mut().write_str(" world!"); /// /// assert_eq!(buffer.r().as_str(), "Hello, world!"); /// /// ``` /// #[inline(always)] pub const fn r(&self) -> &StrWriter<[u8]> { self } /// Casts a `&StrWriter<[u8; N]>` to a `&StrWriter<[u8]>`, /// for calling methods defined on `StrWriter<[u8]>` (most of them). /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let mut buffer = StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str("Hello,"); /// buffer.as_mut().write_str(" world!"); /// /// assert_eq!(buffer.unsize().as_str(), "Hello, world!"); /// /// ``` /// #[inline(always)] pub const fn unsize(&self) -> &StrWriter<[u8]> { self } /// Borrows this `StrWriter<[u8; N]>` into a `StrWriterMut`. /// /// # Example /// /// ```rust /// use const_format::StrWriter; /// /// let mut buffer = StrWriter::new([0; 64]); /// /// buffer.as_mut().write_str_range("trust", 1..usize::MAX); /// /// assert_eq!(buffer.r().as_str(), "rust"); /// /// ``` #[inline(always)] pub const fn as_mut(&mut self) -> StrWriterMut<'_> { StrWriterMut { len: &mut self.len, buffer: &mut self.buffer, _encoding: PhantomData, } } } impl StrWriter { /// For borrowing this mutably in macros, without getting nested mutable references. #[inline(always)] pub const fn borrow_mutably(&mut self) -> &mut Self { self } } const_format-0.2.32/src/fmt/str_writer_mut.rs000064400000000000000000001247611046102023000174350ustar 00000000000000use crate::{ formatting::{ hex_as_ascii, ForEscaping, FormattingFlags, HexFormatting, NumberFormatting, FOR_ESCAPING, }, utils::{min_usize, saturate_range, Constructor}, wrapper_types::{AsciiStr, PWrapper}, }; use super::{Error, Formatter, StrWriter}; use core::{marker::PhantomData, ops::Range}; /// For writing a formatted string into a `[u8]`. /// /// # Construction /// /// This type can be constructed in these ways: /// /// - From a `&mut StrWriter`, with the [`StrWriter::as_mut`] method. /// /// - From a `&mut StrWriter<_>`, with the [`StrWriterMut::new`] constructor. /// /// - From a pair of `usize` and `[u8]` mutable references, /// with the [`from_custom_cleared`] constructor, /// or the [`from_custom`] constructor. /// /// # Relation to `Formatter` /// /// This is the type that [`Formatter`] uses to write formatted text to a slice, /// sharing all the `write_*` methods, /// the difference is that this doesn't store `FormattingFlags`, /// so you must pass them to the `write_*_debug` methods. /// /// # Errors /// /// Every single `write_*` method returns an [`Error::NotEnoughSpace`] if /// there is not enough space to write the argument, leaving the string itself unmodified. /// /// # Encoding type parameter /// /// The `E` type parameter represents the encoding of the buffer that this /// StrWriterMut writes into, /// currently only [`Utf8Encoding`] and [`NoEncoding`] are supported. /// /// # Example /// /// This example demonstrates how you can write a formatted string to a `&mut [u8]`, /// using a `StrWriterMut`. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, StrWriterMut, try_, writec}; /// /// const fn format_number(number: u32,slice: &mut [u8]) -> Result { /// let mut len = 0; /// let mut writer = StrWriterMut::from_custom_cleared(slice, &mut len); /// /// try_!(writec!(writer, "{0} in binary is {0:#b}", number)); /// /// Ok(len) /// } /// /// let mut slice = [0; 32]; /// /// let len = format_number(100, &mut slice)?; /// /// assert_eq!(&slice[..len], "100 in binary is 0b1100100".as_bytes()); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// [`from_custom_cleared`]: #method.from_custom_cleared /// [`from_custom`]: #method.from_custom /// /// [`Utf8Encoding`]: crate::fmt::Utf8Encoding /// [`NoEncoding`]: crate::fmt::NoEncoding /// [`Formatter`]: crate::fmt::Formatter /// [`Error::NotEnoughSpace`]: crate::fmt::Error::NotEnoughSpace /// pub struct StrWriterMut<'w, E = Utf8Encoding> { pub(super) len: &'w mut usize, pub(super) buffer: &'w mut [u8], pub(super) _encoding: PhantomData>, } macro_rules! borrow_fields { ($self:ident, $len:ident, $buffer:ident) => { let $len = &mut *$self.len; let $buffer = &mut *$self.buffer; }; } /// Marker type indicating that the [`StrWriterMut`] is valid utf8, /// enabling the `as_str` method. /// /// [`StrWriterMut`]: ./struct.StrWriterMut.html pub enum Utf8Encoding {} /// Marker type indicating that the [`StrWriterMut`] is arbitrary bytes, /// disabling the `as_str` method. /// /// [`StrWriterMut`]: ./struct.StrWriterMut.html pub enum NoEncoding {} impl<'w> StrWriterMut<'w, Utf8Encoding> { /// Constructs a `StrWriterMut` from a mutable reference to a `StrWriter` /// /// # Example /// /// ```rust /// use const_format::{StrWriter, StrWriterMut}; /// /// let buffer: &mut StrWriter = &mut StrWriter::new([0; 128]); /// { /// let mut writer = StrWriterMut::new(buffer); /// /// let _ = writer.write_str("Number: "); /// let _ = writer.write_u8_display(1); /// } /// assert_eq!(buffer.as_str(), "Number: 1"); /// /// ``` pub const fn new(writer: &'w mut StrWriter) -> Self { Self { len: &mut writer.len, buffer: &mut writer.buffer, _encoding: PhantomData, } } } impl<'w> StrWriterMut<'w, NoEncoding> { /// Construct a `StrWriterMut` from length and byte slice mutable references. /// /// If `length > buffer.len()` is passed, it's simply assigned the length of the buffer. /// /// # Example /// /// ```rust /// use const_format::StrWriterMut; /// /// let mut len = 6; /// let mut buffer = *b"Hello, "; /// /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); /// /// writer.write_str(" world!")?; /// /// assert_eq!(writer.as_bytes(), b"Hello, world!"); /// assert_eq!(buffer, "Hello, world!".as_bytes()); /// assert_eq!(len, "Hello, world!".len()); /// /// # Ok::<(), const_format::Error>(()) /// ``` pub const fn from_custom(buffer: &'w mut [u8], length: &'w mut usize) -> Self { *length = min_usize(*length, buffer.len()); Self { len: length, buffer, _encoding: PhantomData, } } } impl<'w> StrWriterMut<'w, Utf8Encoding> { /// Construct a `StrWriterMut` from length and byte slice mutable references, /// truncating the length to `0`. /// /// Using this instead of [`from_custom`](StrWriterMut::from_custom) allows /// safely casting this to a `&str` with [`as_str_alt`]/[`as_str`] /// /// /// # Example /// /// ```rust /// use const_format::StrWriterMut; /// /// let mut len = 0; /// let mut buffer = [0; 13]; /// /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// writer.write_str("Hello, world!")?; /// /// assert_eq!(writer.as_str(), "Hello, world!"); /// assert_eq!(buffer, "Hello, world!".as_bytes()); /// assert_eq!(len, "Hello, world!".len()); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// [`as_str_alt`]: Self::as_str_alt /// [`as_str`]: Self::as_str /// pub const fn from_custom_cleared(buffer: &'w mut [u8], length: &'w mut usize) -> Self { *length = 0; Self { len: length, buffer, _encoding: PhantomData, } } } impl<'w, E> StrWriterMut<'w, E> { /// Accesses the underlying buffer immutably. /// /// # Example /// /// ```rust /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 7]); /// let mut writer = StrWriterMut::new(&mut buffer); /// assert_eq!(writer.buffer(), &[0; 7]); /// /// writer.write_str("foo")?; /// assert_eq!(writer.buffer(), b"foo\0\0\0\0"); /// /// writer.write_str("bar")?; /// assert_eq!(writer.buffer(), b"foobar\0"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn buffer(&self) -> &[u8] { self.buffer } /// The byte length of the string this is writing. /// /// # Example /// /// ```rust /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// assert_eq!(writer.len(), 0); /// /// writer.write_str("foo")?; /// assert_eq!(writer.len(), 3); /// /// writer.write_str("bar")?; /// assert_eq!(writer.len(), 6); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn len(&self) -> usize { *self.len } /// Whether the string this is writing is empty. /// /// # Example /// /// ```rust /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// assert!( writer.is_empty() ); /// /// writer.write_str("foo")?; /// assert!( !writer.is_empty() ); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn is_empty(&self) -> bool { *self.len == 0 } /// The maximum byte length of the formatted text for this `StrWriterMut`. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// assert_eq!(writer.capacity(), 64); /// /// writer.write_ascii_repeated(b'A', 64)?; /// assert_eq!(writer.capacity(), 64); /// /// assert_eq!(writer.write_str("-").unwrap_err(), Error::NotEnoughSpace); /// assert_eq!(writer.capacity(), 64); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline(always)] pub const fn capacity(&self) -> usize { self.buffer.len() } /// Checks how many more bytes can be written. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// assert_eq!(writer.remaining_capacity(), 64); /// /// writer.write_str("foo")?; /// assert_eq!(writer.remaining_capacity(), 61); /// /// writer.write_ascii_repeated(b'a', 61)?; /// assert_eq!(writer.remaining_capacity(), 0); /// /// assert_eq!(writer.write_str(" ").unwrap_err(), Error::NotEnoughSpace); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn remaining_capacity(&self) -> usize { self.buffer.len() - *self.len } } impl<'w> StrWriterMut<'w, Utf8Encoding> { /// Truncates this `StrWriterMut` to `length`. /// /// If `length` is greater than the current length, this does nothing. /// /// # Errors /// /// Returns an `Error::NotOnCharBoundary` if `length` is not on a char boundary. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// writer.write_str("foo bâr baz"); /// assert_eq!(writer.as_str(), "foo bâr baz"); /// /// assert_eq!(writer.truncate(6).unwrap_err(), Error::NotOnCharBoundary); /// /// writer.truncate(3)?; /// assert_eq!(writer.as_str(), "foo"); /// /// writer.write_str("ooooooo"); /// assert_eq!(writer.as_str(), "fooooooooo"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn truncate(&mut self, length: usize) -> Result<(), Error> { if length <= *self.len { if !is_valid_str_index(self.buffer, length) { return Err(Error::NotOnCharBoundary); } *self.len = length; } Ok(()) } } impl<'w> StrWriterMut<'w, NoEncoding> { /// Truncates this `StrWriterMut<'w, NoEncoding>` to `length`. /// /// If `length` is greater than the current length, this does nothing. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter, StrWriterMut}; /// /// let mut buffer = [0; 32]; /// let mut len = 0; /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); /// /// writer.write_str("foo bar baz"); /// assert_eq!(writer.as_bytes(), b"foo bar baz"); /// /// // Truncating to anything larger than the length is a no-op. /// writer.truncate(usize::MAX / 2); /// assert_eq!(writer.as_bytes(), b"foo bar baz"); /// /// writer.truncate(3); /// assert_eq!(writer.as_bytes(), b"foo"); /// /// writer.write_str("ooooooo"); /// assert_eq!(writer.as_bytes(), b"fooooooooo"); /// /// ``` #[inline] pub const fn truncate(&mut self, length: usize) { if length < *self.len { *self.len = length; } } } impl<'w, E> StrWriterMut<'w, E> { /// Truncates this `StrWriterMut` to length 0. /// /// # Example /// /// ```rust /// use const_format::{Error, StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// writer.write_str("foo")?; /// assert_eq!(writer.as_str(), "foo"); /// /// writer.clear(); /// assert_eq!(writer.as_str(), ""); /// assert!(writer.is_empty()); /// /// writer.write_str("bar"); /// assert_eq!(writer.as_str(), "bar"); /// /// # Ok::<(), const_format::Error>(()) /// ``` #[inline] pub const fn clear(&mut self) { *self.len = 0; } /// Gets the written part of this `StrWriterMut` as a `&[u8]` /// /// The slice is guaranteed to be valid utf8, so this is mostly for convenience. /// /// ### Runtime /// /// If the "rust_1_64" feature is disabled, /// this takes time proportional to `self.capacity() - self.len()`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// writer.write_str("Hello, World!"); /// /// assert_eq!(writer.as_bytes_alt(), "Hello, World!".as_bytes()); /// /// ``` #[inline(always)] pub const fn as_bytes_alt(&self) -> &[u8] { crate::utils::slice_up_to_len_alt(self.buffer, *self.len) } } impl<'w> StrWriterMut<'w, Utf8Encoding> { /// Gets the written part of this StrWriterMut as a `&str` /// /// ### Runtime /// /// If the "rust_1_64" feature is disabled, /// this takes time proportional to `self.capacity() - self.len()`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, StrWriterMut}; /// use const_format::{unwrap, writec}; /// /// /// const CAP: usize = 128; /// /// const __STR: &StrWriter = &{ /// let mut buffer = StrWriter::new([0; CAP]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// // Writing the array with debug formatting, and the integers with hexadecimal formatting. /// unwrap!(writec!(writer, "{:X}", [3u32, 5, 8, 13, 21, 34])); /// /// buffer /// }; /// /// const STR: &str = __STR.as_str_alt(); /// /// fn main() { /// assert_eq!(STR, "[3, 5, 8, D, 15, 22]"); /// } /// ``` #[inline(always)] pub const fn as_str_alt(&self) -> &str { // All the methods that modify the buffer must ensure utf8 validity, // only methods from this module need to ensure this. unsafe { core::str::from_utf8_unchecked(self.as_bytes_alt()) } } conditionally_const! { feature = "rust_1_64"; /// Gets the written part of this StrWriterMut as a `&str` /// /// ### Constness /// /// This can be called in const contexts by enabling the "rust_1_64" feature. /// /// ### Alternative /// /// You can also use the [`as_str_alt`](Self::as_str_alt) method, /// which is always available, /// but takes linear time to run when the "rust_1_64" feature /// is disabled. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// writer.write_str("Hello, how are you?"); /// /// assert_eq!(writer.as_str(), "Hello, how are you?"); /// /// ``` #[inline(always)] pub fn as_str(&self) -> &str { // All the methods that modify the buffer must ensure utf8 validity, // only methods from this module need to ensure this. unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } } } } impl<'w, E> StrWriterMut<'w, E> { conditionally_const! { feature = "rust_1_64"; /// Gets the written part of this `StrWriterMut` as a `&[u8]` /// /// The slice is guaranteed to be valid utf8, so this is mostly for convenience. /// /// ### Constness /// /// This can be called in const contexts by enabling the "rust_1_64" feature. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, StrWriterMut}; /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// writer.write_str("Hello, World!"); /// /// assert_eq!(writer.as_bytes_alt(), "Hello, World!".as_bytes()); /// /// ``` #[inline(always)] pub fn as_bytes(&self) -> &[u8] { crate::utils::slice_up_to_len(self.buffer, *self.len) } } } impl<'w, E> StrWriterMut<'w, E> { /// Constructs a [`Formatter`] that writes into this `StrWriterMut`, /// which can be passed to debug and display formatting methods. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, FormattingFlags, StrWriter, StrWriterMut}; /// use const_format::call_debug_fmt; /// /// use std::ops::Range; /// /// const fn range_debug_fmt( /// slice: &[Range], /// f: &mut Formatter<'_> /// ) -> Result<(), Error> { /// // We need this macro to debug format arrays of non-primitive types /// // Also, it implicitly returns a `const_format::Error` on error. /// call_debug_fmt!(array, slice, f); /// Ok(()) /// } /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// range_debug_fmt( /// &[0..14, 14..31, 31..48], /// &mut writer.make_formatter(FormattingFlags::new().set_binary()) /// )?; /// /// assert_eq!(writer.as_str(), "[0..1110, 1110..11111, 11111..110000]"); /// /// # Ok::<(), Error>(()) /// /// ``` /// /// [`Formatter`]: ./struct.Formatter.html #[inline(always)] pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { Formatter::from_sw_mut( StrWriterMut:: { len: self.len, buffer: self.buffer, _encoding: PhantomData, }, flags, ) } /// For borrowing this mutably in macros, without getting nested mutable references. #[inline(always)] pub const fn borrow_mutably(&mut self) -> &mut StrWriterMut<'w, E> { self } /// For passing a reborrow of this `StrWriterMut` into functions, /// without this you'd need to pass a mutable reference instead. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, FormattingFlags, StrWriter, StrWriterMut, call_debug_fmt}; /// /// use std::ops::Range; /// /// const fn range_debug_fmt( /// slice: &[[u32; 2]], /// mut writer: StrWriterMut<'_> /// ) -> Result<(), Error> { /// let mut formatter = writer.make_formatter(FormattingFlags::new().set_binary()); /// /// // We need this macro to debug format arrays of non-primitive types /// // Also, it implicitly returns a `const_format::Error` on error. /// call_debug_fmt!(array, slice, formatter); /// Ok(()) /// } /// /// let mut buffer = StrWriter::new([0; 64]); /// let mut writer = StrWriterMut::new(&mut buffer); /// /// range_debug_fmt(&[[3, 5], [8, 13]], writer.reborrow())?; /// /// assert_eq!(writer.as_str(), "[[11, 101], [1000, 1101]]"); /// /// # Ok::<(), Error>(()) /// /// ``` #[inline(always)] pub const fn reborrow(&mut self) -> StrWriterMut<'_, E> { StrWriterMut { len: self.len, buffer: self.buffer, _encoding: PhantomData, } } // Safety: You must not write invalid utf8 bytes with the returned StrWriterMut. pub(crate) const unsafe fn into_byte_encoding(self) -> StrWriterMut<'w, NoEncoding> { StrWriterMut { len: self.len, buffer: self.buffer, _encoding: PhantomData, } } } ///////////////////////////////////////////////////////////////////////////////// macro_rules! write_integer_fn { ( display_attrs $display_attrs:tt debug_attrs $debug_attrs:tt $(($display_fn:ident, $debug_fn:ident, $sign:ident, $ty:ident, $Unsigned:ident))* )=>{ impl<'w,E> StrWriterMut<'w,E>{ $( write_integer_fn!{ @methods display_attrs $display_attrs debug_attrs $debug_attrs $display_fn, $debug_fn, $sign, ($ty, $Unsigned), stringify!($ty) } )* } $( write_integer_fn!{ @pwrapper $display_fn, $debug_fn, $sign, ($ty, $Unsigned), stringify!($ty) } )* }; (@pwrapper $display_fn:ident, $debug_fn:ident, $sign:ident, ($ty:ident, $Unsigned:ident), $ty_name:expr )=>{ impl PWrapper<$ty> { /// Writes a #[doc = $ty_name] /// with Display formatting. pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.$display_fn(self.0) } /// Writes a #[doc = $ty_name] /// with Debug formatting. pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.$debug_fn(self.0) } } }; (@methods display_attrs( $(#[$display_attrs:meta])* ) debug_attrs( $(#[$debug_attrs:meta])* ) $display_fn:ident, $debug_fn:ident, $sign:ident, ($ty:ident, $Unsigned:ident), $ty_name:expr )=>{ $(#[$display_attrs])* pub const fn $display_fn(&mut self, number: $ty) -> Result<(), Error> { borrow_fields!(self, this_len, this_buffer); let n = PWrapper(number); let len = n.compute_display_len(FormattingFlags::DEFAULT); let mut cursor = *this_len + len; if cursor > this_buffer.len() { return Err(Error::NotEnoughSpace); } write_integer_fn!(@unsigned_abs $sign, n); loop { cursor-=1; let digit = (n % 10) as u8; this_buffer[cursor] = b'0' + digit; n/=10; if n == 0 { break } } write_integer_fn!(@write_sign $sign, this_len, this_buffer, number); *this_len+=len; Ok(()) } $(#[$debug_attrs])* pub const fn $debug_fn( &mut self, number: $ty, flags: FormattingFlags, ) -> Result<(), Error> { const fn hex( this: &mut StrWriterMut<'_, E>, n: $ty, f: FormattingFlags, ) -> Result<(), Error> { borrow_fields!(this, this_len, this_buffer); let is_alternate = f.is_alternate(); let len = PWrapper(n).hexadecimal_len(f); let mut cursor = *this_len + len; if cursor > this_buffer.len() { return Err(Error::NotEnoughSpace); } if is_alternate { this_buffer[*this_len] = b'0'; this_buffer[*this_len + 1] = b'x'; } write_integer_fn!(@as_unsigned $sign, n, $Unsigned); loop { cursor-=1; let digit = (n & 0b1111) as u8; this_buffer[cursor] = hex_as_ascii(digit, f.hex_fmt()); n >>= 4; if n == 0 { break } } *this_len+=len; Ok(()) } const fn binary( this: &mut StrWriterMut<'_, E>, n: $ty, f: FormattingFlags, ) -> Result<(), Error> { borrow_fields!(this, this_len, this_buffer); let is_alternate = f.is_alternate(); let len = PWrapper(n).binary_len(f); let mut cursor = *this_len + len; if cursor > this_buffer.len() { return Err(Error::NotEnoughSpace); } if is_alternate { this_buffer[*this_len] = b'0'; this_buffer[*this_len + 1] = b'b'; } write_integer_fn!(@as_unsigned $sign, n, $Unsigned); loop { cursor-=1; let digit = (n & 1) as u8; this_buffer[cursor] = hex_as_ascii(digit, f.hex_fmt()); n >>= 1; if n == 0 { break } } *this_len+=len; Ok(()) } match flags.num_fmt() { NumberFormatting::Decimal=>self.$display_fn(number), NumberFormatting::Hexadecimal=>hex(self, number, flags), NumberFormatting::Binary=>binary(self, number, flags), } } }; (@unsigned_abs signed, $n:ident) => ( let mut $n = $n.unsigned_abs(); ); (@unsigned_abs unsigned, $n:ident) => ( let mut $n = $n.0; ); (@as_unsigned signed, $n:ident, $Unsigned:ident) => ( let mut $n = $n as $Unsigned; ); (@as_unsigned unsigned, $n:ident, $Unsigned:ident) => ( let mut $n = $n; ); (@write_sign signed, $self_len:ident, $self_buffer:ident, $n:ident) => ({ if $n < 0 { $self_buffer[*$self_len] = b'-'; } }); (@write_sign unsigned, $self_len:ident, $self_buffer:ident, $n:ident) => ({}); } /// Checks that a range is valid for indexing a string, /// assuming that the range is in-bounds, and start <= end. #[inline] const fn is_valid_str_range(s: &[u8], Range { start, end }: Range) -> bool { let len = s.len(); (end == len || ((s[end] as i8) >= -0x40)) && (start == len || ((s[start] as i8) >= -0x40)) } /// Checks that an index is valid for indexing a string, /// assuming that the index is in-bounds. #[inline] const fn is_valid_str_index(s: &[u8], index: usize) -> bool { let len = s.len(); index == len || ((s[index] as i8) >= -0x40) } impl<'w, E> StrWriterMut<'w, E> { /// Writes a subslice of `s` with Display formatting. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Additional Errors /// /// This method returns `Error::NotOnCharBoundary` if the range is not /// on a character boundary. /// /// Out of bounds range bounds are treated as being at `s.len()`, /// this only returns an error on an in-bounds index that is not on a character boundary. /// /// # Example /// /// ```rust /// use const_format::{FormattingFlags, StrWriterMut}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_str_range("FOO BAR BAZ", 4..7); /// /// assert_eq!(writer.as_str(), "BAR"); /// /// ``` /// pub const fn write_str_range(&mut self, s: &str, range: Range) -> Result<(), Error> { let bytes = s.as_bytes(); let Range { start, end } = saturate_range(bytes, &range); if !is_valid_str_range(bytes, start..end) { return Err(Error::NotOnCharBoundary); } self.write_str_inner(bytes, start, end) } /// Writes `s` with Display formatting. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_str("FOO BAR BAZ"); /// /// assert_eq!(writer.as_str(), "FOO BAR BAZ"); /// /// ``` /// pub const fn write_str(&mut self, s: &str) -> Result<(), Error> { let bytes = s.as_bytes(); self.write_str_inner(bytes, 0, s.len()) } /// Writes `character` with Display formatting /// /// # Example /// /// ```rust /// /// use const_format::StrWriterMut; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_char('3'); /// let _ = writer.write_char('5'); /// let _ = writer.write_char('8'); /// /// assert_eq!(writer.as_str(), "358"); /// /// ``` /// pub const fn write_char(&mut self, character: char) -> Result<(), Error> { let fmt = crate::char_encoding::char_to_display(character); self.write_str_inner(fmt.encoded(), 0, fmt.len()) } /// Writes a subslice of `ascii` with Display formatting. /// /// Out of bounds range bounds are treated as being at `s.len()`. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut, ascii_str}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_ascii_range(ascii_str!("FOO BAR BAZ"), 4..7); /// /// assert_eq!(writer.as_str(), "BAR"); /// /// ``` /// pub const fn write_ascii_range( &mut self, ascii: AsciiStr<'_>, range: Range, ) -> Result<(), Error> { let bytes = ascii.as_bytes(); let Range { start, end } = saturate_range(bytes, &range); self.write_str_inner(bytes, start, end) } /// Writes `ascii` with Display formatting. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut, ascii_str}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_ascii(ascii_str!("FOO BAR BAZ")); /// /// assert_eq!(writer.as_str(), "FOO BAR BAZ"); /// /// ``` /// pub const fn write_ascii(&mut self, ascii: AsciiStr<'_>) -> Result<(), Error> { let bytes = ascii.as_bytes(); self.write_str_inner(bytes, 0, bytes.len()) } /// Writes an ascii `character`, `repeated` times. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_ascii_repeated(b'A', 10); /// /// assert_eq!(writer.as_str(), "AAAAAAAAAA"); /// /// ``` /// pub const fn write_ascii_repeated( &mut self, mut character: u8, repeated: usize, ) -> Result<(), Error> { borrow_fields!(self, self_len, self_buffer); // Truncating non-ascii u8s character &= 0b111_1111; let end = *self_len + repeated; if end > self_buffer.len() { return Err(Error::NotEnoughSpace); } while *self_len < end { self_buffer[*self_len] = character; *self_len += 1; } Ok(()) } #[inline(always)] const fn write_str_inner( &mut self, bytes: &[u8], mut start: usize, end: usize, ) -> Result<(), Error> { borrow_fields!(self, self_len, self_buffer); let len = end - start; if *self_len + len > self_buffer.len() { return Err(Error::NotEnoughSpace); } while start < end { self_buffer[*self_len] = bytes[start]; *self_len += 1; start += 1; } Ok(()) } } /// Debug-formatted string writing impl<'w, E> StrWriterMut<'w, E> { /// Writes a subslice of `s` with Debug-like formatting. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Additional Errors /// /// This method returns `Error::NotOnCharBoundary` if the range is not /// on a character boundary. /// /// Out of bounds range bounds are treated as being at `s.len()`, /// this only returns an error on an in-bounds index that is not on a character boundary. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_str_range_debug("FOO\nBAR\tBAZ", 3..8); /// /// assert_eq!(writer.as_str(), r#""\nBAR\t""#); /// /// ``` /// pub const fn write_str_range_debug( &mut self, s: &str, range: Range, ) -> Result<(), Error> { let bytes = s.as_bytes(); let Range { start, end } = saturate_range(bytes, &range); if !is_valid_str_range(bytes, start..end) { return Err(Error::NotOnCharBoundary); } self.write_str_debug_inner(bytes, start, end) } /// Writes `s` with Debug-like formatting. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_str_debug("FOO\nBAR\tBAZ"); /// /// assert_eq!(writer.as_str(), r#""FOO\nBAR\tBAZ""#); /// /// ``` /// pub const fn write_str_debug(&mut self, str: &str) -> Result<(), Error> { let bytes = str.as_bytes(); self.write_str_debug_inner(bytes, 0, str.len()) } /// Writes `character` with Debug formatting. /// /// # Example /// /// ```rust /// /// use const_format::StrWriterMut; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_str(" "); /// let _ = writer.write_char_debug('\\'); /// let _ = writer.write_str(" "); /// let _ = writer.write_char_debug('A'); /// let _ = writer.write_str(" "); /// let _ = writer.write_char_debug('0'); /// let _ = writer.write_str(" "); /// let _ = writer.write_char_debug('\''); /// let _ = writer.write_str(" "); /// /// assert_eq!(writer.as_str(), r#" '\\' 'A' '0' '\'' "#); /// /// ``` /// pub const fn write_char_debug(&mut self, character: char) -> Result<(), Error> { let fmt = crate::char_encoding::char_to_debug(character); self.write_str_inner(fmt.encoded(), 0, fmt.len()) } /// Writes a subslice of `ascii` with Debug-like formatting. /// /// Out of bounds range bounds are treated as being at `s.len()`. /// /// This is a workaround for being unable to do `&foo[start..end]` at compile time. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut, ascii_str}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_ascii_range_debug(ascii_str!("FOO\nBAR\tBAZ"), 3..8); /// /// assert_eq!(writer.as_str(), r#""\nBAR\t""#); /// /// ``` /// pub const fn write_ascii_range_debug( &mut self, ascii: AsciiStr<'_>, range: Range, ) -> Result<(), Error> { let bytes = ascii.as_bytes(); let Range { start, end } = saturate_range(bytes, &range); self.write_str_debug_inner(bytes, start, end) } /// Writes `ascii` with Debug-like formatting. /// /// # Example /// /// ```rust /// /// use const_format::{FormattingFlags, StrWriterMut, ascii_str}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_ascii_debug(ascii_str!("FOO\nBAR\tBAZ")); /// /// assert_eq!(writer.as_str(), r#""FOO\nBAR\tBAZ""#); /// /// ``` /// pub const fn write_ascii_debug(&mut self, ascii: AsciiStr<'_>) -> Result<(), Error> { let bytes = ascii.as_bytes(); self.write_str_debug_inner(bytes, 0, bytes.len()) } #[inline(always)] const fn write_str_debug_inner( &mut self, bytes: &[u8], mut start: usize, end: usize, ) -> Result<(), Error> { borrow_fields!(self, self_len, self_buffer); let len = end - start; // + 2 for the quote characters around the string. if *self_len + len + 2 > self_buffer.len() { return Err(Error::NotEnoughSpace); } // The amount of bytes available for escapes, // not counting the `writte_c`. let mut remaining_for_escapes = (self_buffer.len() - 2 - len - *self_len) as isize; let mut written = *self_len; self_buffer[written] = b'"'; written += 1; while start != end { let c = bytes[start]; let mut written_c = c; let mut shifted = 0; if c < 128 && ({ shifted = 1 << c; (FOR_ESCAPING.is_escaped & shifted) != 0 }) { self_buffer[written] = b'\\'; written += 1; if (FOR_ESCAPING.is_backslash_escaped & shifted) != 0 { remaining_for_escapes -= 1; if remaining_for_escapes < 0 { return Err(Error::NotEnoughSpace); } written_c = ForEscaping::get_backslash_escape(c); } else { remaining_for_escapes -= 3; if remaining_for_escapes < 0 { return Err(Error::NotEnoughSpace); } self_buffer[written] = b'x'; written += 1; self_buffer[written] = hex_as_ascii(c >> 4, HexFormatting::Upper); written += 1; written_c = hex_as_ascii(c & 0xF, HexFormatting::Upper); }; } self_buffer[written] = written_c; written += 1; start += 1; } self_buffer[written] = b'"'; written += 1; *self_len = written; Ok(()) } } write_integer_fn! { display_attrs( /// Write `number` with display formatting. /// /// # Example /// /// ```rust /// /// use const_format::{Formatter, FormattingFlags, StrWriterMut, ascii_str}; /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// let _ = writer.write_u8_display(137); /// /// assert_eq!(writer.as_str(), "137"); /// /// ``` /// ) debug_attrs( /// Writes `number` with debug formatting. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{FormattingFlags, StrWriterMut}; /// /// const fn debug_fmt<'a>( /// writer: &'a mut StrWriterMut<'_>, /// flag: FormattingFlags /// ) -> &'a str { /// writer.clear(); /// let _ = writer.write_u8_debug(63, flag); /// writer.as_str_alt() /// } /// /// let reg_flag = FormattingFlags::NEW.set_alternate(false); /// let alt_flag = FormattingFlags::NEW.set_alternate(true); /// /// let mut len = 0; /// let mut buffer = [0; 64]; /// let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); /// /// assert_eq!(debug_fmt(&mut writer, reg_flag), "63" ); /// assert_eq!(debug_fmt(&mut writer, reg_flag.set_hexadecimal()), "3F" ); /// assert_eq!(debug_fmt(&mut writer, reg_flag.set_lower_hexadecimal()), "3f"); /// assert_eq!(debug_fmt(&mut writer, reg_flag.set_binary()), "111111" ); /// assert_eq!(debug_fmt(&mut writer, alt_flag), "63" ); /// assert_eq!(debug_fmt(&mut writer, alt_flag.set_hexadecimal()), "0x3F" ); /// assert_eq!(debug_fmt(&mut writer, alt_flag.set_lower_hexadecimal()), "0x3f"); /// assert_eq!(debug_fmt(&mut writer, alt_flag.set_binary()), "0b111111"); /// /// ``` /// ) (write_u8_display, write_u8_debug, unsigned, u8, u8) } write_integer_fn! { display_attrs( /// Writes `number` with display formatting /// /// For an example, /// you can look at the one for the [`write_u8_display`] method. /// /// [`write_u8_display`]: #method.write_u8_display ) debug_attrs( /// Writes `number` with debug formatting. /// /// For an example, /// you can look at the one for the [`write_u8_debug`] method. /// /// [`write_u8_debug`]: #method.write_u8_debug ) (write_u16_display, write_u16_debug, unsigned, u16, u16) (write_u32_display, write_u32_debug, unsigned, u32, u32) (write_u64_display, write_u64_debug, unsigned, u64, u64) (write_u128_display, write_u128_debug, unsigned, u128, u128) (write_usize_display, write_usize_debug, unsigned, usize, usize) (write_i8_display, write_i8_debug, signed, i8, u8) (write_i16_display, write_i16_debug, signed, i16, u16) (write_i32_display, write_i32_debug, signed, i32, u32) (write_i64_display, write_i64_debug, signed, i64, u64) (write_i128_display, write_i128_debug, signed, i128, u128) (write_isize_display, write_isize_debug, signed, isize, usize) } const_format-0.2.32/src/fmt.rs000064400000000000000000000233331046102023000143350ustar 00000000000000//! [`std::fmt`](https://doc.rust-lang.org/std/fmt/)-like //! api that can be used at compile-time. //! //! # Features //! //! This module requires the "fmt" feature to be exported, and the nightly compiler, //! because at the time of writing these docs (2023-10-XX) mutable references in const fn //! require the unstable //! [`const_mut_refs`](https://github.com/rust-lang/rust/issues/57349) feature. //! //! # Implementing the formatting methods //! //! Users of this library can implement debug and display formatting by //! defining `const_debug_fmt` and `const_display_fmt` inherent methods //! with the //! ```ignore //! // use const_format::{Formatter, Error}; //! const fn const_debug_fmt(&self, &mut Formatter<'_>) -> Result<(), Error> //! const fn const_display_fmt(&self, &mut Formatter<'_>) -> Result<(), Error> //! ``` //! signatures, //! and implementing the [`FormatMarker`] trait. //! //! # Limitations //! //! ### Generic impls //! //! Because the formatting of custom types is implemented with duck typing, //! it's not possible to format generic types, instead you must do either of these: //! //! - Provide all the implementations ahead of time, what [`impl_fmt`] is for. //! //! - Provide a macro that formats the type. //! The `call_debug_fmt` macro is a version of this that formats generic std types. //! //! //! # Formatting Syntax //! //! The formatting macros all share the formatting syntax, //! modeled after the syntax of the formatting macros of the standard library. //! //! ### Arguments //! //! Arguments in the format string can be named and positional in these ways: //! //! - Implicitly positional(eg: `formatc!("{}", 20u8)`):
//! Starts at the 0th positional argument and increments with every use. //! //! - Explicit positional(eg: `formatc!("{0}", 10u8)`). //! //! - Named, passed to the macro as named arguments (eg: `formatc!("{foo}", foo = 10u8)`). //! //! - Named, from constant (eg: `formatc!("{FOO}")`): //! Uses the `FOO` constant from the enclosing scope. //! //! - Named, from locals (eg: `writec!(writable, "{foo}")`): //! Uses the `foo` local variable from the enclosing scope, //! only usable with the [`writec`] macro. //! //! ### Formatters //! //! The format arguments can be formatted in these ways: //! //! - Debug formatting (eg: `formatc!("{:?}", 0u8)` ):
//! Similar to how Debug formatting in the standard library works, //! except that it does not escape unicode characters. //! //! - Display formatting (eg: `formatc!("{}", 0u8)`, `formatc!("{:}", 0u8)` ) //! //! - Lowercase hexadecimal formatting (eg: `formatc!("{:x}", 0u8)`):
//! Writes numbers in lowercase hexadecimal. //! This can be combined with debug formatting with the `"{:x?}"` formatter. //! //! - Uppercase hexadecimal formatting (eg: `formatc!("{:X}", 0u8)`):
//! Writes numbers in uppercase hexadecimal. //! This can be combined with debug formatting with the `"{:X?}"` formatter. //! //! - Binary formatting (eg: `formatc!("{:b}", 0u8)`):
//! This can be combined with debug formatting with the `"{:b?}"` formatter. //! //! ### Alternate flag //! //! The alternate flag allows types to format themselves in an alternate way, //! written as "#" in a format string argument. eg:`"{:#}", "{:#?}"`. //! //! This is the built-in behavior for the alternate flag: //! //! - The Debug formater (eg: `formatc!("{:#?}", FOO)`): //! pretty print structs and enums. //! //! - The hexadecimal formater (eg: `formatc!("{:#x}", FOO)`): //! prefixes numbers with `0x`. //! //! - The binary formater (eg: `formatc!("{:#b}", FOO)`): //! prefixes numbers with `0b`. //! //! ### Additional specifiers //! //! `const_format` macros don't support width, fill, alignment, sign, //! or precision specifiers. //! //! //! ### Custom formatting //! //! Arguments can access a [`Formatter`] for custom formatting with an //! `|identifier|` before the expression. //! //! The expression will be evaluated every time it is used in the formatting string. //! //! The expression can evaluate to either a `()` or a `Result<(), const_format::Error>`. //! //! Note: this doesn't distinguish between debug and display formatting. //! //! [Link to full example of custom formatting](#custom-formatting-example) //! //! # Examples //! //! ### Derive //! //! This example demonstrates how you can derive [`ConstDebug`], and use it with the `fmt` API. //! //! It uses the "derive" feature //! #![cfg_attr(feature = "derive", doc = "```rust")] #![cfg_attr(not(feature = "derive"), doc = "```ignore")] //! #![feature(const_mut_refs)] //! //! use const_format::{Error, Formatter, FormattingFlags, PWrapper, StrWriter}; //! use const_format::{ConstDebug, try_, unwrap, writec}; //! //! use std::ops::Range; //! //! #[derive(ConstDebug)] //! pub struct Foo { //! range: Option>, //! point: Point, //! } //! //! #[derive(ConstDebug)] //! pub struct Point { //! x: u32, //! y: u32, //! } //! //! const CAP: usize = 90; //! const fn build_string() -> StrWriter<[u8; CAP]> { //! let mut writer = StrWriter::new([0; CAP]); //! //! let foo = Foo { //! range: Some(0..10), //! point: Point{ x: 13, y: 21 }, //! }; //! //! unwrap!(writec!(writer, "{:X?}", foo)); //! //! writer //! } //! //! const STRING: &str = { //! const STR: &StrWriter = &build_string(); //! STR.as_str_alt() //! }; //! //! // The formatter //! assert_eq!( //! STRING, //! "Foo { range: Some(0..A), point: Point { x: D, y: 15 } }", //! ); //! //! ``` //! //! //! ### No proc macros //! //! This example demonstrates how you can use the `fmt` api without using any proc macros. //! //! ```rust //! #![feature(const_mut_refs)] //! //! use const_format::{Error, Formatter, FormattingFlags, PWrapper, StrWriter}; //! use const_format::{call_debug_fmt, coerce_to_fmt, impl_fmt, try_}; //! //! use std::cmp::Ordering; //! //! pub struct Foo { //! a: u32, //! b: u32, //! c: T, //! d: [Ordering; 3], //! ignored: U, //! } //! //! // //! impl_fmt!{ //! // The type parameters of the impl must be written with trailing commas //! impl[U,] Foo; //! impl[U,] Foo<&str, U>; //! //! pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { //! let mut f = f.debug_struct("Foo"); //! //! // PWrapper is a wrapper for std types, which defines the formatter methods for them. //! try_!(PWrapper(self.a).const_debug_fmt(f.field("a"))); //! //! try_!(PWrapper(self.b).const_debug_fmt(f.field("b"))); //! //! // The `coerce_to_fmt` macro automatically wraps std types in `PWrapper` //! // and does nothing with non-std types. //! try_!(coerce_to_fmt!(self.c).const_debug_fmt(f.field("c"))); //! //! // This macro allows debug formatting of some generic types which //! // wrap non-std types, including: //! // - arrays - slices - Option - newtype wrappers //! call_debug_fmt!(array, self.d, f.field("d")); //! //! f.finish() //! } //! } //! //! const CAP: usize = 128; //! //! const fn build_string() -> StrWriter<[u8; CAP]> { //! let flags = FormattingFlags::NEW.set_alternate(true); //! let mut writer = StrWriter::new([0; CAP]); //! //! const_format::unwrap!( //! Foo { //! a: 5, //! b: 8, //! c: 13, //! d: [Ordering::Less, Ordering::Equal, Ordering::Greater], //! ignored: (), //! }.const_debug_fmt(&mut Formatter::from_sw(&mut writer, flags)) //! ); //! //! writer //! } //! //! const STRING: &str = { //! const S: &StrWriter = &build_string(); //! S.as_str_alt() //! }; //! //! assert_eq!( //! STRING, //! "\ //! Foo { //! a: 5, //! b: 8, //! c: 13, //! d: [ //! Less, //! Equal, //! Greater, //! ], //! }\ //! ", //! ); //! //! //! ``` //! //! //! ### Custom formatting //! //! This example demonstrates how you can do [custom formatting](#custom-formatting-section), //! by using a `Formatter` directly. //! //! ```rust //! #![feature(const_mut_refs)] //! //! use const_format::{call_debug_fmt, formatc}; //! //! // Positional argument //! assert_eq!(formatc!("{}", |fmt| fmt.write_ascii_repeated(b'a', 4) ), "aaaa"); //! //! // Named argument //! assert_eq!(formatc!("{foo}", foo = |fmt| fmt.write_ascii_repeated(b'0', 10) ), "0000000000"); //! //! // Repeating a positional argument multiple times //! assert_eq!(formatc!("{0}{0}{0}", |fmt| fmt.write_str("hi") ), "hihihi"); //! //! // Using debug formatting is no different to display formatting: //! assert_eq!(formatc!("{0:?}{0:?}{0:?}", |fmt| fmt.write_str("hi") ), "hihihi"); //! //! // But binary/hex formatting, and the alternate flag, do have an effect: //! assert_eq!( //! formatc!( //! "{0}\n{0:x}\n{0:X}\n{0:b}\n{0:#x}\n{0:#X}\n{0:#b}", //! |fmt| call_debug_fmt!(array, [3u8, 13], fmt), //! ), //! "\ //! [3, 13]\n\ //! [3, d]\n\ //! [3, D]\n\ //! [11, 1101]\n\ //! [\n 0x3,\n 0xd,\n]\n\ //! [\n 0x3,\n 0xD,\n]\n\ //! [\n 0b11,\n 0b1101,\n]\ //! ", //! ); //! //! ``` //! //! //! [`writec`]: ../macro.writec.html //! [`Formatter`]: ./struct.Formatter.html //! [`FormatMarker`]: ../marker_traits/trait.FormatMarker.html //! [`ConstDebug`]: ../derive.ConstDebug.html //! //! //! //! //! mod error; mod formatter; mod std_type_impls; mod str_writer; mod str_writer_mut; pub use crate::formatting::{FormattingFlags, NumberFormatting}; pub use self::{ error::{Error, Result, ToResult}, formatter::{ComputeStrLength, DebugList, DebugSet, DebugStruct, DebugTuple, Formatter}, str_writer::StrWriter, str_writer_mut::{NoEncoding, StrWriterMut, Utf8Encoding}, }; const_format-0.2.32/src/for_assert_macros.rs000064400000000000000000000007421046102023000172610ustar 00000000000000#![allow(non_fmt_panics)] use crate::pargument::PArgument; #[track_caller] pub const fn assert_(cond: bool, message: &'static str) { if cond { panic!("{}", message) } } // The `T` type parameter is there just so that the PARGUMENTS associated constant // is evaluated lazily. pub trait ConcatArgsIf { const PARGUMENTS: &'static [PArgument]; } impl ConcatArgsIf for S { const PARGUMENTS: &'static [PArgument] = &[]; } const_format-0.2.32/src/for_examples.rs000064400000000000000000000027411046102023000162330ustar 00000000000000//! Types for the documentation examples. //! //! # Features //! //! This module is only exported with the "fmt" feature, and the nightly compiler, //! because at the time of writing these docs (2023-10-XX) mutable references in const fn //! require the unstable //! [`const_mut_refs`](https://github.com/rust-lang/rust/issues/57349) feature. use crate::{impl_fmt, try_, Error, Formatter, PWrapper}; /// An example struct which implements const debug formatting. #[derive(Debug, Copy, Clone)] pub struct Point3 { /// pub x: u32, /// pub y: u32, /// pub z: u32, } impl_fmt! { impl Point3; /// pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { let mut f = f.debug_struct("Point3"); try_!(PWrapper(self.x).const_debug_fmt(f.field("x"))); try_!(PWrapper(self.y).const_debug_fmt(f.field("y"))); try_!(PWrapper(self.z).const_debug_fmt(f.field("z"))); f.finish() } /// pub const fn const_eq(&self, other: &Self) -> bool { self.x == other.x && self.y == other.y && self.z == other.z } } /// An example unit struct which implements const debug formatting. #[derive(Debug, Copy, Clone)] pub struct Unit; impl_fmt! { impl Unit; /// pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.debug_struct("Unit").finish() } /// pub const fn const_eq(&self, _other: &Self) -> bool { true } } const_format-0.2.32/src/formatting.rs000064400000000000000000000214131046102023000157160ustar 00000000000000#[doc(hidden)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Formatting { Debug, Display, } impl Formatting { /// Whether the current variant is `Display` #[inline(always)] pub const fn is_display(self) -> bool { matches!(self, Formatting::Display) } } /// How numbers are formatted in debug formatters. /// /// Hexadecimal or binary formatting in the formatting string from this crate imply /// debug formatting. /// /// #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum NumberFormatting { /// Formats numbers as decimal Decimal, /// Formats numbers as hexadecimal Hexadecimal, /// Formats numbers as binary Binary, } #[doc(hidden)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum HexFormatting { // micro-optimization // by having these values for the discriminants, // going from a number greater than 9 and smaller than 16 // to their ascii digits is as simple as `number + (hex_fmt as u8)` Upper = b'A' - 10, Lower = b'a' - 10, } impl NumberFormatting { #[cfg(test)] #[cfg(feature = "fmt")] pub(crate) const ALL: &'static [Self; 3] = &[ NumberFormatting::Decimal, NumberFormatting::Hexadecimal, NumberFormatting::Binary, ]; } //////////////////////////////////////////////////////////////////////////////// /// This type bundles configuration for how to format data into strings, including. /// /// # Number formatting /// /// How numbers are formatted in debug formatters, /// It can be accessed with the `num_fmt` method, and set with the `set_num_fmt` method. /// /// Each type of number formatting corresponds to a [`NumberFormatting`] variant: /// /// - `NumberFormatting::Decimal` (eg: `formatc!("{:?}", FOO)`): /// formats numbers as decimal. /// /// - `NumberFormatting::Hexadecimal` (eg: `formatc!("{:x}", FOO)`): /// formats numbers as hexadecimal. /// /// - `NumberFormatting::Binary` (eg: `formatc!("{:b}", FOO)`): /// formats numbers as binary. /// /// Hexadecimal or binary formatting in the formatting string from this crate imply /// debug formatting, /// and can be used to for example print an array of binary numbers. /// /// Note: Lowercase hexadecimal formatting requires calling the /// [`set_lower_hexadecimal`](#method.set_lower_hexadecimal) method. /// /// # Alternate flag /// /// A flag that types can use to be formatted differently when it's enabled, /// checked with the `.is_alternate()` method. /// /// The default behavior when it is enabled is this: /// /// - The Debug formater (eg: `formatc!("{:#?}", FOO)`): /// pretty print structs and enums. /// /// - The hexadecimal formater (eg: `formatc!("{:#x}", FOO)`): /// prefixes numbers with `0x`. /// /// - The binary formater (eg: `formatc!("{:#b}", FOO)`): /// prefixes numbers with `0b`.` /// /// [`Formatter`]: ./struct.Formatter.html /// #[must_use] #[derive(Debug, Copy, Clone)] pub struct FormattingFlags { num_fmt: NumberFormatting, // Whether the `NumberFormatting` prints hexadecimal digits in lowercase // (e.g: 0xf00, 0xF00) // // move this in 0.3.0 to `NumberFormatting`. hex_fmt: HexFormatting, is_alternate: bool, } #[doc(hidden)] impl FormattingFlags { pub const __REG: Self = Self::NEW.set_alternate(false).set_decimal(); pub const __HEX: Self = Self::NEW.set_alternate(false).set_hexadecimal(); pub const __LOWHEX: Self = Self::NEW.set_alternate(false).set_lower_hexadecimal(); pub const __BIN: Self = Self::NEW.set_alternate(false).set_binary(); pub const __A_REG: Self = Self::NEW.set_alternate(true).set_decimal(); pub const __A_HEX: Self = Self::NEW.set_alternate(true).set_hexadecimal(); pub const __A_LOWHEX: Self = Self::NEW.set_alternate(true).set_lower_hexadecimal(); pub const __A_BIN: Self = Self::NEW.set_alternate(true).set_binary(); } impl FormattingFlags { #[doc(hidden)] pub const DEFAULT: Self = Self { num_fmt: NumberFormatting::Decimal, hex_fmt: HexFormatting::Upper, is_alternate: false, }; /// Constructs a `FormattingFlags` with these values: /// /// - number formatting: NumberFormatting::Decimal /// /// - is alternate: false /// pub const NEW: Self = Self { num_fmt: NumberFormatting::Decimal, hex_fmt: HexFormatting::Upper, is_alternate: false, }; /// Constructs a `FormattingFlags` with these values: /// /// - number formatting: NumberFormatting::Decimal /// /// - is alternate: false /// #[inline] pub const fn new() -> Self { Self::NEW } /// Sets the integer formatting, /// /// This usually doesn't affect the outputted text in display formatting. #[inline] pub const fn set_num_fmt(mut self, num_fmt: NumberFormatting) -> Self { self.num_fmt = num_fmt; self } /// Sets the number formatting to `NumberFormatting::Decimal`. /// /// This means that numbers are written as decimal. #[inline] pub const fn set_decimal(mut self) -> Self { self.num_fmt = NumberFormatting::Decimal; self } /// Sets the number formatting to `NumberFormatting::Hexadecimal`. /// /// This means that numbers are written as uppercase hexadecimal. #[inline] pub const fn set_hexadecimal(mut self) -> Self { self.num_fmt = NumberFormatting::Hexadecimal; self.hex_fmt = HexFormatting::Upper; self } /// Sets the number formatting to `NumberFormatting::Hexadecimal`, /// and uses lowercase for alphabetic hexadecimal digits. /// /// This means that numbers are written as lowercase hexadecimal. #[inline] pub const fn set_lower_hexadecimal(mut self) -> Self { self.num_fmt = NumberFormatting::Hexadecimal; self.hex_fmt = HexFormatting::Lower; self } /// Sets the number formatting to `NumberFormatting::Binary`. /// /// This means that numbers are written as binary. #[inline] pub const fn set_binary(mut self) -> Self { self.num_fmt = NumberFormatting::Binary; self } /// Sets whether the formatting flag is enabled. #[inline] pub const fn set_alternate(mut self, is_alternate: bool) -> Self { self.is_alternate = is_alternate; self } /// Gets the current `NumberFormatting`. #[inline] pub const fn num_fmt(self) -> NumberFormatting { self.num_fmt } /// Gets whether the alternate flag is enabled #[inline] pub const fn is_alternate(self) -> bool { self.is_alternate } pub(crate) const fn hex_fmt(self) -> HexFormatting { self.hex_fmt } } //////////////////////////////////////////////////////////////////////////////// #[doc(hidden)] /// For writing into an array from the start pub struct LenAndArray { /// The amount of elements written in `array` pub len: usize, pub array: T, } #[doc(hidden)] /// For writing into an array from the end pub struct StartAndArray { /// The first element in `array` pub start: usize, pub array: T, } //////////////////////////////////////////////////////////////////////////////// #[doc(hidden)] pub struct ForEscaping { pub is_escaped: u128, pub is_backslash_escaped: u128, pub escape_char: [u8; 16], } impl ForEscaping { /// Gets the backslash escape for a character that is kwown to be escaped with a backslash. #[inline(always)] pub const fn get_backslash_escape(b: u8) -> u8 { FOR_ESCAPING.escape_char[(b & 0b1111) as usize] } } #[doc(hidden)] /// Converts 0..=0xF to its ascii representation of '0'..='9' and 'A'..='F' #[inline(always)] pub const fn hex_as_ascii(n: u8, hex_fmt: HexFormatting) -> u8 { if n < 10 { n + b'0' } else { n + (hex_fmt as u8) } } #[doc(hidden)] // Really clippy? Array indexing can panic you know. #[allow(clippy::no_effect)] pub const FOR_ESCAPING: &ForEscaping = { let mut is_backslash_escaped = 0; let escaped = [ (b'\t', b't'), (b'\n', b'n'), (b'\r', b'r'), (b'\'', b'\''), (b'"', b'"'), (b'\\', b'\\'), ]; // Using the fact that the characters above all have different bit patterns for // the lowest 4 bits. let mut escape_char = [0u8; 16]; __for_range! {i in 0..escaped.len() => let (code, escape) = escaped[i]; is_backslash_escaped |= 1 << code; let ei = (code & 0b1111) as usize; let prev_escape = escape_char[ei] as usize; ["Oh no, some escaped character uses the same 4 lower bits as another"][prev_escape]; escape_char[ei] = escape; } // Setting all the control characters as being escaped. let is_escaped = is_backslash_escaped | 0xFFFF_FFFF; &ForEscaping { escape_char, is_backslash_escaped, is_escaped, } }; const_format-0.2.32/src/lib.rs000064400000000000000000000341601046102023000143150ustar 00000000000000//! Compile-time string formatting. //! //! This crate provides types and macros for formatting strings at compile-time. //! //! # Rust versions //! //! There are some features that require a variety of stable Rust versions and //! others that require Rust nightly, //! the sections below describe the features that are available for each version. //! //! ### Rust 1.57.0 //! //! These macros are available in Rust 1.57.0: //! //! - [`concatcp`]: //! Concatenates `integers`, `bool`, `char`, and `&str` constants into a `&'static str` constant. //! //! - [`formatcp`]: //! [`format`]-like formatting which takes `integers`, `bool`, `char`, and `&str` constants, //! and emits a `&'static str` constant. //! //! - [`str_get`]: //! Indexes a `&'static str` constant, returning `None` when the index is out of bounds. //! //! - [`str_index`]: //! Indexes a `&'static str` constant. //! //! - [`str_repeat`]: //! Creates a `&'static str` by repeating a `&'static str` constant `times` times. //! //! - [`str_splice`]: //! Replaces a substring in a `&'static str` constant. //! //! - [`map_ascii_case`]: //! Converts a `&'static str` constant to a different casing style, //! determined by a [`Case`] argument. //! //! - [`str_replace`]: //! Replaces all the instances of a pattern in a `&'static str` constant with //! another `&'static str` constant. //! //! //! The `"assertcp"` feature enables the [`assertcp`], [`assertcp_eq`], //! and [`assertcp_ne`] macros. //! These macros are like the standard library assert macros, //! but evaluated at compile-time, //! with the limitation that they can only have primitive types as arguments //! (just like [`concatcp`] and [`formatcp`]). //! //! ### Rust 1.64.0 //! //! The `"rust_1_64"` feature enables these macros: //! //! - [`str_split`]: splits a string constant //! //! ### Rust nightly //! //! By enabling the "fmt" feature, you can use a [`std::fmt`]-like API. //! //! This requires the nightly compiler, because it uses mutable references in const fn, //! which have not been stabilized as of writing these docs. //! //! All the other features of this crate are implemented on top of the [`const_format::fmt`] API: //! //! - [`concatc`]: //! Concatenates many standard library and user defined types into a `&'static str` constant. //! //! - [`formatc`]: //! [`format`]-like macro that can format many standard library and user defined types into //! a `&'static str` constant. //! //! - [`writec`]: //! [`write`]-like macro that can format many standard library and user defined types //! into a type that implements [`WriteMarker`]. //! //! The `"derive"` feature enables the [`ConstDebug`] macro, //! and the `"fmt"` feature.
//! [`ConstDebug`] derives the [`FormatMarker`] trait, //! and implements an inherent `const_debug_fmt` method for compile-time debug formatting. //! //! The `"assertc"` feature enables the [`assertc`], [`assertc_eq`], [`assertc_ne`] macros, //! and the `"fmt"` feature.
//! These macros are like the standard library assert macros, but evaluated at compile-time. //! //! # Examples //! //! ### Concatenation of primitive types //! //! ```rust //! use const_format::concatcp; //! //! const NAME: &str = "Bob"; //! const FOO: &str = concatcp!(NAME, ", age ", 21u8,"!"); //! //! assert_eq!(FOO, "Bob, age 21!"); //! ``` //! //! ### Formatting primitive types //! //! ```rust //! use const_format::formatcp; //! //! const NAME: &str = "John"; //! //! const FOO: &str = formatcp!("{NAME}, age {}!", compute_age(NAME)); //! //! assert_eq!(FOO, "John, age 24!"); //! //! # const fn compute_age(s: &str) -> usize { s.len() * 6 } //! //! ``` //! //! ### Formatting custom types //! //! This example demonstrates how you can use the [`ConstDebug`] derive macro, //! and then format the type into a `&'static str` constant. //! //! This example requires Rust nightly, and the `"derive"` feature. //! #![cfg_attr(feature = "derive", doc = "```rust")] #![cfg_attr(not(feature = "derive"), doc = "```ignore")] //! #![feature(const_mut_refs)] //! //! use const_format::{ConstDebug, formatc}; //! //! #[derive(ConstDebug)] //! struct Message{ //! ip: [Octet; 4], //! value: &'static str, //! } //! //! #[derive(ConstDebug)] //! struct Octet(u8); //! //! const MSG: Message = Message{ //! ip: [Octet(127), Octet(0), Octet(0), Octet(1)], //! value: "Hello, World!", //! }; //! //! const FOO: &str = formatc!("{:?}", MSG); //! //! assert_eq!( //! FOO, //! "Message { ip: [Octet(127), Octet(0), Octet(0), Octet(1)], value: \"Hello, World!\" }" //! ); //! //! ``` //! //! ### Formatted const assertions //! //! This example demonstrates how you can use the [`assertcp_ne`] macro to //! do compile-time inequality assertions with formatted error messages. //! //! This requires the `"assertcp"` feature. //! #![cfg_attr(feature = "assertcp", doc = "```compile_fail")] #![cfg_attr(not(feature = "assertcp"), doc = "```ignore")] //! use const_format::assertcp_ne; //! //! macro_rules! check_valid_pizza{ //! ($user:expr, $topping:expr) => { //! assertcp_ne!( //! $topping, //! "pineapple", //! "You can't put pineapple on pizza, {}", //! $user, //! ); //! } //! } //! //! check_valid_pizza!("John", "salami"); //! check_valid_pizza!("Dave", "sausage"); //! check_valid_pizza!("Bob", "pineapple"); //! //! # fn main(){} //! ``` //! //! This is the compiler output: //! //! ```text //! error[E0080]: evaluation of constant value failed //! --> src/lib.rs:178:27 //! | //! 20 | check_valid_pizza!("Bob", "pineapple"); //! | ^^^^^^^^^^^ the evaluated program panicked at ' //! assertion failed: `(left != right)` //! left: `"pineapple"` //! right: `"pineapple"` //! You can't put pineapple on pizza, Bob //! ', src/lib.rs:20:27 //! //! //! ``` //! //!
//! //! # Limitations //! //! All of the macros from `const_format` have these limitations: //! //! - The formatting macros that expand to //! `&'static str`s can only use constants from concrete types, //! so while a `Type::::FOO` argument would be fine, //! `Type::::FOO` would not be (`T` being a type parameter). //! //! - Integer arguments must have a type inferrable from context, //! [more details in the Integer arguments section](#integer-args). //! //! - They cannot be used places that take string literals. //! So `#[doc = "foobar"]` cannot be replaced with `#[doc = concatcp!("foo", "bar") ]`. //! //! //! //! ### Integer arguments //! //! Integer arguments must have a type inferrable from context. //! so if you only pass an integer literal it must have a suffix. //! //! Example of what does compile: //! //! ```rust //! const N: u32 = 1; //! assert_eq!(const_format::concatcp!(N + 1, 2 + N), "23"); //! //! assert_eq!(const_format::concatcp!(2u32, 2 + 1u8, 3u8 + 1), "234"); //! ``` //! //! Example of what does not compile: //! ```compile_fail //! assert_eq!(const_format::concatcp!(1 + 1, 2 + 1), "23"); //! ``` //! //! # Renaming crate //! //! All function-like macros from `const_format` can be used when the crate is renamed. //! //! The [`ConstDebug`] derive macro has the `#[cdeb(crate = "foo::bar")]` attribute to //! tell it where to find the `const_format` crate. //! //! Example of renaming the `const_format` crate in the Cargo.toml file: //! ```toml //! [dependencies] //! cfmt = {version = "0.*", package = "const_format"} //! ``` //! //! # Cargo features //! //! - `"fmt"`: Enables the [`std::fmt`]-like API, //! requires Rust nightly because it uses mutable references in const fn.
//! This feature includes the [`formatc`]/[`writec`] formatting macros. //! //! - `"derive"`: requires Rust nightly, implies the `"fmt"` feature, //! provides the [`ConstDebug`] derive macro to format user-defined types at compile-time.
//! This implicitly uses the `syn` crate, so clean compiles take a bit longer than without the feature. //! //! - `"assertc"`: requires Rust nightly, implies the `"fmt"` feature, //! enables the [`assertc`], [`assertc_eq`], and [`assertc_ne`] assertion macros.
//! This feature was previously named `"assert"`, //! but it was renamed to avoid confusion with the `"assertcp"` feature. //! //! - `"assertcp"`: //! Enables the [`assertcp`], [`assertcp_eq`], and [`assertcp_ne`] assertion macros. //! //! - `"rust_1_64"`: Enables the [`str_split`] macro. //! Allows the `as_bytes_alt` methods and `slice_up_to_len_alt` methods to run //! in constant time, rather than linear time (proportional to the truncated part of the slice). //! //! # No-std support //! //! `const_format` is unconditionally `#![no_std]`, it can be used anywhere Rust can be used. //! //! # Minimum Supported Rust Version //! //! `const_format` requires Rust 1.57.0. //! //! Features that require newer versions of Rust, or the nightly compiler, //! need to be explicitly enabled with cargo features. //! //! //! [`assertc`]: ./macro.assertc.html //! //! [`assertc_eq`]: ./macro.assertc_eq.html //! //! [`assertc_ne`]: ./macro.assertc_ne.html //! //! [`assertcp`]: ./macro.assertcp.html //! //! [`assertcp_eq`]: ./macro.assertcp_eq.html //! //! [`assertcp_ne`]: ./macro.assertcp_ne.html //! //! [`concatcp`]: ./macro.concatcp.html //! //! [`formatcp`]: ./macro.formatcp.html //! //! [`format`]: https://doc.rust-lang.org/std/macro.format.html //! //! [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html //! //! [`const_format::fmt`]: ./fmt/index.html //! //! [`concatc`]: ./macro.concatc.html //! //! [`formatc`]: ./macro.formatc.html //! //! [`writec`]: ./macro.writec.html //! //! [`write`]: https://doc.rust-lang.org/std/macro.write.html //! //! [`Formatter`]: ./fmt/struct.Formatter.html //! //! [`StrWriter`]: ./fmt/struct.StrWriter.html //! //! [`ConstDebug`]: ./derive.ConstDebug.html //! //! [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html //! //! [`WriteMarker`]: ./marker_traits/trait.WriteMarker.html //! //! [`map_ascii_case`]: ./macro.map_ascii_case.html //! //! [`Case`]: ./enum.Case.html //! //! //! [`str_get`]: ./macro.str_get.html //! //! [`str_index`]: ./macro.str_index.html //! //! [`str_repeat`]: ./macro.str_repeat.html //! //! [`str_splice`]: ./macro.str_splice.html //! //! [`str_replace`]: ./macro.str_replace.html //! //! [`str_split`]: ./macro.str_split.html //! //! [`str::replace`]: https://doc.rust-lang.org/std/primitive.str.html#method.replace //! #![no_std] #![cfg_attr(feature = "fmt", feature(const_mut_refs))] #![cfg_attr(feature = "__docsrs", feature(doc_cfg))] #![deny(rust_2018_idioms)] // This lint is silly #![allow(clippy::blacklisted_name)] // This lint is silly #![allow(clippy::needless_doctest_main)] #![deny(clippy::missing_safety_doc)] #![deny(clippy::shadow_unrelated)] #![deny(clippy::wildcard_imports)] // All The methods that take self by value are for small Copy types #![allow(clippy::wrong_self_convention)] #![allow(clippy::init_numbered_fields)] #![deny(missing_docs)] include! {"const_debug_derive.rs"} #[macro_use] mod macros; mod formatting; #[cfg(feature = "assertc")] mod equality; #[doc(hidden)] #[cfg(feature = "assertcp")] #[macro_use] pub mod for_assert_macros; mod char_encoding; mod pargument; mod const_generic_concatcp; #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] pub mod utils; #[doc(hidden)] #[cfg(any(feature = "fmt", feature = "assertcp"))] mod slice_cmp; #[doc(hidden)] pub mod __hidden_utils; #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] pub mod for_examples; #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] pub mod marker_traits; #[cfg(feature = "__test")] pub mod test_utils; #[cfg(feature = "__test")] #[allow(missing_docs)] pub mod doctests; #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] pub mod fmt; #[cfg(feature = "fmt")] #[doc(hidden)] pub mod msg; #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg_attr(not(feature = "fmt"), doc(hidden))] pub mod wrapper_types; #[doc(hidden)] pub mod __ascii_case_conv; #[doc(hidden)] pub mod __str_methods; pub use __str_methods::SplicedStr; pub use __ascii_case_conv::Case; #[cfg(feature = "fmt")] #[doc(no_inline)] pub use crate::fmt::{Error, Formatter, FormattingFlags, Result, StrWriter, StrWriterMut}; #[cfg(feature = "fmt")] pub use crate::wrapper_types::ascii_str::AsciiStr; #[cfg(feature = "fmt")] pub use crate::wrapper_types::sliced::Sliced; #[cfg_attr(not(feature = "fmt"), doc(hidden))] pub use crate::wrapper_types::pwrapper::PWrapper; #[doc(hidden)] #[allow(non_snake_case)] pub mod __cf_osRcTFl4A { pub use crate::*; } #[doc(hidden)] pub mod pmr { pub use {bool, str, u8, usize}; pub use const_format_proc_macros::{__concatcp_impl, __formatcp_impl, respan_to}; #[cfg(feature = "fmt")] pub use const_format_proc_macros::{__formatc_if_impl, __formatc_impl, __writec_impl}; #[cfg(feature = "assertcp")] pub use const_format_proc_macros::__formatcp_if_impl; pub use core::{ cmp::Reverse, convert::identity, mem::transmute, num::Wrapping, ops::Range, option::Option::{self, None, Some}, result::Result::{self, Err, Ok}, }; pub use crate::const_generic_concatcp::__priv_concatenate; #[cfg(feature = "assertcp")] pub use crate::for_assert_macros::{assert_, ConcatArgsIf}; #[cfg(feature = "fmt")] pub use crate::{ fmt::{ComputeStrLength, Error, Formatter, StrWriter, StrWriterMut, ToResult}, marker_traits::{ FormatMarker, IsAFormatMarker, IsAWriteMarker, IsNotStdKind, IsStdKind, WriteMarker, }, }; pub use crate::{ formatting::{ hex_as_ascii, ForEscaping, Formatting, FormattingFlags, HexFormatting, LenAndArray, NumberFormatting, StartAndArray, FOR_ESCAPING, }, pargument::{PArgument, PConvWrapper, PVariant}, wrapper_types::PWrapper, }; #[doc(hidden)] #[repr(transparent)] pub struct __AssertStr { pub x: &'static str, } } #[cfg(all(feature = "__test", feature = "derive", feature = "assertcp"))] identity! { #[doc = include_str!("../../README.md")] pub struct ReadmeTest; } #[cfg(all(test, not(feature = "__test")))] compile_error! { "tests must be run with the \"__test\" feature" } const_format-0.2.32/src/macros/assertions/assertc_macros.rs000064400000000000000000000312751046102023000222410ustar 00000000000000macro_rules! with_shared_docs {( $(#[$before_clarification:meta])* ;clarification $(#[$before_syntax:meta])* ;syntax $(#[$after_syntax:meta])* ;error_message $(#[$after_error_message:meta])* ;limitations $item:item ) => ( $(#[$before_clarification])* /// /// This macro requires the `"assertc"` feature to be exported. /// $(#[$before_syntax])* /// /// This macro uses [the same syntax](./fmt/index.html#fmtsyntax) /// for the format string and supports the same formatting arguments as the /// [`formatc`] macro. /// $(#[$after_syntax])* $(#[$after_error_message])* /// # Limitations /// /// This macro has these limitations: /// /// - It can only use constants that involve concrete types, /// so while a `Type::::FOO` in an argument would be fine, /// `Type::::FOO` would not be (`T` being a type parameter). /// /// - Integer arguments must have a type inferrable from context, /// [as described in the integer arguments section in the root module /// ](./index.html#integer-args). /// /// [`PWrapper`]: ./struct.PWrapper.html /// [`formatc`]: ./macro.formatc.html /// [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html /// $item )} //////////////////////////////////////////////////////////////////////////////// with_shared_docs! { /// Compile-time assertions with formatting. /// ;clarification ;syntax ;error_message ;limitations /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::assertc; /// /// use std::mem::size_of; /// /// assertc!( /// size_of::<&str>() == size_of::<&[u8]>(), /// "The size of `&str`({} bytes) and `&[u8]`({} bytes) aren't the same?!?!", /// size_of::<&str>(), /// size_of::<&[u8]>(), /// ); /// /// # fn main(){} /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// use const_format::assertc; /// /// const L: u64 = 2; /// const R: u64 = 2; /// /// assertc!(L + R == 5, "{} plus {} isn't 5, buddy", L, R); /// /// # fn main(){} /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertc_macros.rs:109:10 /// | /// 11 | assertc!(L + R == 5, "{} plus {} isn't 5, buddy", L, R); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed. /// 2 plus 2 isn't 5, buddy /// ', const_format/src/macros/assertions/assertc_macros.rs:11:10 /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertc")))] #[macro_export] macro_rules! assertc { ($($parameters:tt)*) => ( $crate::__assertc_inner!{ __formatc_if_impl ($($parameters)*) ($($parameters)*) } ); } } //////////////////////////////////////////////////////////////////////////////// macro_rules! assert_eq_docs { ( $(#[$documentation:meta])* ;documentation $item:item ) => ( with_shared_docs! { $(#[$documentation])* ;clarification /// # Arguments /// /// This macro accepts these types for comparison and debug printing: /// /// - Standard library types for which [`PWrapper`] wrapping that type /// has a `const_eq` method. /// This includes all integer types, `&str`, slices/arrays of integers/`&str`, /// Options of integers/`&str`, etc. /// /// - non-standard-library types that implement [`FormatMarker`] with debug formatting
/// and have a `const fn const_eq(&self, other:&Self) -> bool` inherent method, /// ;syntax ;error_message ;limitations $item } ) } assert_eq_docs! { /// Compile-time equality assertion with formatting. /// ;documentation /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::assertc_eq; /// /// use std::mem::size_of; /// /// assertc_eq!(size_of::(), size_of::<[usize;1]>()); /// /// const TWO: u32 = 2; /// assertc_eq!(TWO, TWO, "Oh no {} doesn't equal itself!!", TWO); /// /// # fn main(){} /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// use const_format::assertc_eq; /// /// use std::mem::size_of; /// /// assertc_eq!(size_of::(), size_of::()); /// /// # fn main(){} /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertc_macros.rs:222:13 /// | /// 10 | assertc_eq!(size_of::(), size_of::()); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left == right)` /// left: `4` /// right: `1`', const_format/src/macros/assertions/assertc_macros.rs:10:13 /// ``` /// /// ### Comparing user-defined types /// /// This example demonstrates how you can assert that two values of a /// user-defined type are equal. /// #[cfg_attr(feature = "derive", doc = "```compile_fail")] #[cfg_attr(not(feature = "derive"), doc = "```ignore")] /// #![feature(const_mut_refs)] /// /// use const_format::{Formatter, PWrapper}; /// use const_format::{ConstDebug, assertc_eq, try_}; /// /// const POINT: Point = Point{ x: 5, y: 8, z: 13 }; /// const OTHER_POINT: Point = Point{ x: 21, y: 34, z: 55 }; /// /// assertc_eq!(POINT, OTHER_POINT); /// /// #[derive(ConstDebug)] /// pub struct Point { /// pub x: u32, /// pub y: u32, /// pub z: u32, /// } /// /// impl Point { /// pub const fn const_eq(&self, other: &Self) -> bool { /// self.x == other.x && /// self.y == other.y && /// self.z == other.z /// } /// } /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/assertions.rs:331:14 /// | /// 12 | assertc_eq!(POINT, OTHER_POINT); /// | ^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left == right)` /// left: `Point { /// x: 5, /// y: 8, /// z: 13, /// }` /// right: `Point { /// x: 21, /// y: 34, /// z: 55, /// }`', src/macros/assertions.rs:12:14 /// /// error: aborting due to previous error /// /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertc")))] #[macro_export] macro_rules! assertc_eq { ($($parameters:tt)*) => ( $crate::__assertc_equality_inner!{ ($($parameters)*) ($($parameters)*) ( == ) ("==") } ); } } assert_eq_docs! { /// Compile-time inequality assertion with formatting. /// ;documentation /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::assertc_ne; /// /// use std::mem::size_of; /// /// assertc_ne!(size_of::(), size_of::<[u32; 2]>()); /// /// const TWO: u32 = 2; /// const THREE: u32 = 3; /// assertc_ne!(TWO, THREE, "Oh no {} somehow equals {}!!", TWO, THREE); /// /// # fn main(){} /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// #![feature(const_mut_refs)] /// /// use const_format::assertc_ne; /// /// use std::mem::size_of; /// /// type Foo = u32; /// /// assertc_ne!(size_of::(), size_of::()); /// /// # fn main(){} /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertc_macros.rs:350:13 /// | /// 12 | assertc_ne!(size_of::(), size_of::()); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left != right)` /// left: `4` /// right: `4`', const_format/src/macros/assertions/assertc_macros.rs:12:13 /// ``` /// /// ### Comparing user-defined types /// /// This example demonstrates how you can assert that two values of a /// user-defined type are unequal. /// #[cfg_attr(feature = "derive", doc = "```compile_fail")] #[cfg_attr(not(feature = "derive"), doc = "```ignore")] /// #![feature(const_mut_refs)] /// /// use const_format::{Formatter, PWrapper}; /// use const_format::{ConstDebug, assertc_ne, try_}; /// /// const POINT: Point = Point{ x: 5, y: 8, z: 13 }; /// /// assertc_ne!(POINT, POINT); /// /// #[derive(ConstDebug)] /// pub struct Point { /// pub x: u32, /// pub y: u32, /// pub z: u32, /// } /// /// impl Point { /// pub const fn const_eq(&self, other: &Self) -> bool { /// self.x == other.x && /// self.y == other.y && /// self.z == other.z /// } /// } /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> src/macros/assertions.rs:451:14 /// | /// 11 | assertc_ne!(POINT, POINT); /// | ^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left != right)` /// left: `Point { /// x: 5, /// y: 8, /// z: 13, /// }` /// right: `Point { /// x: 5, /// y: 8, /// z: 13, /// }`', src/macros/assertions.rs:11:14 /// /// error: aborting due to previous error /// /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertc")))] #[macro_export] macro_rules! assertc_ne { ($($parameters:tt)*) => ( $crate::__assertc_equality_inner!{ ($($parameters)*) ($($parameters)*) ( != ) ("!=") } ); } } #[doc(hidden)] #[macro_export] macro_rules! __assertc_equality_inner { ( ($($parameters:tt)*) ( $left:expr, $right:expr $(, $fmt_literal:expr $(,$fmt_arg:expr)*)? $(,)? ) ($($op:tt)*) ($op_str:expr) )=>{ const _: () = { use $crate::__cf_osRcTFl4A; use $crate::pmr::respan_to as __cf_respan_to; const LEFT: $crate::pmr::bool = { // Have to use `respan_to` to make the `multiple coerce found` error // point at the `$left` argument here. use $crate::coerce_to_fmt as __cf_coerce_to_fmt; match [&$left, &$right] { __cf_respan_to!(($left) [left, right]) => __cf_respan_to!(($left) __cf_coerce_to_fmt!(left).const_eq(right)), } }; const RIGHT: $crate::pmr::bool = true; $crate::__assertc_common!{ __formatc_if_impl ($($parameters)*) (LEFT $($op)* RIGHT) ( concat!( "\nassertion failed: `(left ", $op_str, " right)`\n", " left: `{left_NHPMWYD3NJA:#?}`\n\ right: `{right_NHPMWYD3NJA:#?}`", $("\n", $fmt_literal, "\n")? ), $($($fmt_arg,)*)? left_NHPMWYD3NJA = $left, right_NHPMWYD3NJA = $right ) } }; } } const_format-0.2.32/src/macros/assertions/assertcp_macros.rs000064400000000000000000000207711046102023000224200ustar 00000000000000macro_rules! with_shared_docs {( $(#[$before_clarification:meta])* ;clarification $(#[$before_syntax:meta])* ;syntax $(#[$after_syntax:meta])* ;limitations $item:item ) => ( $(#[$before_clarification])* /// /// [For **examples** look here](#examples) /// /// This macro requires the `"assertcp"` feature to be exported.
/// $(#[$before_syntax])* /// # Syntax /// /// This macro uses the same syntax /// for the format string and formatting arguments as the /// [`formatcp`](crate::formatcp) macro. /// $(#[$after_syntax])* /// # Limitations /// /// This macro can only take constants of these types as arguments: /// /// - `&str` /// /// - `i*`/`u*` (all the primitive integer types). /// /// - `char` /// /// - `bool` /// /// This macro also has these limitations: /// /// - It can only use constants that involve concrete types, /// so while a `Type::::FOO` in an argument would be fine, /// `Type::::FOO` would not be (`T` being a type parameter). /// /// - Integer arguments must have a type inferrable from context, /// [as described in the integer arguments section in the root module /// ](./index.html#integer-args). /// $item )} with_shared_docs! { /// Compile-time assertion with formatting. /// ;clarification ;syntax ;limitations /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// use const_format::assertcp; /// /// use std::mem::align_of; /// /// assertcp!( /// align_of::<&str>() == align_of::(), /// "The alignment of `&str`({} bytes) and `usize`({} bytes) isn't the same?!?!", /// align_of::<&str>(), /// align_of::(), /// ); /// /// # fn main(){} /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// use const_format::assertcp; /// /// const L: u64 = 2; /// const R: u32 = 5; /// /// assertcp!(L.pow(R) == 64, "{L} to the {R} isn't 64, it's {}", L.pow(R)); /// /// # fn main(){} /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertcp_macros.rs:116:11 /// | /// 9 | assertcp!(L.pow(R) == 64, "{L} to the {R} isn't 64, it's {}", L.pow(R)); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed. /// 2 to the 5 isn't 64, it's 32 /// ', const_format/src/macros/assertions/assertcp_macros.rs:9:11 /// /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertcp")))] #[macro_export] macro_rules! assertcp { ($($parameters:tt)*) => ( $crate::__assertc_inner!{ __formatcp_if_impl ($($parameters)*) ($($parameters)*) } ); } } #[doc(hidden)] #[macro_export] macro_rules! __assertcp_equality_inner { ( ($($parameters:tt)*) ( $left:expr, $right:expr $(, $fmt_literal:expr $(,$fmt_arg:expr)*)? $(,)? ) ($($op:tt)*) ($op_str:expr) )=>{ #[allow(non_snake_case)] const _: () = { use $crate::__cf_osRcTFl4A; const ARGS_NHPMWYD3NJA: ($crate::pmr::bool, $crate::pmr::PArgument, $crate::pmr::PArgument) = { let left = $crate::PWrapper($left); let right = $crate::pmr::PConvWrapper($right); let cond = left.const_eq(&right.0); let fmt = $crate::pmr::FormattingFlags::NEW.set_alternate(true); ( cond, $crate::pmr::PConvWrapper(left.0).to_pargument_debug(fmt), right.to_pargument_debug(fmt), ) }; $crate::__assertc_common!{ __formatcp_if_impl ($($parameters)*) (ARGS_NHPMWYD3NJA.0 $($op)* true) ( concat!( "\nassertion failed: `(left ", $op_str, " right)`\n", " left: `{left_NHPMWYD3NJA:#?}`\n\ right: `{right_NHPMWYD3NJA:#?}`", $("\n", $fmt_literal, "\n")? ), $($($fmt_arg,)*)? left_NHPMWYD3NJA = ARGS_NHPMWYD3NJA.1, right_NHPMWYD3NJA = ARGS_NHPMWYD3NJA.2 ) } }; } } with_shared_docs! { /// Compile-time equality assertion with formatting. /// ;clarification ;syntax ;limitations /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// use const_format::assertcp_eq; /// /// const NAME: &str = "Bob"; /// /// assertcp_eq!(NAME, "Bob", "Guessed wrong, the right value is {}", NAME); /// /// const SUM: u8 = 1 + 2 + 3; /// assertcp_eq!(6u8, SUM, "Guessed wrong, the right value is {}", SUM); /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// use const_format::assertcp_eq; /// /// use std::mem::size_of; /// /// #[repr(C)] /// struct Type(u16, u16, u16); /// /// assertcp_eq!(size_of::(), size_of::<[u16; 2]>(), "Whoops, `Type` is too large"); /// /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertcp_macros.rs:235:14 /// | /// 12 | assertcp_eq!(size_of::(), size_of::<[u16; 2]>(), "Whoops, `Type` is too large"); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left == right)` /// left: `6` /// right: `4` /// Whoops, `Type` is too large /// ', const_format/src/macros/assertions/assertcp_macros.rs:12:14 /// /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertcp")))] #[macro_export] macro_rules! assertcp_eq { ($($parameters:tt)*) => ( $crate::__assertcp_equality_inner!{ ($($parameters)*) ($($parameters)*) ( == ) ("==") } ); } } with_shared_docs! { /// Compile-time inequality assertion with formatting. /// ;clarification ;syntax ;limitations /// /// # Examples /// /// ### Passing assertion /// /// ```rust /// use const_format::assertcp_ne; /// /// assertcp_ne!(std::mem::size_of::(), 1usize, "Oh no, usize is tiny!"); /// /// const CHAR: char = ';'; /// assertcp_ne!(CHAR, '.', "CHAR must not be a dot!"); /// ``` /// /// ### Failing assertion /// /// This example demonstrates a failing assertion, /// and how the compiler error looks like as of 2023-10-14. /// /// ```compile_fail /// use const_format::assertcp_ne; /// /// const NAME: &str = ""; /// assertcp_ne!(NAME, "", "NAME must not be empty!"); /// /// ``` /// /// This is the compiler output: /// /// ```text /// error[E0080]: evaluation of constant value failed /// --> const_format/src/macros/assertions/assertcp_macros.rs:297:14 /// | /// 8 | assertcp_ne!(NAME, "", "NAME must not be empty!"); /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at ' /// assertion failed: `(left != right)` /// left: `""` /// right: `""` /// NAME must not be empty! /// ', const_format/src/macros/assertions/assertcp_macros.rs:8:14 /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "assertcp")))] #[macro_export] macro_rules! assertcp_ne { ($($parameters:tt)*) => ( $crate::__assertcp_equality_inner!{ ($($parameters)*) ($($parameters)*) ( != ) ("!=") } ); } } const_format-0.2.32/src/macros/assertions.rs000064400000000000000000000026041046102023000172230ustar 00000000000000#[cfg(feature = "assertc")] mod assertc_macros; #[cfg(feature = "assertcp")] mod assertcp_macros; #[doc(hidden)] #[macro_export] macro_rules! __assertc_inner { ( $fmt_macro:ident ($($parameters:tt)*) ($cond:expr $(, $fmt_literal:expr $(,$fmt_arg:expr)*)? $(,)?) ) => { #[allow(non_snake_case)] const _: () = { use $crate::__cf_osRcTFl4A; $crate::__assertc_common!{ $fmt_macro ($($parameters)*) ($cond) ( concat!( "\nassertion failed.\n", $($fmt_literal,)? "\n", ) $($(,$fmt_arg)*)? ) } }; } } #[doc(hidden)] #[macro_export] macro_rules! __assertc_common { ( $fmt_macro:ident ($($span:tt)*) ($cond:expr) ($($fmt_literal:expr $(,$fmt_arg:expr)*)?) ) => ( const PANIC_IF_TRUE_NHPMWYD3NJA: bool = !($cond); const MSG_NHPMWYD3NJA: &str = $crate::pmr::$fmt_macro!( (PANIC_IF_TRUE_NHPMWYD3NJA) ($($fmt_literal,)?), $($($fmt_arg,)*)? ); __cf_osRcTFl4A::pmr::respan_to!{ ($($span)*) __cf_osRcTFl4A::pmr::assert_(PANIC_IF_TRUE_NHPMWYD3NJA, MSG_NHPMWYD3NJA) } ); } const_format-0.2.32/src/macros/call_debug_fmt.rs000064400000000000000000000135411046102023000177620ustar 00000000000000/// For debug formatting of some specific generic std types, and other types. /// /// # Errors /// /// This macro propagates errors from the debug formatting methods that /// it calls, by `return`ing them. /// /// # Macro variants /// /// The macro has these variants: /// /// - `slice` (also `array`): to format a slice or an array of *any debug type. /// /// - `Option`: to format an `Option` of *any debug type. /// /// - `newtype`: to format a single field tuple struct (eg: `struct Foo(Bar);`) /// which wraps *any debug type. /// /// - `std`: to format the standard library types, where `PWrapper` /// has a `const_debug_fmt` method.
/// /// - `other`: to format non-standard-library types that have a `const_debug_fmt` method. /// /// *"any debug type" meaning types that have a `const_debug_fmt` method /// /// # Example /// /// ### Printing all of them /// /// Printing all of the kinds of types this supports. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ /// for_examples::{Point3, Unit}, /// Error, Formatter, FormattingFlags, StrWriter, /// call_debug_fmt, try_, unwrap, /// }; /// /// use std::num::Wrapping; /// /// const CAP: usize = 512; /// /// // `call_debug_fmt` implicitly returns on error, /// // so the function has to return a `Result<_, const_format::Error>` /// const fn make() -> Result, Error> { /// let mut writer = StrWriter::new([0; CAP]); /// let mut fmt = Formatter::from_sw(&mut writer, FormattingFlags::NEW); /// let mut fmt = fmt.debug_struct("Foo"); /// /// let point = Point3{ x: 5, y: 8, z: 13 }; /// /// call_debug_fmt!(array, [Unit, Unit], fmt.field("array") ); /// call_debug_fmt!(slice, [0u8, 1], fmt.field("slice") ); /// call_debug_fmt!(Option, Some(point), fmt.field("option") ); /// call_debug_fmt!(newtype NumWrapping, Wrapping(255u16), fmt.field("newtype") ); /// call_debug_fmt!(std, false, fmt.field("std_") ); /// call_debug_fmt!(other, point, fmt.field("other") ); /// /// try_!(fmt.finish()); /// Ok(writer) /// } /// /// const TEXT: &str = { /// const PROM: &StrWriter<[u8]> = &unwrap!(make()); /// PROM.as_str_alt() /// }; /// /// const EXPECTED: &str = "\ /// Foo { \ /// array: [Unit, Unit], \ /// slice: [0, 1], \ /// option: Some(Point3 { x: 5, y: 8, z: 13 }), \ /// newtype: NumWrapping(255), \ /// std_: false, \ /// other: Point3 { x: 5, y: 8, z: 13 } \ /// }\ /// "; /// /// assert_eq!(TEXT, EXPECTED); /// /// ``` /// /// ### Used as `formatc` argument /// /// This macro can be used in the formatting macros by using the Formatter in the argument,
/// with the `|formatter_ident| expression_that_uses_formatter ` syntax. /// /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ /// for_examples::{Point3, Unit}, /// Error, Formatter, FormattingFlags, StrWriter, /// call_debug_fmt, formatc, try_, unwrap, /// }; /// /// use std::num::Wrapping; /// /// const POINT: Point3 = Point3{ x: 5, y: 8, z: 13 }; /// /// const TEXT: &str = formatc!( /// "a: {},b: {},c: {},d: {},e: {},f: {},", /// |fmt| call_debug_fmt!(array, [Unit, Unit], fmt ), /// |fmt| call_debug_fmt!(slice, [0u8, 1], fmt ), /// |fmt| call_debug_fmt!(Option, Some(POINT), fmt ), /// |fmt| call_debug_fmt!(newtype NumWrapping, Wrapping(255u16), fmt ), /// |fmt| call_debug_fmt!(std, false, fmt ), /// |fmt| call_debug_fmt!(other, POINT, fmt ), /// ); /// /// const EXPECTED: &str = "\ /// a: [Unit, Unit],\ /// b: [0, 1],\ /// c: Some(Point3 { x: 5, y: 8, z: 13 }),\ /// d: NumWrapping(255),\ /// e: false,\ /// f: Point3 { x: 5, y: 8, z: 13 },\ /// "; /// /// assert_eq!(TEXT, EXPECTED); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[macro_export] macro_rules! call_debug_fmt { (array, $expr:expr, $formatter:expr $(,)* ) => {{ match (&$expr, $formatter.borrow_mutably()) { (expr, formatter) => { let mut n = 0; let len = expr.len(); let mut f = formatter.debug_list(); while n != len { $crate::__call_debug_fmt_dispatch!(&expr[n], f.entry()); n += 1; } $crate::try_!(f.finish()); } } }}; (slice, $expr:expr, $formatter:expr $(,)*) => { $crate::call_debug_fmt!(array, $expr, $formatter) }; (Option, $expr:expr, $formatter:expr $(,)*) => {{ match $formatter.borrow_mutably() { formatter => $crate::try_!(match &$expr { $crate::pmr::Some(x) => { let mut f = formatter.debug_tuple("Some"); $crate::__call_debug_fmt_dispatch!(x, f.field()); f.finish() } $crate::pmr::None => formatter.write_str("None"), }), } }}; (newtype $name:ident, $expr:expr, $formatter:expr $(,)*) => { match (&$expr, $formatter.borrow_mutably()) { (newtype_, formatter) => { let mut f = formatter.debug_tuple(stringify!($name)); $crate::__call_debug_fmt_dispatch!(&newtype_.0, f.field()); $crate::try_!(f.finish()); } } }; (std, $expr:expr, $formatter:expr $(,)*) => { if let Err(e) = $crate::coerce_to_fmt!(&$expr).const_debug_fmt($formatter) { return Err(e); } }; (other, $expr:expr, $formatter:expr $(,)*) => { if let Err(e) = $expr.const_debug_fmt($formatter) { return Err(e); } }; } #[doc(hidden)] #[macro_export] macro_rules! __call_debug_fmt_dispatch { ($e:expr, $f:expr) => { if let Err(e) = $crate::coerce_to_fmt!(&$e).const_debug_fmt($f) { return Err(e); } }; } const_format-0.2.32/src/macros/constructors.rs000064400000000000000000000022001046102023000175710ustar 00000000000000/// Constructs an [`AsciiStr`] constant from an ascii string, /// /// # Compile-time errors /// /// This macro produces a compile-time error by indexing an empty array with /// the index of the first non-ascii byte. /// /// # Example /// /// ```rust /// use const_format::ascii_str; /// /// let fooo = ascii_str!("hello"); /// /// assert_eq!(fooo.as_str(), "hello"); /// /// // You can pass constants as arguments! /// const BAR_S: &str = "world"; /// let bar = ascii_str!(BAR_S); /// /// assert_eq!(bar.as_str(), "world"); /// /// ``` /// /// ```compile_fail /// use const_format::ascii_str; /// /// let fooo = ascii_str!("Γειά σου Κόσμε!"); /// /// ``` /// /// [`AsciiStr`]: ./struct.AsciiStr.html /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! ascii_str { ($str:expr $(,)*) => {{ const __CF_ASCII_STR_CONSTANT: $crate::AsciiStr<'static> = { match $crate::AsciiStr::new($str.as_bytes()) { Ok(x) => x, $crate::pmr::Err(e) => [][e.invalid_from], } }; __CF_ASCII_STR_CONSTANT }}; } const_format-0.2.32/src/macros/fmt_macros.rs000064400000000000000000000463471046102023000171770ustar 00000000000000/// Concatenates constants of primitive types into a `&'static str`. /// /// Each argument is stringified after evaluating it, so `concatcp!(1u8 + 3) == "4"` /// /// [For **examples** look here](#examples) /// /// `concatcp` stands for "concatenate constants (of) primitives" /// /// # Limitations /// /// This macro can only take constants of these types as inputs: /// /// - `&str` /// /// - `i*`/`u*` (all the primitive integer types). /// /// - `char` /// /// - `bool` /// /// This macro also shares /// [the limitations described in here](./index.html#macro-limitations) /// as well. /// /// # Examples /// /// ### Literal arguments /// /// /// ```rust /// use const_format::concatcp; /// /// const MSG: &str = concatcp!(2u8, "+", 2u8, '=', 2u8 + 2); /// /// assert_eq!(MSG, "2+2=4"); /// /// ``` /// /// ### `const` arguments /// /// ```rust /// use const_format::concatcp; /// /// const PASSWORD: &str = "password"; /// /// const fn times() -> u64 { 10 } /// /// const MSG: &str = /// concatcp!("The password is \"", PASSWORD, "\", you can only guess ", times(), " times."); /// /// assert_eq!(MSG, r#"The password is "password", you can only guess 10 times."#); /// /// ``` /// #[macro_export] macro_rules! concatcp { ()=>{""}; ($($arg: expr),* $(,)?)=>( $crate::pmr::__AssertStr {x:{ use $crate::__cf_osRcTFl4A; $crate::pmr::__concatcp_impl!{ $( ( $arg ), )* } }}.x ); } #[doc(hidden)] #[macro_export] macro_rules! __concatcp_inner { ($variables:expr) => {{ #[doc(hidden)] const ARR_LEN: usize = $crate::pmr::PArgument::calc_len($variables); #[doc(hidden)] const CONCAT_ARR: &$crate::pmr::LenAndArray<[u8; ARR_LEN]> = &$crate::pmr::__priv_concatenate($variables); #[doc(hidden)] #[allow(clippy::transmute_ptr_to_ptr)] const CONCAT_STR: &str = unsafe { // This transmute truncates the length of the array to the amound of written bytes. let slice = $crate::pmr::transmute::<&[u8; ARR_LEN], &[u8; CONCAT_ARR.len]>(&CONCAT_ARR.array); $crate::__priv_transmute_bytes_to_str!(slice) }; CONCAT_STR }}; } //////////////////////////////////////////////////////////////////////////////// /// Formats constants of primitive types into a `&'static str` /// /// [For **examples** look here](#examples) /// /// `formatcp` stands for "format constants (of) primitives" /// /// # Syntax /// /// This macro uses a limited version of the syntax from the standard library [`format`] macro, /// it can do these things: /// /// - Take positional arguments: `formatcp!("{}{0}", "hello" )` /// /// - Take named arguments: `formatcp!("{a}{a}", a = "hello" )` /// /// - Use constants from scope as arguments: `formatcp!("{FOO}")`
/// equivalent to the [`format_args_implicits` RFC] /// /// - Use Debug-like formatting (eg: `formatcp!("{:?}", "hello" )`:
/// Similar to how `Debug` formatting in the standard library works, /// except that it does not escape unicode characters. /// /// - Use LowerHex formatting (eg: `formatcp!("{:x}", "hello" )`):
/// Formats numbers as lowercase hexadecimal. /// The alternate version (written as `"{:#x}"`) prefixes the number with `0x` /// /// - Use UpperHex formatting (eg: `formatcp!("{:X}", "hello" )`):
/// Formats numbers as capitalized hexadecimal. /// The alternate version (written as `"{:#X}"`) prefixes the number with `0x` /// /// - Use Binary formatting (eg: `formatcp!("{:b}", "hello" )`)
/// The alternate version (written as `"{:#b}"`) prefixes the number with `0b` /// /// - Use Display formatting: `formatcp!("{}", "hello" )` /// /// /// # Limitations /// /// This macro can only take constants of these types as inputs: /// /// - `&str` /// /// - `i*`/`u*` (all the primitive integer types). /// /// - `char` /// /// - `bool` /// /// This macro also shares /// [the limitations described in here](./index.html#macro-limitations) /// as well. /// /// # Formating behavior /// /// ### Debug-like /// /// The `{:?}` formatter formats things similarly to how Debug does it. /// /// For `&'static str` it does these things: /// - Prepend and append the double quote character (`"`). /// - Escape the `'\t'`,`'\n'`,`'\r'`,`'\\'`, `'\''`, and`'\"'` characters. /// - Escape control characters with `\xYY`, /// where `YY` is the hexadecimal value of the control character. /// /// Example: /// ``` /// use const_format::formatcp; /// /// assert_eq!(formatcp!("{:?}", r#" \ " ó "#), r#"" \\ \" ó ""#); /// ``` /// /// For `char` it does these things: /// - Prepend and append the single quote character (`'`). /// - Uses the same escapes as `&'static str`. /// /// ### Display /// /// The `{}`/`{:}` formatter produces the same output as in [`format`]. /// /// /// # Examples /// /// ### Implicit argument /// /// ```rust /// use const_format::formatcp; /// /// const NAME: &str = "John"; /// /// const MSG: &str = formatcp!("Hello {NAME}, your name is {} bytes long", NAME.len()); /// /// assert_eq!(MSG, "Hello John, your name is 4 bytes long"); /// /// ``` /// /// ### Repeating arguments /// /// ```rust /// use const_format::formatcp; /// /// const MSG: &str = formatcp!("{0}{S}{0}{S}{0}", "SPAM", S = " "); /// /// assert_eq!(MSG, "SPAM SPAM SPAM"); /// /// ``` /// /// ### Debug-like and Display formatting /// /// ```rust /// use const_format::formatcp; /// /// { /// const TEXT: &str = r#"hello " \ world"#; /// const MSG: &str = formatcp!("{TEXT}____{TEXT:?}"); /// /// assert_eq!(MSG, r#"hello " \ world____"hello \" \\ world""#); /// } /// { /// const CHARS: &str = formatcp!("{0:?} - {0} - {1} - {1:?}", '"', '👀'); /// /// assert_eq!(CHARS, r#"'\"' - " - 👀 - '👀'"#); /// } /// ``` /// /// ### Additional specifiers /// /// `const_format` macros don't support width, fill, alignment, sign, /// or precision specifiers. /// /// [`format`]: https://doc.rust-lang.org/std/macro.format.html /// /// [`format_args_implicits` RFC]: /// https://github.com/rust-lang/rfcs/blob/master/text/2795-format-args-implicit-identifiers.md /// /// #[macro_export] macro_rules! formatcp { ($format_string:expr $( $(, $expr:expr )+ )? $(,)? ) => ( $crate::pmr::__AssertStr {x:{ use $crate::__cf_osRcTFl4A; $crate::pmr::__formatcp_impl!( ($format_string) $(, $($expr,)+)? ) }}.x ); } //////////////////////////////////////////////////////////////////////////////// /// Concatenates constants of standard library and/or user-defined types into a `&'static str`. /// /// User defined types must implement the [`FormatMarker`] trait and /// and have a `const_display_fmt` method (as described in the trait) to be concatenated. /// /// # Stable equivalent /// /// For an equivalent macro which can be used in stable Rust, /// but can only concatenate primitive types, /// you can use the [`concatcp`](crate::concatcp) macro. /// /// # Limitations /// /// This macro has [the limitations described in here](./index.html#macro-limitations). /// /// # Examples /// /// ### With standard library types /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::concatc; /// /// assert_eq!(concatc!("There is ", 99u8, " monkeys!"), "There is 99 monkeys!"); /// /// ``` /// /// ### With user-defined types /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Formatter, Sliced, concatc, impl_fmt}; /// /// const STRING: &str = "foo bar baz"; /// /// assert_eq!(concatc!(Sliced(STRING, 4..7), ' ', Foo), "bar table"); /// /// struct Foo; /// /// impl_fmt!{ /// impl Foo; /// const fn const_display_fmt(&self, fmt: &mut Formatter<'_>) -> const_format::Result { /// fmt.write_str("table") /// } /// } /// ``` /// /// /// [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! concatc { ()=>{""}; ($($anything:tt)*)=>( $crate::pmr::__AssertStr {x:{ use $crate::__cf_osRcTFl4A; $crate::__concatc_expr!(($($anything)*) ($($anything)*)) as &'static $crate::pmr::str }}.x ) } #[doc(hidden)] #[cfg(feature = "fmt")] #[macro_export] macro_rules! __concatc_expr { (($($arg: expr),* $(,)?) ($($span:tt)*) )=>({ const fn fmt_NHPMWYD3NJA( mut fmt: $crate::fmt::Formatter<'_>, ) -> $crate::Result { use $crate::coerce_to_fmt as __cf_coerce_to_fmt; use $crate::pmr::respan_to as __cf_respan_to; use $crate::try_ as __cf_try; $({ let __cf_respan_to!(($arg) fmt) = &mut fmt; __cf_respan_to!(($arg) __cf_try!(__cf_coerce_to_fmt!($arg).const_display_fmt(fmt)) ); })* $crate::pmr::Ok(()) } $crate::__concatc_inner!(fmt_NHPMWYD3NJA, true, $($span)*) }) } #[doc(hidden)] #[macro_export] macro_rules! __concatc_inner { ($debug_fmt_fn:ident, $cond:expr, $($span:tt)*) => {{ const fn len_nhpmwyd3nj() -> usize { if $cond { let mut strlen = __cf_osRcTFl4A::pmr::ComputeStrLength::new(); let fmt = strlen.make_formatter(__cf_osRcTFl4A::FormattingFlags::NEW); match $debug_fmt_fn(fmt) { __cf_osRcTFl4A::pmr::Ok(()) => strlen.len(), __cf_osRcTFl4A::pmr::Err(_) => 0, } } else { 0 } } const LEN_NHPMWYD3NJA: usize = len_nhpmwyd3nj(); const fn str_writer_nhpmwyd3nja( ) -> __cf_osRcTFl4A::msg::ErrorTupleAndStrWriter<[u8; LEN_NHPMWYD3NJA]> { let mut writer = __cf_osRcTFl4A::pmr::StrWriter::new([0; LEN_NHPMWYD3NJA]); let error = if $cond { $debug_fmt_fn(__cf_osRcTFl4A::pmr::Formatter::from_sw( &mut writer, __cf_osRcTFl4A::FormattingFlags::NEW, )) } else { __cf_osRcTFl4A::pmr::Ok(()) }; __cf_osRcTFl4A::msg::ErrorTupleAndStrWriter { error: __cf_osRcTFl4A::msg::ErrorTuple::new(error, &writer), writer, } } const STR_WRITER_NHPMWYD3NJA: &__cf_osRcTFl4A::msg::ErrorTupleAndStrWriter< [u8; LEN_NHPMWYD3NJA], > = &str_writer_nhpmwyd3nja(); const _: __cf_osRcTFl4A::msg::Ok = <<__cf_osRcTFl4A::msg::ErrorPicker< [(); STR_WRITER_NHPMWYD3NJA.error.error_variant], [(); STR_WRITER_NHPMWYD3NJA.error.capacity], > as __cf_osRcTFl4A::msg::ErrorAsType>::Type>::NEW; const STR_NHPMWYD3NJA: &str = STR_WRITER_NHPMWYD3NJA.writer.unsize().as_str_alt(); STR_NHPMWYD3NJA }}; } //////////////////////////////////////////////////////////////////////////////// /// Formats constants of standard library and/or user-defined types into a `&'static str`. /// /// User-defined types must implement the [`FormatMarker`] trait /// (as described in the docs for that trait) to be usable with this macro. /// /// # Stable equivalent /// /// For an equivalent macro which can be used in stable Rust, /// but can only format primitive types, /// you can use the [`formatcp`](crate::formatcp) macro. /// /// # Syntax /// /// This macro uses the syntax described in /// [the const_format::fmt module](./fmt/index.html#fmtsyntax) /// /// # Limitations /// /// This macro has [the limitations described in here](./index.html#macro-limitations). /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::for_examples::Point3; /// use const_format::formatc; /// /// // Formatting a non-std struct. /// const POINT: &str = formatc!("{:?}", Point3{x: 8, y: 13, z: 21}); /// /// // Formatting a number as decimal, hexadecimal, and binary /// const NUMBER: &str = formatc!("{0},{0:x},{0:b}", 10u8); /// /// // Formatting the numbers in an array as decimal, hexadecimal, and binary. /// // You can use the name of cnstants from scope, as well as named arguments. /// const ARR: &[u32] = &[9, 25]; /// const ARRAY: &str = formatc!("{ARR:?},{ARR:X},{ARR:b}"); /// /// /// assert_eq!(POINT, "Point3 { x: 8, y: 13, z: 21 }"); /// assert_eq!(NUMBER, "10,a,1010"); /// assert_eq!(ARRAY, "[9, 25],[9, 19],[1001, 11001]"); /// /// ``` /// /// ### Custom formatting. /// /// This example demonstrates how you can access the [`Formatter`] in arguments /// to do custom formatting. /// /// For more details on this you can look /// [in the fmt module](./fmt/index.html#custom-formatting-section). /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::for_examples::Point3; /// use const_format::{formatc, try_}; /// /// const P: Point3 = Point3{x: 5, y: 13, z: 21}; /// /// const STR: &str = formatc!("{0};{0:#X};{0:#b}", |fmt|{ /// try_!(fmt.write_u32_debug(P.x)); /// try_!(fmt.write_str(" ")); /// try_!(fmt.write_u32_debug(P.y)); /// try_!(fmt.write_char('.')); /// }); /// /// assert_eq!(STR, "5 13.;0x5 0xD.;0b101 0b1101."); /// /// ``` /// [`Formatter`]: crate::fmt::Formatter /// [`FormatMarker`]: crate::marker_traits::FormatMarker /// /// #[macro_export] #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] macro_rules! formatc { ($format_string:expr $( $(, $expr:expr )+ )? $(,)? ) => ( $crate::pmr::__AssertStr {x:{ use $crate::__cf_osRcTFl4A; $crate::pmr::__formatc_impl!{ ($format_string) $(, $($expr,)+)? } }}.x ); } /// Writes some formatted standard library and/or user-defined types into a buffer. /// /// This macro evaluates to a `Result<(), const_format::Error>` which must be handled. /// /// # Syntax /// /// The syntax is similar to that of other formatting macros in this crate: /// /// ```ìgnore /// ẁritec!( /// writer_expression, /// "formatting literal", /// positional_arg_0_expression, /// positional_arg_1_expression, /// named_arg_foo = expression, /// named_arg_bar = expression, /// ) /// ``` /// /// The syntax is otherwise the same as described in /// [the `const_format::fmt` module](./fmt/index.html#fmtsyntax). /// /// # Writers /// /// The first argument must be a type that implements the [`WriteMarker`] trait, /// and has these inherent methods: /// ```ignore /// const fn borrow_mutably(&mut self) -> &mut Self /// const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> /// ``` /// /// [This example](#custom-writable-example) below shows how to use this macro /// with a custom type. /// /// # Limitations /// /// Integer arguments must have a type inferrable from context, /// [more details in the Integer arguments section](./index.html#integer-args). /// /// # Examples /// /// ### Ẁriting a Display impl. /// /// ``` /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, StrWriter}; /// use const_format::{impl_fmt, try_, writec}; /// /// pub struct Foo(u32, &'static str); /// /// impl_fmt!{ /// impl Foo; /// pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// try_!(writec!(f, "{},", self.0)); /// try_!(writec!(f, "{:?};", self.1)); /// Ok(()) /// } /// } /// /// // Coerces the `&mut StrWriter<[u8; 128]>` to `&mut StrWriter<[u8]>`. /// // This is necessary because the `as_str` method is defined for `StrWriter<[u8]>`. /// let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); /// writec!(writer, "{}", Foo(100, "bar"))?; /// /// assert_eq!(writer.as_str(), r#"100,"bar";"#); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// /// ### Writing to a custom type /// /// This example demonstrates how you can use the `ẁritec` macro with a custom type, /// in this case it's a buffer that is cleared every time it's written. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::marker_traits::{IsNotAStrWriter, WriteMarker}; /// use const_format::{Formatter, FormattingFlags}; /// use const_format::writec; /// /// const ARRAY_CAP: usize = 20; /// struct Array { /// len: usize, /// arr: [u8; ARRAY_CAP], /// } /// /// impl WriteMarker for Array{ /// type Kind = IsNotAStrWriter; /// type This = Self; /// } /// /// impl Array { /// // Gets the part of the array that has been written to. /// pub const fn as_bytes(&self) -> &[u8] { /// const_format::utils::slice_up_to_len_alt(&self.arr, self.len) /// } /// /// pub const fn borrow_mutably(&mut self) -> &mut Self { /// self /// } /// /// pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { /// Formatter::from_custom_cleared(&mut self.arr, &mut self.len, flags) /// } /// } /// /// /// let mut buffer = Array{ arr: [0; ARRAY_CAP], len: 0 }; /// /// writec!(buffer, "{:?}", [3u8, 5, 8, 13, 21])?; /// assert_eq!(buffer.as_bytes(), b"[3, 5, 8, 13, 21]"); /// /// writec!(buffer, "{}{}", "Hello, world!", 100u16)?; /// assert_eq!(buffer.as_bytes(), b"Hello, world!100"); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// ### Custom formatting. /// /// This example demonstrates how you can access the [`Formatter`] in arguments /// to do custom formatting. /// /// Note that `return` inside arguments returns from the function around the `writec`. /// /// For more details on this you can look /// [in the fmt module](./fmt/index.html#custom-formatting-section). /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::for_examples::Point3; /// use const_format::{StrWriter, call_debug_fmt, try_, writec}; /// /// const P: Point3 = Point3{x: 5, y: 13, z: 21}; /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); /// /// writec!( /// writer, /// "The options are: {}, and {}", /// |fmt| call_debug_fmt!(Option, Some(P), fmt), /// |fmt| call_debug_fmt!(Option, None::, fmt), /// )?; /// /// assert_eq!(writer.as_str(), "The options are: Some(Point3 { x: 5, y: 13, z: 21 }), and None"); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// ### Locals in the format string /// /// This example demonstrates how you can format local variables, /// by using their identifiers in the format string. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Formatter, FormattingFlags, StrWriter, try_, writec}; /// /// const fn writeit(mut fmt: Formatter<'_>, foo: u32, bar: &str) -> const_format::Result { /// try_!(writec!(fmt, "{foo},{foo:?},{foo:#x},{foo:#b};")); /// try_!(writec!(fmt, "{bar},{bar:?}")); /// Ok(()) /// } /// /// let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); /// /// writeit(writer.make_formatter(FormattingFlags::NEW), 100, "hello")?; /// /// assert_eq!(writer.as_str(), r#"100,100,0x64,0b1100100;hello,"hello""#); /// /// # Ok::<(), const_format::Error>(()) /// ``` /// /// [`Formatter`]: ./fmt/struct.Formatter.html /// [`WriteMarker`]: ./marker_traits/trait.WriteMarker.html /// /// /// /// #[macro_export] #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] macro_rules! writec { ( $writer:expr, $format_string:expr $( $(, $expr:expr )+ )? $(,)? ) => ({ use $crate::__cf_osRcTFl4A; $crate::pmr::__writec_impl!{ ($writer) ($format_string) $(, $($expr,)+)? } }); } const_format-0.2.32/src/macros/helper_macros.rs000064400000000000000000000074351046102023000176630ustar 00000000000000#[doc(hidden)] #[macro_export] macro_rules! __for_range{ ( $var:ident in $range:expr => $($for_body:tt)* )=>({ let $crate::pmr::Range{start: mut $var, end} = $range; while $var < end { {$($for_body)*} $var+=1; } }) } macro_rules! identity { ($($tt:tt)*) => { $($tt)* }; } #[doc(hidden)] #[macro_export] macro_rules! iter_copy_slice{ ( $var:ident in $array:expr => $($for_body:tt)* )=>({ let mut array: &[_] = &$array; while let [$var, ref rem @ ..] = *array { {$($for_body)*} array = rem; } }) } #[doc(hidden)] #[macro_export] macro_rules! __write_pvariant { (char, $parg:expr, $elem:ident => $out:ident) => {{ let encoded = $elem.encoded(); let len = $elem.len(); let mut start = 0; while start < len { $out.array[$out.len] = encoded[start]; $out.len += 1; start += 1; } }}; (int, $parg:expr, $elem:ident => $out:ident) => {{ let wrapper = $crate::pmr::PWrapper($elem); let debug_display; let bin; let hex; let sa: &$crate::pmr::StartAndArray<[_]> = match $parg.fmt { $crate::pmr::Formatting::Display => { debug_display = wrapper.to_start_array_display(); &debug_display } $crate::pmr::Formatting::Debug => match $parg.fmt_flags.num_fmt() { $crate::pmr::NumberFormatting::Decimal => { debug_display = wrapper.to_start_array_debug(); &debug_display } $crate::pmr::NumberFormatting::Binary => { bin = wrapper.to_start_array_binary($parg.fmt_flags); &bin } $crate::pmr::NumberFormatting::Hexadecimal => { hex = wrapper.to_start_array_hexadecimal($parg.fmt_flags); &hex } }, }; let mut start = sa.start; while start < sa.array.len() { $out.array[$out.len] = sa.array[start]; $out.len += 1; start += 1; } }}; (str, $parg:expr, $elem:ident => $out:ident) => {{ let str = $elem.as_bytes(); let is_display = $parg.fmt.is_display(); let mut i = 0; if is_display { while i < str.len() { $out.array[$out.len] = str[i]; $out.len += 1; i += 1; } } else { $out.array[$out.len] = b'"'; $out.len += 1; while i < str.len() { use $crate::pmr::{hex_as_ascii, ForEscaping, FOR_ESCAPING}; let c = str[i]; let mut written_c = c; if c < 128 { let shifted = 1 << c; if (FOR_ESCAPING.is_escaped & shifted) != 0 { $out.array[$out.len] = b'\\'; $out.len += 1; if (FOR_ESCAPING.is_backslash_escaped & shifted) == 0 { $out.array[$out.len] = b'x'; $out.array[$out.len + 1] = hex_as_ascii(c >> 4, $crate::pmr::HexFormatting::Upper); $out.len += 2; written_c = hex_as_ascii(c & 0b1111, $crate::pmr::HexFormatting::Upper); } else { written_c = ForEscaping::get_backslash_escape(c); }; } } $out.array[$out.len] = written_c; $out.len += 1; i += 1; } $out.array[$out.len] = b'"'; $out.len += 1; } }}; } const_format-0.2.32/src/macros/impl_fmt.rs000064400000000000000000000203061046102023000166370ustar 00000000000000/// For implementing debug or display formatting "manually". /// /// # Generated code /// /// This macro generates: /// /// - An implementation of the [`FormatMarker`] trait for all the `impl`d types, /// /// - All the listed impls, by repeating the methods (and other associated items) /// passed to this macro in each of the impls. /// /// # Example /// /// ### Generic type /// /// This demonstrates how you can implement debug formatting for a generic struct. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, PWrapper, StrWriter}; /// use const_format::{formatc, impl_fmt, try_}; /// /// use std::marker::PhantomData; /// /// pub struct Tupled(u32, T); /// /// // Implements debug formatting for: /// // - Tupled> /// // - Tupled /// // - Tupled> /// // Repeating the `const_debug_fmt` function definition in each of those 3 impls. /// impl_fmt!{ /// // The trailing comma is required /// impl[T,] Tupled> /// where[ T: 'static ]; /// /// impl[] Tupled; /// impl Tupled>; /// /// pub const fn const_debug_fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { /// let mut fmt = fmt.debug_tuple("Tupled"); /// /// // PWrapper implements const_debug_fmt methods for many std types. /// // /// // You can use `call_debug_fmt` for formatting generic std types /// // if this doesn't work /// try_!(PWrapper(self.0).const_debug_fmt(fmt.field())); /// try_!(PWrapper(self.1).const_debug_fmt(fmt.field())); /// /// fmt.finish() /// } /// } /// /// const S_PHANTOM: &str = formatc!("{:?}", Tupled(3, PhantomData::)); /// const S_BOOL: &str = formatc!("{:?}", Tupled(5, false)); /// const S_OPTION: &str = formatc!("{:?}", Tupled(8, Some(true))); /// /// assert_eq!(S_PHANTOM, "Tupled(3, PhantomData)"); /// assert_eq!(S_BOOL, "Tupled(5, false)"); /// assert_eq!(S_OPTION, "Tupled(8, Some(true))"); /// /// /// ``` /// /// ### Enum /// /// This demonstrates how you can implement debug formatting for an enum, /// using this macro purely for implementing the [`FormatMarker`] trait. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, PWrapper, StrWriter}; /// use const_format::{formatc, impl_fmt, try_}; /// /// use std::cmp::Ordering; /// /// pub enum Enum { /// Braced{ord: Ordering}, /// Tupled(u32, u32), /// Unit, /// } /// /// impl_fmt!{ /// impl Enum; /// /// pub const fn const_debug_fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { /// match self { /// Self::Braced{ord} => { /// let mut fmt = fmt.debug_struct("Braced"); /// /// // PWrapper implements const_debug_fmt methods for many std types. /// // /// // You can use `call_debug_fmt` for formatting generic std types /// // if this doesn't work /// try_!(PWrapper(*ord).const_debug_fmt(fmt.field("ord"))); /// /// fmt.finish() /// } /// Self::Tupled(f0,f1) => { /// let mut fmt = fmt.debug_tuple("Tupled"); /// /// try_!(PWrapper(*f0).const_debug_fmt(fmt.field())); /// try_!(PWrapper(*f1).const_debug_fmt(fmt.field())); /// /// fmt.finish() /// } /// Self::Unit => { /// fmt.debug_tuple("Unit").finish() /// } /// } /// } /// } /// /// const S_BRACED: &str = formatc!("{:?}", Enum::Braced{ord: Ordering::Greater}); /// const S_TUPLED: &str = formatc!("{:?}", Enum::Tupled(5, 8)); /// const S_UNIT: &str = formatc!("{:?}", Enum::Unit); /// /// assert_eq!(S_BRACED, "Braced { ord: Greater }"); /// assert_eq!(S_TUPLED, "Tupled(5, 8)"); /// assert_eq!(S_UNIT, "Unit"); /// /// ``` /// /// [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[macro_export] macro_rules! impl_fmt { ( is_std_type; $($rem:tt)* ) => ( $crate::__impl_fmt_recursive!{ impls[ is_std_type = true; ] tokens[$($rem)*] } ); ( $($rem:tt)* ) => ( $crate::__impl_fmt_recursive!{ impls[ is_std_type = false; ] tokens[$($rem)*] } ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_fmt_recursive{ ( impls[$($impls:tt)*] tokens[ $(#[$impl_attr:meta])* impl[$($impl_:tt)*] $type:ty $(where[ $($where:tt)* ])?; $($rem:tt)* ] ) => ( $crate::__impl_fmt_recursive!{ impls[ $($impls)* ( $(#[$impl_attr])* #[allow(unused_mut)] impl[$($impl_)*] $type where[ $($($where)*)? ]; ) ] tokens[ $($rem)* ] } ); // The same as the above macro branch, but it doesn't require the `[]` in `impl[]` ( impls[$($impls:tt)*] tokens[ $(#[$impl_attr:meta])* impl $type:ty $(where[ $($where:tt)* ])?; $($rem:tt)* ] ) => ( $crate::__impl_fmt_recursive!{ impls[ $($impls)* ( $(#[$impl_attr])* #[allow(unused_mut)] impl[] $type where[ $($($where)*)? ]; ) ] tokens[ $($rem)* ] } ); ( impls $impls:tt tokens[ $($rem:tt)* ] ) => ( $crate::__impl_fmt_inner!{ @all_impls impls $impls ($($rem)*) } ); } #[doc(hidden)] #[macro_export] macro_rules! __impl_fmt_inner { (@all_impls impls [ is_std_type = $is_std_type:ident; $( $an_impl:tt )+ ] $stuff:tt )=>{ $( $crate::__impl_fmt_inner!{ @impl_get_type_kind is_std_type = $is_std_type; $an_impl } $crate::__impl_fmt_inner!{ @an_impl is_std_type = $is_std_type; $an_impl $stuff } )+ }; (@impl_get_type_kind is_std_type = true; ( $(#[$impl_attr:meta])* impl[$($impl_:tt)*] $type:ty where[ $($where:tt)* ]; ) )=>{ $(#[$impl_attr])* impl<$($impl_)*> $crate::pmr::FormatMarker for $type where $($where)* { type Kind = $crate::pmr::IsStdKind; type This = Self; } $(#[$impl_attr])* impl<$($impl_)* __T> $crate::pmr::IsAFormatMarker where $($where)* { #[inline(always)] pub const fn coerce(self, reference: &$type) -> PWrapper<$type> { PWrapper(*reference) } } }; (@impl_get_type_kind is_std_type = false; ( $(#[$impl_attr:meta])* impl[$($impl_:tt)*] $type:ty where[ $($where:tt)* ]; ) )=>{ $(#[$impl_attr])* impl<$($impl_)*> $crate::pmr::FormatMarker for $type where $($where)* { type Kind = $crate::pmr::IsNotStdKind; type This = Self; } }; (@an_impl is_std_type = $is_std_type:ident; ( $(#[$impl_attr:meta])* impl[$($impl_:tt)*] $type:ty where[ $($where:tt)* ]; ) ( $($everything:tt)* ) )=>{ $(#[$impl_attr])* impl<$($impl_)*> $crate::__impl_fmt_inner!(@self_ty $type, $is_std_type ) where $($where)* { $($everything)* } }; (@self_ty $self:ty, /*is_std_type*/ true )=>{ $crate::pmr::PWrapper<$self> }; (@self_ty $self:ty, /*is_std_type*/ false )=>{ $self }; } const_format-0.2.32/src/macros/map_ascii_case.rs000064400000000000000000000055331046102023000177550ustar 00000000000000/// Converts the casing style of a `&'static str` constant, /// ignoring non-ascii unicode characters. /// /// This nacro is equivalent to a function with this signature: /// /// ```rust /// const fn map_ascii_case(case: const_format::Case, input: &'static str) -> &'static str /// # {""} /// ``` /// /// The [`Case`](enum.Case.html) parameter determines the casing style of the returned string. /// /// # Ascii /// /// This only transforms ascii characters because broader unicode case conversion, /// while possible, is much harder to implement purely with `const fn`s. /// /// Non-ascii characters are treated as though they're alphabetic ascii characters. /// /// # Ignored characters /// /// These casing styles treat non-alphanumeric ascii characters as spaces, /// removing them from the returned string: /// /// - `Case::Pascal` /// - `Case::Camel` /// - `Case::Snake` /// - `Case::UpperSnake` /// - `Case::Kebab` /// - `Case::UpperKebab` /// /// # Example /// /// ```rust /// use const_format::{Case, map_ascii_case}; /// /// { /// const LOW: &str = map_ascii_case!(Case::Lower, "hello WORLD"); /// assert_eq!(LOW, "hello world"); /// } /// { /// const IN: &str = "hello WORLD каждому"; /// const OUT: &str = map_ascii_case!(Case::Upper, IN); /// // non-ascii characters are ignored by map_ascii_case. /// assert_eq!(OUT, "HELLO WORLD каждому"); /// } /// /// const IN2: &str = "hello fooкаждому100Bar#qux"; /// { /// const OUT: &str = map_ascii_case!(Case::Pascal, IN2); /// assert_eq!(OUT, "HelloFooкаждому100BarQux"); /// } /// { /// const OUT: &str = map_ascii_case!(Case::Camel, IN2); /// assert_eq!(OUT, "helloFooкаждому100BarQux"); /// } /// { /// const OUT: &str = map_ascii_case!(Case::Snake, IN2); /// assert_eq!(OUT, "hello_fooкаждому_100_bar_qux"); /// } /// { /// const OUT: &str = map_ascii_case!(Case::UpperSnake, IN2); /// assert_eq!(OUT, "HELLO_FOOкаждому_100_BAR_QUX"); /// } /// { /// const OUT: &str = map_ascii_case!(Case::Kebab, IN2); /// assert_eq!(OUT, "hello-fooкаждому-100-bar-qux"); /// } /// { /// const OUT: &str = map_ascii_case!(Case::UpperKebab, IN2); /// assert_eq!(OUT, "HELLO-FOOкаждому-100-BAR-QUX"); /// } /// /// /// ``` #[macro_export] macro_rules! map_ascii_case { ($case:expr, $str:expr) => {{ const S_OSRCTFL4A: &$crate::pmr::str = $str; const CASE_OSRCTFL4A: $crate::Case = $case; { const L: $crate::pmr::usize = $crate::__ascii_case_conv::size_after_conversion(CASE_OSRCTFL4A, S_OSRCTFL4A); const OB: &[$crate::pmr::u8; L] = &$crate::__ascii_case_conv::convert_str::(CASE_OSRCTFL4A, S_OSRCTFL4A); const OS: &$crate::pmr::str = unsafe { $crate::__priv_transmute_bytes_to_str!(OB) }; OS } }}; } const_format-0.2.32/src/macros/str_methods.rs000064400000000000000000000370671046102023000173770ustar 00000000000000/// Replaces all the instances of `$pattern` in `$input` /// (a `&'static str` constant) with `$replace_with` (a `&'static str` constant). /// /// # Signature /// /// This macro acts like a function of this signature: /// ```rust /// # trait Pattern {} /// fn str_replace( /// input: &'static str, /// pattern: impl Pattern, /// replace_with: &'static str, /// ) -> &'static str /// # {""} /// ``` /// and is evaluated at compile-time. /// /// Where `pattern` can be any of these types: /// /// - `&'static str` /// /// - `char` /// /// - `u8`: required to be ascii (`0` up to `127` inclusive). /// /// # Example /// /// /// ```rust /// use const_format::str_replace; /// /// // Passing a string pattern /// assert_eq!( /// str_replace!("The incredible shrinking man.", "i", "eee"), /// "The eeencredeeeble shreeenkeeeng man.", /// ); /// /// // Passing a char pattern /// assert_eq!( /// str_replace!("The incredible shrinking man.", ' ', "---"), /// "The---incredible---shrinking---man.", /// ); /// /// // Passing an ascii u8 pattern. /// assert_eq!( /// str_replace!("The incredible shrinking man.", b'i', "eee"), /// "The eeencredeeeble shreeenkeeeng man.", /// ); /// /// // Removing all instances of the pattern /// assert_eq!( /// str_replace!("remove haire", "re", ""), /// "move hai", /// ); /// /// // This shows that all the arguments can be `const`s, they don't have to be literals. /// { /// const IN: &str = "Foo Boo Patoo"; /// const REPLACING: &str = "oo"; /// const REPLACE_WITH: &str = "uh"; /// assert_eq!(str_replace!(IN, REPLACING, REPLACE_WITH), "Fuh Buh Patuh"); /// } /// ``` /// /// [`str::replace`]: https://doc.rust-lang.org/std/primitive.str.html#method.replace #[macro_export] macro_rules! str_replace { ($input:expr, $pattern:expr, $replace_with:expr $(,)*) => {{ const ARGS_OSRCTFL4A: $crate::__str_methods::ReplaceInput = $crate::__str_methods::ReplaceInputConv($input, $pattern, $replace_with).conv(); { const OB: &[$crate::pmr::u8; ARGS_OSRCTFL4A.replace_length()] = &ARGS_OSRCTFL4A.replace(); const OS: &$crate::pmr::str = unsafe { $crate::__priv_transmute_bytes_to_str!(OB) }; OS } }}; } /// Creates a `&'static str` by repeating a `&'static str` constant `times` times /// /// This is evaluated at compile-time. /// /// # Example /// /// ```rust /// use const_format::str_repeat; /// /// { /// const OUT: &str = str_repeat!("hi ", 4); /// assert_eq!(OUT, "hi hi hi hi ") /// } /// { /// const IN: &str = "bye "; /// const REPEAT: usize = 5; /// const OUT: &str = str_repeat!(IN, REPEAT); /// assert_eq!(OUT, "bye bye bye bye bye ") /// } /// /// ``` /// /// ### Failing /// /// If this macro would produce too large a string, /// it causes a compile-time error. /// /// ```compile_fail /// const_format::str_repeat!("hello", usize::MAX / 4); /// ``` /// #[cfg_attr( feature = "__test", doc = r##" ```rust const_format::str_repeat!("hello", usize::MAX.wrapping_add(4)); ``` "## )] #[macro_export] macro_rules! str_repeat { ($string:expr, $times:expr $(,)*) => {{ const P_OSRCTFL4A: &$crate::__str_methods::StrRepeatArgs = &$crate::__str_methods::StrRepeatArgs($string, $times); { use $crate::__hidden_utils::PtrToRef; use $crate::pmr::{str, transmute, u8}; const P: &$crate::__str_methods::StrRepeatArgs = P_OSRCTFL4A; $crate::pmr::respan_to! { ($string) const _ASSERT_VALID_LEN: () = P.assert_valid(); } const OUT_B: &[u8; P.out_len] = &unsafe { let ptr = P.str.as_ptr() as *const [u8; P.str_len]; transmute::<[[u8; P.str_len]; P.repeat], [u8; P.out_len]>( [*PtrToRef { ptr }.reff; P.repeat], ) }; const OUT_S: &str = unsafe { $crate::__priv_transmute_bytes_to_str!(OUT_B) }; OUT_S } }}; } /// Replaces a substring in a `&'static str` constant. /// Returns both the new resulting `&'static str`, and the replaced substring. /// /// # Signature /// /// This macro acts like a function of this signature: /// ```rust /// # trait SomeIndex {} /// fn str_splice( /// input: &'static str, /// range: impl SomeIndex, /// replace_with: &'static str, /// ) -> const_format::SplicedStr /// # {unimplemented!()} /// ``` /// and is evaluated at compile-time. /// /// ### `range` argument /// /// The `range` parameter determines what part of `input` is replaced, /// and can be any of these types: /// /// - `usize`: the starting index of a char, only includes that char. /// - `Range` /// - `RangeTo` /// - `RangeFrom` /// - `RangeInclusive` /// - `RangeToInclusive` /// - `RangeFull` /// /// [`SplicedStr`] contains: /// - `output`: a `&'static str` with the substring at `range` in `input` replaced with /// `replace_with`. /// - `removed`: the substring at `range` in `input`. /// /// # Example /// /// ```rust /// use const_format::{str_splice, SplicedStr}; /// /// const OUT: SplicedStr = str_splice!("foo bar baz", 4..=6, "is"); /// assert_eq!(OUT , SplicedStr{output: "foo is baz", removed: "bar"}); /// /// // You can pass `const`ants to this macro, not just literals /// { /// const IN: &str = "this is bad"; /// const INDEX: std::ops::RangeFrom = 8..; /// const REPLACE_WITH: &str = "... fine"; /// const OUT: SplicedStr = str_splice!(IN, INDEX, REPLACE_WITH); /// assert_eq!(OUT , SplicedStr{output: "this is ... fine", removed: "bad"}); /// } /// { /// const OUT: SplicedStr = str_splice!("ABC豆-", 3, "DEFGH"); /// assert_eq!(OUT , SplicedStr{output: "ABCDEFGH-", removed: "豆"}); /// } /// ``` /// /// ### Invalid index /// /// Invalid indices cause compilation errors. /// /// ```compile_fail /// const_format::str_splice!("foo", 0..10, ""); /// ``` #[cfg_attr( feature = "__test", doc = r#" ```rust const_format::str_splice!("foo", 0..3, ""); ``` ```compile_fail const_format::str_splice!("foo", 0..usize::MAX, ""); ``` ```rust assert_eq!( const_format::str_splice!("効率的", 3..6, "A"), const_format::SplicedStr{output: "効A的", removed: "率"} , ); ``` ```compile_fail assert_eq!( const_format::str_splice!("効率的", 1..6, "A"), const_format::SplicedStr{output: "効A的", removed: "率"} , ); ``` ```compile_fail assert_eq!( const_format::str_splice!("効率的", 3..5, "A"), const_format::SplicedStr{output: "効A的", removed: "率"} , ); ``` "# )] /// /// /// [`SplicedStr`]: ./struct.SplicedStr.html #[macro_export] macro_rules! str_splice { ($string:expr, $index:expr, $insert:expr $(,)*) => {{ const P_OSRCTFL4A: $crate::__str_methods::StrSpliceArgs = $crate::__str_methods::StrSplceArgsConv($string, $index, $insert).conv(); { use $crate::__hidden_utils::PtrToRef; use $crate::__str_methods::{DecomposedString, SplicedStr, StrSpliceArgs}; use $crate::pmr::{str, u8}; const P: &StrSpliceArgs = &P_OSRCTFL4A; type DecompIn = DecomposedString<[u8; P.used_rstart], [u8; P.used_rlen], [u8; P.suffix_len]>; type DecompOut = DecomposedString<[u8; P.used_rstart], [u8; P.insert_len], [u8; P.suffix_len]>; $crate::pmr::respan_to! { ($string) const _ASSERT_VALID_INDEX: () = P.index_validity.assert_valid(); } const OUT_A: (&DecompOut, &str) = unsafe { let input = PtrToRef { ptr: P.str.as_ptr() as *const DecompIn, } .reff; let insert = PtrToRef { ptr: P.insert.as_ptr() as *const [u8; P.insert_len], } .reff; ( &DecomposedString { prefix: input.prefix, middle: *insert, suffix: input.suffix, }, $crate::__priv_transmute_bytes_to_str!(&input.middle), ) }; const OUT: SplicedStr = unsafe { let output = OUT_A.0 as *const DecompOut as *const [u8; P.out_len]; SplicedStr { output: $crate::__priv_transmute_raw_bytes_to_str!(output), removed: OUT_A.1, } }; OUT } }}; } /// Indexes a `&'static str` constant. /// /// /// # Signature /// /// This macro acts like a function of this signature: /// ```rust /// # trait SomeIndex {} /// fn str_index(input: &'static str, range: impl SomeIndex) -> &'static str /// # {unimplemented!()} /// ``` /// and is evaluated at compile-time. /// /// This accepts /// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument) /// /// # Example /// /// ``` /// use const_format::str_index; /// /// use std::ops::RangeFrom; /// /// assert_eq!(str_index!("foo bar baz", ..7), "foo bar"); /// assert_eq!(str_index!("foo bar baz", 4..7), "bar"); /// assert_eq!(str_index!("foo bar baz", 4..), "bar baz"); /// /// { /// const IN: &str = "hello world"; /// const INDEX: RangeFrom = 6..; /// // You can pass `const`ants to this macro, not just literals /// const OUT_0: &str = str_index!(IN, INDEX); /// assert_eq!(OUT_0, "world"); /// } /// { /// const OUT: &str = str_index!("hello world", 4); /// assert_eq!(OUT, "o"); /// } /// /// ``` /// /// ### Invalid index /// /// Invalid indices cause compilation errors. /// /// ```compile_fail /// const_format::str_index!("foo", 0..10); /// ``` #[cfg_attr( feature = "__test", doc = r#" ```rust assert_eq!(const_format::str_index!("効率的", 3..6), "率"); ``` ```compile_fail assert_eq!(const_format::str_index!("効率的", 3..5), "率"); ``` ```compile_fail assert_eq!(const_format::str_index!("効率的", 4..6), "率"); ``` "# )] /// /// #[macro_export] macro_rules! str_index { ($string:expr, $index:expr $(,)*) => {{ const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs = $crate::__str_methods::StrIndexArgsConv($string, $index).conv(); { $crate::pmr::respan_to! { ($string) const _ASSERT_VALID_INDEX: () = P_OSRCTFL4A.index_validity.assert_valid(); } use $crate::__hidden_utils::PtrToRef; use $crate::__str_methods::DecomposedString; type DecompIn = DecomposedString< [u8; P_OSRCTFL4A.used_rstart], [u8; P_OSRCTFL4A.used_rlen], [u8; 0], >; const OUT: &'static str = unsafe { let input = PtrToRef { ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn, } .reff; $crate::__priv_transmute_raw_bytes_to_str!(&input.middle) }; OUT } }}; } /// Indexes a `&'static str` constant, /// returning `None` when the index is not on a character boundary. /// /// /// # Signature /// /// This macro acts like a function of this signature: /// ```rust /// # trait SomeIndex {} /// fn str_get(input: &'static str, range: impl SomeIndex) -> Option<&'static str> /// # {unimplemented!()} /// ``` /// and is evaluated at compile-time. /// /// This accepts /// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument) /// /// # Example /// /// ``` /// use const_format::str_get; /// /// use std::ops::RangeFrom; /// /// assert_eq!(str_get!("foo 鉄 baz", ..7), Some("foo 鉄")); /// assert_eq!(str_get!("foo 鉄 baz", 4..7), Some("鉄")); /// assert_eq!(str_get!("foo 鉄 baz", 4..100), None); /// /// /// { /// const IN: &str = "hello 鉄"; /// const INDEX: RangeFrom = 6..; /// // You can pass `const`ants to this macro, not just literals /// const OUT: Option<&str> = str_get!(IN, INDEX); /// assert_eq!(OUT, Some("鉄")); /// } /// { /// const OUT: Option<&str> = str_get!("hello 鉄", 4); /// assert_eq!(OUT, Some("o")); /// } /// { /// // End index not on a character boundary /// const OUT: Option<&str> = str_get!("hello 鉄", 0..7); /// assert_eq!(OUT, None); /// } /// { /// // Out of bounds indexing /// const OUT: Option<&str> = str_get!("hello 鉄", 0..1000 ); /// assert_eq!(OUT, None); /// } /// /// ``` #[cfg_attr( feature = "__test", doc = r#" ```rust assert_eq!(const_format::str_get!("効率的", 3..6), Some("率")); assert_eq!(const_format::str_get!("効率的", 3..5), None); assert_eq!(const_format::str_get!("効率的", 4..6), None); ``` "# )] /// #[macro_export] macro_rules! str_get { ($string:expr, $index:expr $(,)*) => {{ const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs = $crate::__str_methods::StrIndexArgsConv($string, $index).conv(); { use $crate::__hidden_utils::PtrToRef; use $crate::__str_methods::DecomposedString; type DecompIn = DecomposedString< [u8; P_OSRCTFL4A.used_rstart], [u8; P_OSRCTFL4A.used_rlen], [u8; 0], >; const OUT: $crate::pmr::Option<&'static $crate::pmr::str> = unsafe { if P_OSRCTFL4A.index_validity.is_valid() { let input = PtrToRef { ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn, } .reff; $crate::pmr::Some($crate::__priv_transmute_raw_bytes_to_str!(&input.middle)) } else { $crate::pmr::None } }; OUT } }}; } /// Splits `$string` (a `&'static str` constant) with `$splitter`, /// returning an array of `&'static str`s. /// /// # Signature /// /// This macro acts like a function of this signature: /// ```rust /// # const LEN: usize = 0; /// # trait Splitter {} /// fn str_split(string: &'static str, splitter: impl Splitter) -> [&'static str; LEN] /// # { [] } /// ``` /// and is evaluated at compile-time. /// /// `impl Splitter` is any of these types: /// /// - `&'static str` /// /// - `char` /// /// - `u8`: only ascii values (0 up to 127 inclusive) are allowed /// /// The value of `LEN` depends on the `string` and `splitter` arguments. /// /// /// # Example /// /// ```rust /// use const_format::str_split; /// /// assert_eq!(str_split!("this is nice", ' '), ["this", "is", "nice"]); /// /// assert_eq!(str_split!("Hello, world!", ", "), ["Hello", "world!"]); /// /// // A `""` splitter outputs all chars individually (`str::split` does the same) /// assert_eq!(str_split!("🧡BAR🧠", ""), ["", "🧡", "B", "A", "R", "🧠", ""]); /// /// // Splitting the string with an ascii byte /// assert_eq!(str_split!("dash-separated-string", b'-'), ["dash", "separated", "string"]); /// /// { /// const STR: &str = "foo bar baz"; /// const SPLITTER: &str = " "; /// /// // both arguments to the `str_aplit` macro can be non-literal constants /// const SPLIT: [&str; 3] = str_split!(STR, SPLITTER); /// /// assert_eq!(SPLIT, ["foo", "bar", "baz"]); /// } /// /// ``` #[macro_export] #[cfg(feature = "rust_1_64")] #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "rust_1_64")))] macro_rules! str_split { ($string:expr, $splitter:expr $(,)?) => {{ const ARGS_OSRCTFL4A: $crate::__str_methods::SplitInput = $crate::__str_methods::SplitInputConv($string, $splitter).conv(); { const OB: [&$crate::pmr::str; ARGS_OSRCTFL4A.length()] = ARGS_OSRCTFL4A.split_it(); OB } }}; } const_format-0.2.32/src/macros.rs000064400000000000000000000222301046102023000150260ustar 00000000000000#[macro_use] mod assertions; #[macro_use] #[cfg(feature = "fmt")] mod call_debug_fmt; #[macro_use] mod constructors; #[macro_use] mod helper_macros; #[macro_use] mod fmt_macros; #[macro_use] #[cfg(feature = "fmt")] mod impl_fmt; #[macro_use] mod map_ascii_case; #[macro_use] mod str_methods; /// For returning early on an error, otherwise evaluating to `()`. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{Error, StrWriter}; /// use const_format::{try_, writec}; /// /// const fn write_stuff(buffer: &mut StrWriter) -> Result<&[u8], Error> { /// try_!(writec!(buffer, "Foo{},Bar{},Baz{},", 8u32, 13u32, 21u32)); /// Ok(buffer.as_bytes_alt()) /// } /// /// let mut buffer = StrWriter::new([0; 32]); /// assert_eq!(write_stuff(&mut buffer)?, "Foo8,Bar13,Baz21,".as_bytes()); /// /// # Ok::<(), Error>(()) /// ``` #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! try_ { ($e:expr) => { if let $crate::pmr::Err(e) = $e { return $crate::pmr::Err(e); } }; } /// Equivalent to `Result::unwrap`, for use with [`const_format::Error`] errors. /// /// You can use this when you know for certain that no error will happen. /// /// [`const_format::Error`]: ./fmt/enum.Error.html /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{StrWriter, unwrap, writec}; /// /// const CAP: usize = 11; /// const TEXT: &str = { /// const S: &StrWriter = &{ /// let mut writer = StrWriter::new([0; CAP]); /// unwrap!(writec!(writer, "foo bar baz")); /// writer /// }; /// S.as_str_alt() /// }; /// assert_eq!(TEXT, "foo bar baz") /// /// ``` #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! unwrap { ($e:expr $(,)*) => { match $e { $crate::pmr::Ok(x) => x, $crate::pmr::Err(error) => $crate::Error::unwrap(&error), } }; } /// Equivalent to `Result::unwrap_or_else` but allows returning from the enclosing function. /// /// # Examples /// /// ### Early return /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::unwrap_or_else; /// /// const fn unwrap_square(number: Result) -> u64 { /// let n = unwrap_or_else!(number, |n| return n as u64 ) as u64; /// n * n /// } /// /// assert_eq!(unwrap_square(Ok(10)), 100); /// assert_eq!(unwrap_square(Ok(30)), 900); /// assert_eq!(unwrap_square(Err(100)), 100); /// /// ``` /// /// ### As unwrap_or /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{AsciiStr, unwrap_or_else}; /// /// const FOO: AsciiStr = unwrap_or_else!(AsciiStr::new(b"AB\x80"), |_| AsciiStr::empty() ); /// /// const BAR: AsciiStr = unwrap_or_else!(AsciiStr::new(b"bar"), |_| loop{} ); /// /// assert_eq!(FOO.as_str(), ""); /// assert_eq!(BAR.as_str(), "bar"); /// /// ``` #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! unwrap_or_else { ($e:expr, |$($error:ident)? $(_)*| $orelse:expr ) => { match $e { $crate::pmr::Ok(x) => x, $crate::pmr::Err($($error,)?..) => $orelse, } }; } /// Coerces a reference to a type that has a `const_*_fmt` method. /// /// # Behavior /// /// For arrays it coerces them into a slice, and wraps them in a [`PWrapper`]. /// /// For std types, it wraps them in a [`PWrapper`], which implements the /// `const_*_fmt` methods. /// /// For non-std types, it just returns back the same reference. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ /// for_examples::Unit, /// Formatter, FormattingFlags, PWrapper, StrWriter, /// coerce_to_fmt, /// }; /// /// const CAP: usize = 128; /// const fn make_strwriter() -> StrWriter<[u8; CAP]> { /// let mut writer = StrWriter::new([0; CAP]); /// let mut fmt = Formatter::from_sw(&mut writer, FormattingFlags::NEW); /// /// // This is equivalent to the `PWrapper::slice(&[0u8, 1])` below /// let _ = coerce_to_fmt!([0u8, 1]).const_debug_fmt(&mut fmt); /// let _ = fmt.write_str(","); /// /// let _ = PWrapper::slice(&[0u8, 1]).const_debug_fmt(&mut fmt); /// let _ = fmt.write_str(","); /// /// /// // This is equivalent to the `PWrapper(100u32)` line /// let _ = coerce_to_fmt!(100u32).const_debug_fmt(&mut fmt); /// let _ = fmt.write_str(","); /// /// let _ = PWrapper(100u32).const_debug_fmt(&mut fmt); /// let _ = fmt.write_str(","); /// /// /// // This is equivalent to the `Unit.const_debug_fmt(&mut fmt)` line /// let _ = coerce_to_fmt!(Unit).const_debug_fmt(&mut fmt); /// /// /// let _ = fmt.write_str(","); /// let _ = Unit.const_debug_fmt(&mut fmt); /// /// writer /// } /// /// const TEXT: &str = { /// const TEXT_: &StrWriter = &make_strwriter(); /// TEXT_.as_str_alt() /// }; /// /// assert_eq!(TEXT, "[0, 1],[0, 1],100,100,Unit,Unit"); /// /// ``` /// /// [`PWrapper`]: ./struct.PWrapper.html #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[macro_export] macro_rules! coerce_to_fmt { ($reference:expr) => {{ match $reference { ref reference => { let marker = $crate::pmr::IsAFormatMarker::NEW; if false { marker.infer_type(reference); } marker.coerce(marker.unreference(reference)) } } }}; } /// Converts a `&'static StrWriter` to a `&'static str`, in a `const`/`static` initializer. /// /// This is usable in `const` or `static` initializers, /// but not inside of `const fn`s. /// /// **Deprecated:** This macro is deprecated because /// the [`StrWriter::as_str_alt`](crate::StrWriter::as_str_alt) method /// allows converting a`&'static StrWriter` to a `&'static str`. /// /// # Runtime /// /// If the "rust_1_64" feature is disabled, /// this takes time proportional to `$expr.capacity() - $expr.len()`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::StrWriter; /// use const_format::{strwriter_as_str, unwrap, writec}; /// /// /// const CAP: usize = 128; /// /// const __STR: &StrWriter = &{ /// let mut writer = StrWriter::new([0; CAP]); /// /// // Writing the array with debug formatting, and the integers with hexadecimal formatting. /// unwrap!(writec!(writer, "{:x}", [3u32, 5, 8, 13, 21, 34])); /// /// writer /// }; /// /// const STR: &str = strwriter_as_str!(__STR); /// /// fn main() { /// assert_eq!(STR, "[3, 5, 8, d, 15, 22]"); /// } /// ``` /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] #[deprecated(since = "0.2.19", note = "Use `StrWriter::as_str_alt` instead")] #[macro_export] macro_rules! strwriter_as_str { ($expr:expr) => { unsafe { let writer: &'static $crate::StrWriter = $expr; #[allow(clippy::transmute_bytes_to_str)] $crate::__priv_transmute_bytes_to_str!(writer.as_bytes_alt()) } }; } #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] macro_rules! conditionally_const { ( feature = $feature:literal; $( $( #[$meta:meta] )* $vis:vis fn $fn_name:ident ($($params:tt)*) -> $ret:ty $block:block )* ) => ( $( $(#[$meta])* #[cfg(feature = $feature)] $vis const fn $fn_name ($($params)*) -> $ret $block $(#[$meta])* #[cfg(not(feature = $feature))] $vis fn $fn_name ($($params)*) -> $ret $block )* ) } #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[cfg(feature = "fmt")] macro_rules! std_kind_impl { ( impl[$($impl:tt)*] $self:ty $(where[ $($where_:tt)* ])? )=>{ impl<$($impl)*> $crate::pmr::FormatMarker for $self where $($($where_)*)? { type Kind = $crate::pmr::IsStdKind; type This = Self; } impl<$($impl)* __T> $crate::pmr::IsAFormatMarker<$crate::pmr::IsStdKind, $self, __T> where $($($where_)*)? { #[inline(always)] pub const fn coerce(self, reference: &$self) -> $crate::pmr::PWrapper<$self> { $crate::pmr::PWrapper(*reference) } } } } #[macro_export] #[doc(hidden)] macro_rules! __priv_transmute_bytes_to_str { ($bytes:expr) => {{ let bytes: &'static [$crate::pmr::u8] = $bytes; let string: &'static $crate::pmr::str = { $crate::__hidden_utils::PtrToRef { ptr: bytes as *const [$crate::pmr::u8] as *const str, } .reff }; string }}; } #[macro_export] #[doc(hidden)] macro_rules! __priv_transmute_raw_bytes_to_str { ($bytes:expr) => {{ let bytes: *const [$crate::pmr::u8] = $bytes; let string: &'static $crate::pmr::str = { $crate::__hidden_utils::PtrToRef { ptr: bytes as *const str, } .reff }; string }}; } const_format-0.2.32/src/marker_traits/format_marker.rs000064400000000000000000000202421046102023000212430ustar 00000000000000//! Marker trait for types that implement the const formatting methods. //! //! use crate::wrapper_types::PWrapper; use core::marker::PhantomData; //////////////////////////////////////////////////////////////////////////////// /// Marker trait for types that implement the const formatting methods. /// /// Debug formatting can be derived using the [`ConstDebug`] derive macro. /// /// # Implementors /// /// Types that implement this trait are also expected to implement at least one of /// these inherent methods: /// /// ```ignore /// // use const_format::{Error, Format}; /// /// const fn const_debug_fmt(&self, &mut Formatter<'_>) -> Result<(), Error> /// /// const fn const_display_fmt(&self, &mut Formatter<'_>) -> Result<(), Error> /// ``` /// /// # Coercions /// /// The [`Kind`](#associatedtype.Kind) and [`This`](#associatedtype.This) associated types /// are used in the [`IsAFormatMarker`] marker type /// to automatically wrap types in [`PWrapper`] if they're from the standard library, /// otherwise leaving them unwrapped. /// /// # Examples /// /// ### Display formatting /// /// This example demonstrates how you can implement display formatting, /// without using the [`impl_fmt`] macro. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::{ /// marker_traits::{FormatMarker, IsNotStdKind}, /// Error, Formatter, StrWriter, /// formatc, writec, /// }; /// /// use std::cmp::Ordering; /// /// /// struct Compared(u32, Ordering, u32); /// /// // This is what the `impl_fmt` macro implements implicitly for all non-std types /// impl FormatMarker for Compared { /// type Kind = IsNotStdKind; /// type This = Self; /// } /// /// impl Compared { /// pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let op = match self.1 { /// Ordering::Less => "<", /// Ordering::Equal => "==", /// Ordering::Greater => ">", /// }; /// writec!(f, "{} {} {}", self.0, op, self.2) /// } /// } /// /// const S_0: &str = formatc!("{}", Compared(0, Ordering::Less, 1)); /// const S_1: &str = formatc!("{}", Compared(1, Ordering::Equal, 1)); /// const S_2: &str = formatc!("{}", Compared(2, Ordering::Greater, 1)); /// /// assert_eq!(S_0, "0 < 1"); /// assert_eq!(S_1, "1 == 1"); /// assert_eq!(S_2, "2 > 1"); /// /// ``` /// /// ### Debug formatting /// /// For examples of using the [`ConstDebug`] derive macro, /// [look here](crate::ConstDebug#examples). /// /// These are examples of implementing debug formatting using the `impl_fmt` macro for: /// /// - [Tupled structs and tuple variants.](../fmt/struct.DebugTuple.html#example) /// /// - [Braced structs and braced variants.](../fmt/struct.DebugStruct.html#example) /// /// /// [`IsAFormatMarker`]: ./struct.IsAFormatMarker.html /// [`ConstDebug`]: crate::ConstDebug /// [`impl_fmt`]: ../macro.impl_fmt.html /// pub trait FormatMarker { /// What kind of type this is, this can be one of: /// /// - [`IsArrayKind`]: For slices, and arrays. /// /// - [`IsStdKind`]: Any other standard library type. /// /// - [`IsNotStdKind`]: Any type that is not from the standard library. /// /// [`IsArrayKind`]: ./struct.IsArrayKind.html /// [`IsStdKind`]: ./struct.IsStdKind.html /// [`IsNotStdKind`]: ./struct.IsNotStdKind.html type Kind; /// The type after dereferencing, /// implemented as `type This = Self;` for all non-reference types type This: ?Sized; } /// Marker type for arrays and slices, /// used as the [`Kind`] associated type in [`FormatMarker`]. /// /// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind /// pub struct IsArrayKind(PhantomData); /// Marker type for the remaining standard library types,, /// used as the [`Kind`] associated type in [`FormatMarker`]. /// /// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind /// pub struct IsStdKind; /// Marker type for non-standard library types, /// used as the [`Kind`] associated type in [`FormatMarker`]. /// /// [`Kind`]: ./trait.FormatMarker.html#associatedtype.Kind /// pub struct IsNotStdKind; macro_rules! std_kind_impls { ($($ty:ty),* $(,)* ) => ( $( impl FormatMarker for $ty { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker { /// Copies the value from `reference`, and wraps it in a `PWrapper` #[inline(always)] pub const fn coerce(self, reference: &$ty) -> PWrapper<$ty> { PWrapper(*reference) } } )* ) } /////////////////////////////////////////////////////////////////////////////// /// Hack used to automatically wrap standard library types inside [`PWrapper`], /// while leaving user defined types unwrapped. /// /// # Type parameters /// /// `K` is `::Kind` /// The kind of type that `T` is, /// [a slice](./struct.IsArrayKind.html), /// [other std types](./struct.IsStdKind.html), /// [non-std types](./struct.IsNotStdKind.html). /// /// `T` is `::This`: /// The `R` type after removing all layers of references. /// /// `R`: a type that implements [`FormatMarker`]. /// /// # Coerce Method /// /// The `coerce` method is what does the conversion from a `&T` depending on /// the `K` type parameter: /// /// - [`IsArrayKind`]: the reference is coerced to a slice, and wrapped in a [`PWrapper`]. /// /// - [`IsStdKind`]: the referenced value is copied, and wrapped in a [`PWrapper`]. /// /// - [`IsNotStdKind`]: the reference is simply returned as a `&T`. /// #[allow(clippy::type_complexity)] pub struct IsAFormatMarker( PhantomData<( PhantomData PhantomData>, PhantomData PhantomData>, PhantomData PhantomData>, )>, ); impl Copy for IsAFormatMarker {} impl Clone for IsAFormatMarker { fn clone(&self) -> Self { *self } } impl IsAFormatMarker where R: ?Sized + FormatMarker, { /// Constructs an `IsAFormatMarker` pub const NEW: Self = Self(PhantomData); } impl IsAFormatMarker { /// Infers the type parameters by taking a reference to `R` . /// /// The `K` and `T` type parameters are determined by `R` in /// the [`NEW`] associated constant. /// /// [`NEW`]: #associatedconstant.NEW #[inline(always)] pub const fn infer_type(self, _: &R) -> Self { self } /// Removes layers of references by coercing the argument. #[inline(always)] pub const fn unreference(self, r: &T) -> &T { r } } ///////////////////////////////////////////////////////////////////////////// impl IsAFormatMarker, T, R> { /// Coerces an array to a slice, then wraps the slice in a `PWrapper` #[inline(always)] pub const fn coerce(self, slice: &[U]) -> PWrapper<&[U]> { PWrapper(slice) } } impl IsAFormatMarker { /// An identity function, just takes `reference` and returns it. #[inline(always)] pub const fn coerce(self, reference: &T) -> &T { reference } } ///////////////////////////////////////////////////////////////////////////// std_kind_impls! { i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, bool, char, } impl FormatMarker for str { type Kind = IsStdKind; type This = Self; } impl IsAFormatMarker { /// Wraps `reference` in a `PWrapper`. #[inline(always)] pub const fn coerce(self, reference: &str) -> PWrapper<&str> { PWrapper(reference) } } impl FormatMarker for [T; N] { type Kind = IsArrayKind; type This = Self; } impl FormatMarker for [T] { type Kind = IsArrayKind; type This = [T]; } impl FormatMarker for &T where T: ?Sized + FormatMarker, { type Kind = T::Kind; type This = T::This; } impl FormatMarker for &mut T where T: ?Sized + FormatMarker, { type Kind = T::Kind; type This = T::This; } const_format-0.2.32/src/marker_traits/write_marker.rs000064400000000000000000000152601046102023000211110ustar 00000000000000//! Marker trait for types that can be written to. //! //! use crate::fmt::{Formatter, StrWriter, StrWriterMut}; use core::marker::PhantomData; //////////////////////////////////////////////////////////////////////////////// /// Marker trait for types that can be written into. /// /// # Implementors /// /// Types that implement this trait are also expected to implement these inherent methods: /// /// ```ignore /// // use const_format::{FormattingFlags, Formatter}; /// /// const fn borrow_mutably(&mut self) -> &mut Self /// const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> /// ``` /// /// # Coercions /// /// The [`Kind`](#associatedtype.Kind) and [`This`](#associatedtype.This) associated types /// are used in the [`IsAWriteMarker`] marker type /// to convert a `&mut StrWriter<_>` to a `StrWriterMut<'_>`, /// and leave other mutable references unconverted. /// /// # Example /// /// Implementing this trait for a String-like inline allocated type. /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::marker_traits::{IsNotAStrWriter, WriteMarker}; /// use const_format::{Formatter, FormattingFlags}; /// use const_format::writec; /// /// mod arraystring { /// use const_format::marker_traits::{IsNotAStrWriter, WriteMarker}; /// use const_format::{Formatter, FormattingFlags}; /// /// const ARRAY_CAP: usize = 64; /// pub struct ArrayString { /// len: usize, /// arr: [u8; ARRAY_CAP], /// } /// /// impl WriteMarker for ArrayString { /// type Kind = IsNotAStrWriter; /// type This = Self; /// } /// /// impl ArrayString { /// pub const fn new() -> Self { /// Self { len: 0, arr: [0; ARRAY_CAP] } /// } /// /// // Gets the part of the array that has been written to. /// pub const fn as_bytes(&self) -> &[u8] { /// const_format::utils::slice_up_to_len_alt(&self.arr, self.len) /// } /// /// pub const fn borrow_mutably(&mut self) -> &mut Self { /// self /// } /// /// pub const fn make_formatter(&mut self, flags: FormattingFlags) -> Formatter<'_> { /// Formatter::from_custom(&mut self.arr, &mut self.len, flags) /// } /// } /// } /// use arraystring::ArrayString; /// /// /// let mut buffer = ArrayString::new(); /// /// writec!(buffer, "{:?}", [3u8, 5])?; /// assert_eq!(buffer.as_bytes(), b"[3, 5]"); /// /// writec!(buffer, "{}{:b}", "Hello, world!", 100u16)?; /// assert_eq!(buffer.as_bytes(), b"[3, 5]Hello, world!1100100"); /// /// # Ok::<(), const_format::Error>(()) /// ``` pub trait WriteMarker { /// Whether this is a StrWriter or not, this can be either of /// [`IsAStrWriter`] or [`IsNotAStrWriter`] /// /// [`IsAStrWriter`]: crate::marker_traits::IsAStrWriter /// [`IsNotAStrWriter`]: crate::marker_traits::IsNotAStrWriter type Kind; /// The type after dereferencing, /// implemented as `type This = Self;` for all non-reference types type This: ?Sized; } /// Marker type for `StrWriter`'s [`Kind`] in [`WriteMarker`]s /// /// [`Kind`]: ./trait.WriteMarker.html#associatedtype.Kind /// [`WriteMarker`]: ./trait.WriteMarker.html /// pub struct IsAStrWriter; /// Marker type for the [`Kind`] of all non-`StrWriter` types that implement [`WriteMarker`]. /// /// [`Kind`]: ./trait.WriteMarker.html#associatedtype.Kind /// [`WriteMarker`]: ./trait.WriteMarker.html /// pub struct IsNotAStrWriter; /////////////////////////////////////////////////////////////////////////////// impl WriteMarker for StrWriter { type Kind = IsAStrWriter; type This = Self; } impl WriteMarker for StrWriterMut<'_> { type Kind = IsNotAStrWriter; type This = Self; } impl WriteMarker for Formatter<'_> { type Kind = IsNotAStrWriter; type This = Self; } impl WriteMarker for &T where T: ?Sized + WriteMarker, { type Kind = T::Kind; type This = T::This; } impl WriteMarker for &mut T where T: ?Sized + WriteMarker, { type Kind = T::Kind; type This = T::This; } /////////////////////////////////////////////////////////////////////////////// /// Hack used to automatically convert a /// mutable reference to a [`StrWriter`] to a [`StrWriterMut`], /// and do nothing with other types. /// /// The conversion is done with the `coerce` methods. /// /// /// # Type parameters /// /// `K` is `::Kind` /// The kind of type that `T` is, either a [`IsAStrWriter`] or [`IsNotAStrWriter`] /// /// `T` is `::This`: /// The `R` type after removing all layers of references. /// /// `R`: A type that implements `WriteMarker`. /// /// # Coerce Method /// /// The `coerce` method is what does the conversion from a `&mut T` /// depending on the `K` type parameter: /// /// - [`IsAStrWriter`]: the reference is converted into a `StrWriterMut<'_>`. /// /// - [`IsNotAStrWriter`]: the reference is simply returned unchanged. /// /// /// [`StrWriter`]: ../fmt/struct.StrWriter.html /// /// [`StrWriterMut`]: ../fmt/struct.StrWriterMut.html /// /// [`IsAStrWriter`]: ./struct.IsAStrWriter.html /// /// [`IsNotAStrWriter`]: ./struct.IsNotAStrWriter.html /// #[allow(clippy::type_complexity)] pub struct IsAWriteMarker( PhantomData<( PhantomData PhantomData>, PhantomData PhantomData>, PhantomData PhantomData>, )>, ); impl Copy for IsAWriteMarker {} impl Clone for IsAWriteMarker { fn clone(&self) -> Self { *self } } impl IsAWriteMarker where R: ?Sized + WriteMarker, { /// Constructs a `IsAWriteMarker` pub const NEW: Self = Self(PhantomData); } ///////////////////////////////////////////////////////////////////////////// impl IsAWriteMarker { /// Infers the type parmaeters of this `IsAWriteMarker` with the passed reference. #[inline(always)] pub const fn infer_type(self, _: &R) -> Self { self } } ///////////////////////////////////////////////////////////////////////////// impl IsAWriteMarker, R> { /// Converts the `&mut StrWriter` to a `StrWriterMut<'_>`. #[inline(always)] pub const fn coerce(self, mutref: &mut StrWriter) -> StrWriterMut<'_> { mutref.as_mut() } } impl IsAWriteMarker { /// An idntity function, just takes`mutref` and returns it. #[inline(always)] pub const fn coerce(self, mutref: &mut T) -> &mut T { mutref } } ///////////////////////////////////////////////////////////////////////////// const_format-0.2.32/src/marker_traits.rs000064400000000000000000000006121046102023000164110ustar 00000000000000//! Marker traits for types that can be formatted and/or be written to. //! //! # Features //! //! This module is only exported with the "fmt" feature mod format_marker; mod write_marker; #[doc(inline)] pub use self::{ format_marker::{FormatMarker, IsAFormatMarker, IsArrayKind, IsNotStdKind, IsStdKind}, write_marker::{IsAStrWriter, IsAWriteMarker, IsNotAStrWriter, WriteMarker}, }; const_format-0.2.32/src/msg.rs000064400000000000000000000042161046102023000143340ustar 00000000000000#![allow(non_camel_case_types)] use crate::fmt::{Error, StrWriter}; use core::marker::PhantomData; macro_rules! type_level_error { ( arguments($($args:tt)*) $($error:ident => $error_ty:ident<$($error_param:ident),*> ,)* ) => { type_level_error!{ @inner arguments($($args)*) Result::Ok(()), Ok => Ok<>, $( Result::Err(Error::$error), $error => $error_ty<$($error_param),*>, )* } }; (@inner arguments($cap:ident) $($matched:pat , $error:ident => $error_ty:ident<$($error_param:ident),*> ,)* ) => { enum ErrorKind { $($error,)* } impl ErrorTuple { pub const EMPTY: ErrorTuple = ErrorTuple{ error_variant: ErrorKind::Ok as usize, capacity: 0, }; pub const fn new(opt: Result<(), Error>, writer: &StrWriter) -> Self{ let variant = match opt { $($matched => ErrorKind::$error as usize,)* }; Self{ error_variant: variant, capacity: writer.capacity(), } } } $( pub struct $error_ty<$($error_param,)*>(PhantomData<($($error_param,)*)>); impl<$($error_param,)*> $error_ty<$($error_param,)*> { pub const NEW: Self = Self(PhantomData); } impl<$cap> ErrorAsType for ErrorPicker<[(); ErrorKind::$error as usize], $cap> { type Type = $error_ty<$($error_param,)*>; } )* } } pub struct ErrorTupleAndStrWriter
{ pub error: ErrorTuple, pub writer: StrWriter, } pub struct ErrorPicker(PhantomData (E, Cap)>); pub struct ErrorTuple { pub error_variant: usize, pub capacity: usize, } pub trait ErrorAsType { type Type; } type_level_error! { arguments(cap) NotEnoughSpace => not_enough_space_to_write_text_in_StrWriter_with_this_capacity, NotAscii => input_text_was_not_ascii<>, NotOnCharBoundary => NotOnCharBoundary<>, } const_format-0.2.32/src/pargument.rs000064400000000000000000000126221046102023000155500ustar 00000000000000#![allow(clippy::wrong_self_convention)] use crate::{ char_encoding::FmtChar, formatting::{Formatting, FormattingFlags}, wrapper_types::PWrapper, }; #[doc(hidden)] /// The uniform representation for every argument of the concatcp macro. pub struct PArgument { pub elem: PVariant, pub fmt_len: usize, pub fmt: Formatting, pub fmt_flags: FormattingFlags, } impl PArgument { /// Calculates the length of the string after adding up all the PArguments pub const fn calc_len(mut args: &[PArgument]) -> usize { let mut sum = 0; while let [curr, rem @ ..] = args { args = rem; sum += curr.fmt_len; } sum } } #[doc(hidden)] pub enum PVariant { Str(&'static str), Int(Integer), Char(FmtChar), } #[derive(Debug, Copy, Clone)] pub struct Integer { pub is_negative: bool, pub unsigned: u128, pub mask: &'static u128, // A mask which disables the bits that weren't in the original number } #[doc(hidden)] pub struct PConvWrapper(pub T); macro_rules! pconvwrapper_impls { ( $( ($Signed:ty, $Unsigned:ty) )* ) => ( pconvwrapper_impls!{ @inner to_pargument_display, compute_display_len, Formatting::Display; $(($Signed, $Unsigned))* } pconvwrapper_impls!{ @inner to_pargument_debug, compute_debug_len, Formatting::Debug; $(($Signed, $Unsigned))* } $( #[doc(hidden)] impl PConvWrapper<$Signed>{ pub const fn to_integer(self)->Integer{ Integer{ is_negative: self.0 < 0, unsigned: PWrapper(self.0).unsigned_abs() as u128, mask: &(((!(0 as $Signed)) as $Unsigned) as u128), } } } #[doc(hidden)] impl PConvWrapper<$Unsigned>{ pub const fn to_integer(self)->Integer{ Integer{ is_negative: false, unsigned: self.0 as u128, mask: &((!(0 as $Unsigned)) as u128), } } } )* ); (@inner $method:ident, $called:ident, $formatting:expr; $( ($Signed:ty, $Unsigned:ty) )* ) => ( $( #[doc(hidden)] impl PConvWrapper<$Signed> { pub const fn $method(self, fmt_flags: FormattingFlags)->PArgument{ PArgument { fmt_len: $crate::pmr::PWrapper(self.0).$called(fmt_flags), fmt: $formatting, fmt_flags, elem: PVariant::Int(self.to_integer()), } } } #[doc(hidden)] impl PConvWrapper<$Unsigned> { pub const fn $method(self, fmt_flags: FormattingFlags)->PArgument{ PArgument { fmt_len: $crate::pmr::PWrapper(self.0).$called(fmt_flags), fmt: $formatting, fmt_flags, elem: PVariant::Int(self.to_integer()), } } } )* ); } pconvwrapper_impls! { (i8, u8) (i16, u16) (i32, u32) (i64, u64) (i128, u128) (isize, usize) } #[doc(hidden)] impl PConvWrapper { #[inline] pub const fn to_pargument_display(self, _: FormattingFlags) -> PArgument { self.0 } #[inline] pub const fn to_pargument_debug(self, _: FormattingFlags) -> PArgument { self.0 } } #[doc(hidden)] impl PConvWrapper { #[inline] pub const fn to_pargument_display(self, _: FormattingFlags) -> PArgument { PConvWrapper(if self.0 { "true" } else { "false" }) .to_pargument_display(FormattingFlags::DEFAULT) } #[inline] pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { self.to_pargument_display(fmt_flags) } } #[doc(hidden)] impl PConvWrapper { #[inline] pub const fn to_pargument_display(self, fmt_flags: FormattingFlags) -> PArgument { let elem = crate::char_encoding::char_to_display(self.0); PArgument { fmt_len: elem.len(), fmt_flags, fmt: Formatting::Display, elem: PVariant::Char(elem), } } #[inline] pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { let elem = crate::char_encoding::char_to_debug(self.0); PArgument { fmt_len: elem.len(), fmt_flags, fmt: Formatting::Debug, elem: PVariant::Char(elem), } } } #[doc(hidden)] impl PConvWrapper<&'static str> { #[inline] pub const fn to_pargument_display(self, fmt_flags: FormattingFlags) -> PArgument { PArgument { fmt_len: PWrapper(self.0).compute_display_len(fmt_flags), fmt_flags, fmt: Formatting::Display, elem: PVariant::Str(self.0), } } #[inline] pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { PArgument { fmt_len: PWrapper(self.0).compute_debug_len(fmt_flags), fmt_flags, fmt: Formatting::Debug, elem: PVariant::Str(self.0), } } } const_format-0.2.32/src/slice_cmp.rs000064400000000000000000000061411046102023000155030ustar 00000000000000/// A const equivalent of `&str` equality comparison. /// /// # Example /// #[cfg_attr(feature = "fmt", doc = "```rust")] #[cfg_attr(not(feature = "fmt"), doc = "```ignore")] /// use const_format::utils::str_eq; /// /// const STRS: &[&str] = &[ /// "foo", /// "fooooo", /// "bar", /// "baz", /// ]; /// /// const ARE_0_0_EQ: bool = str_eq(STRS[0], STRS[0]); /// const ARE_0_1_EQ: bool = str_eq(STRS[0], STRS[1]); /// /// const ARE_1_1_EQ: bool = str_eq(STRS[1], STRS[1]); /// const ARE_1_2_EQ: bool = str_eq(STRS[1], STRS[2]); /// /// const ARE_2_2_EQ: bool = str_eq(STRS[2], STRS[2]); /// const ARE_2_3_EQ: bool = str_eq(STRS[2], STRS[3]); /// /// assert!( ARE_0_0_EQ ); /// assert!( !ARE_0_1_EQ ); /// /// assert!( ARE_1_1_EQ ); /// assert!( !ARE_1_2_EQ ); /// /// assert!( ARE_2_2_EQ ); /// assert!( !ARE_2_3_EQ ); /// /// ``` /// pub const fn str_eq(left: &str, right: &str) -> bool { u8_slice_eq(left.as_bytes(), right.as_bytes()) } /// A const equivalent of `&[u8]` equality comparison. /// /// # Example /// #[cfg_attr(feature = "fmt", doc = "```rust")] #[cfg_attr(not(feature = "fmt"), doc = "```ignore")] /// use const_format::utils::u8_slice_eq; /// /// const SLICES: &[&[u8]] = &[ /// &[10, 20], /// &[10, 20, 30, 40], /// &[3, 5, 8, 13], /// &[4, 9, 16, 25], /// ]; /// /// const ARE_0_0_EQ: bool = u8_slice_eq(SLICES[0], SLICES[0]); /// const ARE_0_1_EQ: bool = u8_slice_eq(SLICES[0], SLICES[1]); /// /// const ARE_1_1_EQ: bool = u8_slice_eq(SLICES[1], SLICES[1]); /// const ARE_1_2_EQ: bool = u8_slice_eq(SLICES[1], SLICES[2]); /// /// const ARE_2_2_EQ: bool = u8_slice_eq(SLICES[2], SLICES[2]); /// const ARE_2_3_EQ: bool = u8_slice_eq(SLICES[2], SLICES[3]); /// /// assert!( ARE_0_0_EQ ); /// assert!( !ARE_0_1_EQ ); /// /// assert!( ARE_1_1_EQ ); /// assert!( !ARE_1_2_EQ ); /// /// assert!( ARE_2_2_EQ ); /// assert!( !ARE_2_3_EQ ); /// /// ``` /// pub const fn u8_slice_eq(left: &[u8], right: &[u8]) -> bool { if left.len() != right.len() { return false; } let mut i = 0; while i != left.len() { if left[i] != right[i] { return false; } i += 1; } true } #[cfg(test)] mod tests { use super::*; #[test] fn slice_eq_test() { assert!(u8_slice_eq(&[], &[])); assert!(!u8_slice_eq(&[], &[0])); assert!(!u8_slice_eq(&[0], &[])); assert!(u8_slice_eq(&[0], &[0])); assert!(!u8_slice_eq(&[0], &[1])); assert!(!u8_slice_eq(&[1], &[0])); assert!(!u8_slice_eq(&[0], &[0, 1])); assert!(!u8_slice_eq(&[0, 1], &[0])); assert!(u8_slice_eq(&[0, 1], &[0, 1])); assert!(!u8_slice_eq(&[0, 1], &[0, 2])); } #[test] fn str_eq_test() { assert!(str_eq("", "")); assert!(!str_eq("", "0")); assert!(!str_eq("0", "")); assert!(str_eq("0", "0")); assert!(!str_eq("0", "1")); assert!(!str_eq("1", "0")); assert!(!str_eq("0", "0, 1")); assert!(!str_eq("0, 1", "0")); assert!(!str_eq("0, 1", "1")); assert!(str_eq("0, 1", "0, 1")); assert!(!str_eq("0, 1", "0, 2")); } } const_format-0.2.32/src/test_utils.rs000064400000000000000000000042351046102023000157460ustar 00000000000000#![allow(missing_docs)] #[doc(hidden)] #[macro_export] macro_rules! __identity { ($($tt:tt)*) => {$($tt)*}; } #[doc(hidden)] #[macro_export] macro_rules! __declare_rng_ext { () => { pub trait RngExt { fn as_rng(&self) -> &fastrand::Rng; fn pick<'a, T>(&self, slice: &'a [T]) -> &'a T { &slice[self.as_rng().usize(0..slice.len())] } fn char_(&self, bounds: core::ops::RangeInclusive) -> char { let this = self.as_rng(); if let None = bounds.clone().next() { panic!("There are no chars in the {:?} bounds", bounds); } let u32_bounds = u32::from(*bounds.start())..=u32::from(*bounds.end()); loop { if let Some(x) = core::char::from_u32(this.u32(u32_bounds.clone())) { break x; } } } fn unicode_char(&self) -> char { let this = self.as_rng(); let range = this .pick(&[ '\u{0000}'..='\u{007F}', '\u{0080}'..='\u{07FF}', '\u{0800}'..='\u{FFFF}', '\u{10000}'..='\u{10FFFF}', ]) .clone(); this.char_(range) } } impl RngExt for fastrand::Rng { fn as_rng(&self) -> &fastrand::Rng { self } } }; } pub const ALL_ASCII: &str = "\ \x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\ \x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \ !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]\ ^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u{80}\u{81}\u{90}\u{91}\ "; pub const ALL_ASCII_ESCAPED: &str = "\ \\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0B\\x0C\\r\\x0E\\x0F\ \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1A\\x1B\\x1C\\x1D\\x1E\\x1F \ !\\\"#$%&\\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]\ ^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u{80}\u{81}\u{90}\u{91}\ "; const_format-0.2.32/src/utils.rs000064400000000000000000000126141046102023000147070ustar 00000000000000//! Miscelaneous functions. use core::ops::Range; /// Newtype wrapper to get around limitations in `const fn`s pub(crate) struct Constructor(fn() -> T); pub use crate::slice_cmp::{str_eq, u8_slice_eq}; #[doc(hidden)] #[inline] pub const fn saturate_range(s: &[u8], range: &Range) -> Range { let len = s.len(); let end = min_usize(range.end, len); min_usize(range.start, end)..end } #[doc(hidden)] #[cfg(feature = "rust_1_64")] #[repr(C)] pub union Dereference<'a, T: ?Sized> { pub ptr: *const T, pub reff: &'a T, } //////////////////////////////////////////////////////////////////////////////// macro_rules! slice_up_to_len_alt_docs { ($item:item) => { /// A const equivalent of `&slice[..len]`. /// /// If `slice.len() < len`, this simply returns `slice` back. /// /// # Runtime /// /// If the "rust_1_64" feature is disabled, /// thich takes linear time to remove the trailing elements, /// proportional to `slice.len() - len`. /// /// If the "rust_1_64" feature is enabled, it takes constant time to run. /// /// # Example /// /// ```rust /// use const_format::utils::slice_up_to_len_alt; /// /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89]; /// /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2); /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4); /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX); /// /// assert_eq!(TWO, &[3, 5]); /// assert_eq!(FOUR, &[3, 5, 8, 13]); /// assert_eq!(FIBB, ALL); /// /// ``` $item }; } slice_up_to_len_alt_docs! { #[cfg(feature = "rust_1_64")] #[inline(always)] pub const fn slice_up_to_len_alt(slice: &[T], len: usize) -> &[T] { slice_up_to_len(slice, len) } } slice_up_to_len_alt_docs! { #[cfg(not(feature = "rust_1_64"))] pub const fn slice_up_to_len_alt(slice: &[T], len: usize) -> &[T] { let mut rem = slice.len().saturating_add(1).saturating_sub(len); let mut ret = slice; if rem == 0 { return slice; } macro_rules! slice_up_to_len_alt_impl{ ( $( ($len:expr, [$($ignored:tt)*]) )* )=>{ $( while rem > $len { if let [next @ .., $($ignored)* ] = ret { ret = next; rem -= $len; } } )* } } slice_up_to_len_alt_impl!{ (36, [_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,]) (6, [_, _, _, _, _, _]) (1, [_]) } ret } } //////////////////////////////////////////////////////////////////////////////// macro_rules! slice_up_to_len_docs { ($item:item) => { /// A conditionally-const equivalent of `&slice[..len]`. /// /// If `slice.len() < len`, this simply returns `slice` back. /// /// # Constness /// /// This function takes constant time, /// and in order to be `const fn` it requires the "rust_1_64" /// feature to be enabled. /// /// # Example /// /// ```rust /// use const_format::utils::slice_up_to_len_alt; /// /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89]; /// /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2); /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4); /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX); /// /// assert_eq!(TWO, &[3, 5]); /// assert_eq!(FOUR, &[3, 5, 8, 13]); /// assert_eq!(FIBB, ALL); /// /// ``` $item }; } slice_up_to_len_docs! { #[cfg(feature = "rust_1_64")] #[inline] pub const fn slice_up_to_len(slice: &[T], len: usize) -> &[T] { if len > slice.len() { return slice; } // Doing this to get a slice up to length at compile-time unsafe { let raw_slice = core::ptr::slice_from_raw_parts(slice.as_ptr(), len); Dereference { ptr: raw_slice }.reff } } } slice_up_to_len_docs! { #[cfg(not(feature = "rust_1_64"))] #[inline] pub fn slice_up_to_len(slice: &[T], len: usize) -> &[T] { if len > slice.len() { return slice; } &slice[..len] } } //////////////////////////////////////////////////////////////////////////////// pub(crate) const fn min_usize(l: usize, r: usize) -> usize { if l < r { l } else { r } } #[cfg(test)] mod tests { use super::*; #[test] fn test_slice_up_to_len_alt() { let mut list = [0u16; 256]; (100..).zip(list.iter_mut()).for_each(|(i, m)| *m = i); for i in 0..list.len() { assert_eq!(slice_up_to_len_alt(&list, i), &list[..i]); } } #[test] fn slice_in_bounds() { assert_eq!(slice_up_to_len(&[3, 5], 0), []); assert_eq!(slice_up_to_len(&[3, 5], 1), [3]); assert_eq!(slice_up_to_len(&[3, 5], 2), [3, 5]); assert_eq!(slice_up_to_len(&[3, 5], 3), [3, 5]); assert_eq!(slice_up_to_len(&[3, 5], 4), [3, 5]); } } const_format-0.2.32/src/wrapper_types/ascii_str.rs000064400000000000000000000216321046102023000204330ustar 00000000000000// use crate::fmt::Error; #[cfg(feature = "fmt")] use crate::fmt::{Error, Formatter}; use core::fmt::{self, Display}; //////////////////////////////////////////////////////////////////////////////// /// An ascii string slice. /// /// You can also construct an `AsciiStr` at compile-time with the [`ascii_str`] macro, /// erroring at compile if the constant isn't ascii. /// /// # Example /// /// ```rust /// use const_format::wrapper_types::{AsciiStr, NotAsciiError}; /// use const_format::ascii_str; /// /// const HELLO: AsciiStr = unwrap_ascii(AsciiStr::new(b"hello")); /// const EURO: AsciiStr = unwrap_ascii(AsciiStr::new("foo €".as_bytes())); /// /// assert_eq!(HELLO.as_str(), "hello"); /// assert_eq!(EURO.as_str(), ""); /// assert_eq!(AsciiStr::new("foo €".as_bytes()), Err(NotAsciiError{invalid_from: 4})); /// /// const fn unwrap_ascii(res: Result, NotAsciiError>) -> AsciiStr<'_> { /// match res { /// Ok(x) => x, /// Err(_) => ascii_str!(""), /// } /// } /// /// ``` /// /// [`ascii_str`]: ./macro.ascii_str.html /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct AsciiStr<'a>(&'a [u8]); impl<'a> AsciiStr<'a> { /// Constructs this AsciiStr from a possibly non-ascii str slice. /// /// Returns a `NonAsciiError` error on the first non-ascii byte. /// /// # Example /// /// ```rust /// use const_format::wrapper_types::{AsciiStr, NotAsciiError}; /// /// let ok = AsciiStr::from_str("foo bar").unwrap(); /// /// assert_eq!(ok.as_str(), "foo bar"); /// assert_eq!(AsciiStr::from_str("foo bar ½"), Err(NotAsciiError{invalid_from: 8})); /// /// ``` #[inline(always)] pub const fn from_str(s: &'a str) -> Result { Self::new(s.as_bytes()) } /// Constructs this AsciiStr from a possibly non-ascii byte slice. /// /// Returns a `NonAsciiError` error on the first non-ascii byte. /// /// # Example /// /// ```rust /// use const_format::wrapper_types::{AsciiStr, NotAsciiError}; /// /// let ok = AsciiStr::new(b"foo bar").unwrap(); /// /// assert_eq!(ok.as_str(), "foo bar"); /// assert_eq!(AsciiStr::new(b"foo bar \x80"), Err(NotAsciiError{invalid_from: 8})); /// /// ``` pub const fn new(s: &'a [u8]) -> Result { __for_range! {i in 0..s.len()=> if s[i] > 127 { return Err(NotAsciiError{invalid_from: i}); } } Ok(AsciiStr(s)) } /// Constructs an empty `AsciiStr` /// /// # Example /// /// ```rust /// use const_format::AsciiStr; /// /// assert_eq!(AsciiStr::empty().as_str(), ""); /// ``` pub const fn empty() -> Self { Self(&[]) } /// Queries the length of the `AsciiStr` /// /// # Example /// /// ```rust /// use const_format::{AsciiStr, ascii_str}; /// /// assert_eq!(AsciiStr::empty().len(), 0); /// assert_eq!(ascii_str!("hello").len(), 5); /// ``` #[inline(always)] pub const fn len(self) -> usize { self.0.len() } /// Queries whether this `AsciiStr` is empty. /// /// # Example /// /// ```rust /// use const_format::{AsciiStr, ascii_str}; /// /// assert_eq!(AsciiStr::empty().is_empty(), true); /// assert_eq!(ascii_str!("hello").is_empty(), false); /// ``` #[inline(always)] pub const fn is_empty(self) -> bool { self.0.is_empty() } /// Accessor for the wrapped ascii string. /// /// # Example /// /// ```rust /// use const_format::{AsciiStr, ascii_str}; /// /// assert_eq!(AsciiStr::empty().as_bytes(), b""); /// assert_eq!(ascii_str!("hello").as_bytes(), b"hello"); /// ``` #[inline(always)] pub const fn as_bytes(self) -> &'a [u8] { self.0 } /// Accessor for the wrapped ascii string. /// /// # Example /// /// ```rust /// use const_format::{AsciiStr, ascii_str}; /// /// assert_eq!(AsciiStr::empty().as_str(), ""); /// assert_eq!(ascii_str!("hello").as_str(), "hello"); /// ``` #[inline] pub fn as_str(self) -> &'a str { unsafe { core::str::from_utf8_unchecked(self.0) } } } //////////////////////////////////////////////////////////////////////////////// /// Error from [`AsciiStr`] constructor, caused by a byte not being valid ascii. /// /// [`AsciiStr`]: ../struct.AsciiStr.html #[derive(Debug, Copy, Clone, PartialEq)] pub struct NotAsciiError { /// The first non-ascii byte in the byte slice. pub invalid_from: usize, } impl Display for NotAsciiError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fmt.write_str("error: the input bytes were not valid ascii") } } //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "fmt")] impl_fmt! { impl AsciiStr<'_>; /// pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_ascii(*self) } /// pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_ascii_debug(*self) } } //////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { use super::*; #[cfg(feature = "fmt")] use crate::fmt::ComputeStrLength; use arrayvec::ArrayString; #[test] fn basic() { { let ok = AsciiStr::new("hello!".as_bytes()).unwrap(); assert_eq!(ok.as_bytes(), "hello!".as_bytes()); assert_eq!(ok.as_str(), "hello!"); } { let err = AsciiStr::from_str("Φοο!").unwrap_err(); assert_eq!(err.invalid_from, 0) } { let err = AsciiStr::from_str("hello Φοο!").unwrap_err(); assert_eq!(err.invalid_from, 6) } } // This doesn't use unsafe code #[cfg(not(miri))] #[test] fn only_ascii_constructible() { let mut string = ArrayString::<[u8; 1024]>::new(); let min = '\u{20}'; let max = '\u{80}'; assert!(!max.is_ascii()); for end in min..=max { for start in min..=end { string.clear(); for n in start..=end { string.push(n); } let res = AsciiStr::new(string.as_bytes()); assert_eq!(res.is_ok(), string.as_bytes().is_ascii()); if let Ok(ascii) = res { assert_eq!(ascii.as_bytes(), string.as_bytes()); } } } } #[cfg(feature = "fmt")] #[test] fn formatting() { use crate::fmt::{FormattingFlags, NumberFormatting, StrWriter}; const fn inner_debug( ascii: AsciiStr<'_>, writer: &mut StrWriter, flags: FormattingFlags, ) -> Result { try_!(ascii.const_debug_fmt(&mut writer.make_formatter(flags))); let mut str_len = ComputeStrLength::new(); try_!(ascii.const_debug_fmt(&mut str_len.make_formatter(flags))); Ok(str_len.len()) } const fn inner_display( ascii: AsciiStr<'_>, writer: &mut StrWriter, flags: FormattingFlags, ) -> Result { try_!(ascii.const_display_fmt(&mut writer.make_formatter(flags))); let mut str_len = ComputeStrLength::new(); try_!(ascii.const_display_fmt(&mut str_len.make_formatter(flags))); Ok(str_len.len()) } fn test_case( ascii: AsciiStr<'_>, writer: &mut StrWriter, flags: FormattingFlags, expected_debug: &str, expected_display: &str, ) { writer.clear(); let len = inner_debug(ascii, writer, flags).unwrap(); assert_eq!(writer.as_str(), expected_debug); assert_eq!(writer.len(), len, "{}", writer.as_str()); writer.clear(); let len = inner_display(ascii, writer, flags).unwrap(); assert_eq!(writer.as_str(), expected_display); assert_eq!(writer.len(), len, "{}", writer.as_str()); } let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); let foo = AsciiStr::new("\0\x10hello\tworld\n".as_bytes()).unwrap(); for num_fmt in NumberFormatting::ALL.iter().copied() { for is_alt in [false, true].iter().copied() { let flag = FormattingFlags::NEW .set_num_fmt(num_fmt) .set_alternate(is_alt); test_case( foo, writer, flag, "\"\\x00\\x10hello\\tworld\\n\"", foo.as_str(), ); } } } } const_format-0.2.32/src/wrapper_types/pwrapper/tests.rs000064400000000000000000000131261046102023000214540ustar 00000000000000use crate::{ formatting::{FormattingFlags, NumberFormatting as NF}, pargument::PConvWrapper, wrapper_types::PWrapper, }; use arrayvec::ArrayString; use core::fmt::{self, Write}; fn get_digits_display(n: impl fmt::Display) -> ArrayString<[u8; 64]> { let mut buff = ArrayString::<[u8; 64]>::new(); write!(buff, "{}", n).unwrap(); buff } fn get_hex_digits(n: impl fmt::UpperHex) -> ArrayString<[u8; 64]> { let mut buff = ArrayString::<[u8; 64]>::new(); write!(buff, "{:X}", n).unwrap(); buff } fn get_lower_hex_digits(n: impl fmt::LowerHex) -> ArrayString<[u8; 64]> { let mut buff = ArrayString::<[u8; 64]>::new(); write!(buff, "{:x}", n).unwrap(); buff } fn get_binary_digits(n: impl fmt::Binary) -> ArrayString<[u8; 192]> { let mut buff = ArrayString::<[u8; 192]>::new(); write!(buff, "{:b}", n).unwrap(); buff } const DEF_FLAGS: FormattingFlags = FormattingFlags::DEFAULT; // This doesn't use unsafe code #[cfg(not(miri))] macro_rules! check_number_of_digits_ { ($ty:ty) => {{ fn number_of_digits_test_case(val: $ty) { let display_digits = get_digits_display(val); let hex_digits = get_hex_digits(val); let lower_hex_digits = get_lower_hex_digits(val); let binary_digits = get_binary_digits(val); let wrapper = PWrapper(val); { assert_eq!( wrapper.compute_display_len(DEF_FLAGS), display_digits.len(), "const_display_len" ); assert_eq!( wrapper.compute_debug_len(DEF_FLAGS), display_digits.len(), "const_debug_len " ); assert_eq!( wrapper.compute_debug_len(DEF_FLAGS.set_num_fmt(NF::Hexadecimal)), hex_digits.len(), "const_debug_len hexadecimal" ); assert_eq!( wrapper.compute_debug_len(DEF_FLAGS.set_lower_hexadecimal()), lower_hex_digits.len(), "const_debug_len lower hexadecimal" ); assert_eq!( wrapper.compute_debug_len(DEF_FLAGS.set_num_fmt(NF::Binary)), binary_digits.len(), "const_debug_len binary" ); } { let integer = PWrapper(PConvWrapper(val).to_integer()); let sa = integer.to_start_array_display(); assert_eq!( &sa.array[sa.start..], display_digits.as_bytes(), "const_display_len" ); let sa = integer.to_start_array_debug(); assert_eq!( &sa.array[sa.start..], display_digits.as_bytes(), "const_debug_len " ); let sa = integer.to_start_array_hexadecimal(FormattingFlags::NEW); assert_eq!( &sa.array[sa.start..], hex_digits.as_bytes(), "const_debug_len hexadecimal" ); let sa = integer .to_start_array_hexadecimal(FormattingFlags::NEW.set_lower_hexadecimal()); assert_eq!( &sa.array[sa.start..], lower_hex_digits.as_bytes(), "const_debug_len lower hexadecimal" ); let sa = integer.to_start_array_binary(FormattingFlags::NEW); assert_eq!( &sa.array[sa.start..], binary_digits.as_bytes(), "const_debug_len binary" ); } } let zero: $ty = 0; let one: $ty = 1; let two: $ty = 2; number_of_digits_test_case(zero); number_of_digits_test_case(one); number_of_digits_test_case(two); let mut n: $ty = 10; loop { number_of_digits_test_case(n - 1); number_of_digits_test_case(n); number_of_digits_test_case(n + 1); match n.checked_mul(10) { Some(next) => n = next, None => break, } } let max_s2: $ty = <$ty>::MAX - 2; let max_s1: $ty = <$ty>::MAX - 1; let max_s0: $ty = <$ty>::MAX; number_of_digits_test_case(max_s2); number_of_digits_test_case(max_s1); number_of_digits_test_case(max_s0); }}; } // This doesn't use unsafe code #[cfg(not(miri))] #[test] fn pwrapper_methods() { check_number_of_digits_!(i8); check_number_of_digits_!(u8); check_number_of_digits_!(i16); check_number_of_digits_!(u16); check_number_of_digits_!(i32); check_number_of_digits_!(u32); check_number_of_digits_!(u64); check_number_of_digits_!(i64); check_number_of_digits_!(usize); check_number_of_digits_!(isize); check_number_of_digits_!(u128); check_number_of_digits_!(i128); } #[cfg(feature = "fmt")] #[test] fn wrapped_formatting() { use crate::fmt::{Error, StrWriter}; const fn inner(writer: &mut StrWriter) -> Result<(), Error> { try_!(writec!(writer, "{},", PWrapper(3u8))); try_!(writec!(writer, "{},", PWrapper(5u16))); try_!(writec!(writer, "{},", PWrapper(8u32))); try_!(writec!(writer, "{},", PWrapper("hello"))); Ok(()) } let writer: &mut StrWriter = &mut StrWriter::new([0; 128]); inner(writer).unwrap(); assert_eq!(writer.as_str(), "3,5,8,hello,"); } const_format-0.2.32/src/wrapper_types/pwrapper.rs000064400000000000000000000364351046102023000203220ustar 00000000000000use crate::{ formatting::{FormattingFlags, NumberFormatting, StartAndArray, FOR_ESCAPING}, pargument::Integer, }; use core::ops::Range; #[cfg(test)] mod tests; /// Wrapper for many std types, /// which implements the `const_debug_fmt` and/or `const_display_fmt` methods for them. /// /// The macros from this crate automatically wraps std types in this type, /// so you only need to use it if you're manually calling the `const_*_fmt` methods. /// /// ### Constructors /// /// Most std types can be wrapped in this type simply by doing `PWrapper(value)`. /// /// To wrap arrays, there is the [`PWrapper::slice`](#method.slice) constructor /// for convenience. /// /// ### Excluded std types /// /// Note that this type does not implement the formatting methods /// for std types which wrap non-std types, /// only for a selection of wrapped std types. /// /// You can use the [`call_debug_fmt`] macro to format arrays/slices/Options of /// any type that can be const debug formatted. /// /// # Example /// /// This example demonstrates how you can implement debug formatting for a type /// using PWrapper to write std types. /// #[cfg_attr(feature = "fmt", doc = "```rust")] #[cfg_attr(not(feature = "fmt"), doc = "```ignore")] /// #![feature(const_mut_refs)] /// /// use const_format::{Error, Formatter, PWrapper}; /// use const_format::{impl_fmt, formatc, try_}; /// /// use core::num::NonZeroU32; /// /// pub struct Divide(pub u32, pub u32); /// /// impl_fmt!{ /// impl Divide; /// /// pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { /// let Self(left, right) = *self; /// let divided = self.0 / self.1; /// /// let mut f = f.debug_struct("Divide"); /// try_!(PWrapper(self.0).const_debug_fmt(f.field("numerator"))); /// try_!(PWrapper(self.1).const_debug_fmt(f.field("denominator"))); /// try_!(PWrapper(divided).const_debug_fmt(f.field("divided"))); /// f.finish() /// } /// } /// /// const TEXT: &str = formatc!("{:?}", Divide(34, 11)); /// const T_HEX: &str = formatc!("{:X?}", Divide(34, 11)); /// const T_BIN: &str = formatc!("{:b?}", Divide(34, 11)); /// /// assert_eq!(TEXT, "Divide { numerator: 34, denominator: 11, divided: 3 }"); /// assert_eq!(T_HEX, "Divide { numerator: 22, denominator: B, divided: 3 }"); /// assert_eq!(T_BIN, "Divide { numerator: 100010, denominator: 1011, divided: 11 }"); /// ``` /// /// [`call_debug_fmt`]: ./macro.call_debug_fmt.html /// [`writec`]: ./macro.writec.html /// #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] #[derive(Copy, Clone)] pub struct PWrapper(pub T); impl<'a, T> PWrapper<&'a [T]> { /// For constructing from a reference to an array. /// /// With slices you can do `PWrapper(slice)` as well. #[inline(always)] pub const fn slice(x: &'a [T]) -> Self { Self { 0: x } } } macro_rules! compute_hex_count { ($bits:expr, $int:expr, $with_0x:expr) => {{ let with_0x = ($with_0x as usize) << 1; let i = ($bits - $int.leading_zeros()) as usize; (if i == 0 { 1 } else { (i >> 2) + ((i & 3) != 0) as usize }) + with_0x }}; } macro_rules! compute_binary_count { ($bits:expr, $int:expr, $with_0b:expr) => {{ let with_0b = ($with_0b as usize) << 1; let i = ($bits - $int.leading_zeros()) as usize; (if i == 0 { 1 } else { i }) + with_0b }}; } macro_rules! impl_number_of_digits { (num number_of_digits;delegate $n:ident $len:ident)=>{ $n.number_of_digits() }; (num number_of_digits;128 $n:ident $len:ident)=>{{ if $n >= 1_0000_0000_0000_0000{$n /= 1_0000_0000_0000_0000; $len += 16;} impl_number_of_digits!(num number_of_digits;64 $n $len) }}; (num number_of_digits;64 $n:ident $len:ident)=>{{ if $n >= 1_0000_0000_0000{$n /= 1_0000_0000_0000; $len += 12;} impl_number_of_digits!(num number_of_digits;32 $n $len) }}; (num number_of_digits;32 $n:ident $len:ident)=>{{ if $n >= 1_0000_0000{$n /= 100_000_000; $len += 8;} impl_number_of_digits!(num number_of_digits;16 $n $len) }}; (num number_of_digits;16 $n:ident $len:ident)=>{{ if $n >= 1_0000{$n /= 1_0000; $len += 4;} impl_number_of_digits!(num number_of_digits;8 $n $len) }}; (num number_of_digits;8 $n:ident $len:ident)=>{{ if $n >= 100{$n /= 100; $len += 2;} if $n >= 10{ $len += 1;} $len }}; (@shared $This:ty, $bits:tt)=>{ impl PWrapper<$This> { /// Computes how long much space is necessary to write this integer as a literal. #[allow(unused_mut,unused_variables)] #[doc(hidden)] pub const fn compute_debug_len(self, fmt: FormattingFlags)-> usize { match fmt.num_fmt() { NumberFormatting::Decimal=> self.compute_display_len(fmt), NumberFormatting::Hexadecimal=> compute_hex_count!($bits, self.0, fmt.is_alternate()), NumberFormatting::Binary=> compute_binary_count!($bits, self.0, fmt.is_alternate()), } } /// Computes how long much space is necessary to /// write this integer as a hexadecimal literal. pub const fn hexadecimal_len(self, fmt: FormattingFlags)-> usize { compute_hex_count!($bits, self.0, fmt.is_alternate()) } /// Computes how long much space is necessary to /// write this integer as a binary literal. pub const fn binary_len(self, fmt: FormattingFlags)-> usize { compute_binary_count!($bits, self.0, fmt.is_alternate()) } } }; (impl_either; signed, ($This:ty, $Unsigned:ty), $bits:tt $(,)? )=>{ impl_number_of_digits!{@shared $This, $bits} impl PWrapper<$This> { /// Returns the absolute value of this integer, as the equivalent unsigned type. pub const fn unsigned_abs(self) -> $Unsigned { self.0.wrapping_abs() as $Unsigned } #[allow(unused_mut,unused_variables)] #[doc(hidden)] pub const fn compute_display_len(self, _: FormattingFlags)-> usize { let mut n = self.0.wrapping_abs() as $Unsigned; let mut len = 1 + (self.0 < 0) as usize; impl_number_of_digits!(num number_of_digits;$bits n len) } } }; (impl_either; unsigned, ($This:ty, $Unsigned:ty), $bits:tt $(,)? )=>{ impl_number_of_digits!{@shared $This, $bits} impl PWrapper<$This> { /// Returns the absolute value of this integer, as the equivalent unsigned type. pub const fn unsigned_abs(self) -> $Unsigned { self.0 } #[doc(hidden)] pub const fn compute_display_len(self, _: FormattingFlags)-> usize { let mut n = self.0; let mut len = 1usize; impl_number_of_digits!(num number_of_digits;$bits n len) } } }; } impl_number_of_digits! {impl_either; signed , (i8, u8), 8} impl_number_of_digits! {impl_either; signed , (i16, u16), 16} impl_number_of_digits! {impl_either; signed , (i32, u32), 32} impl_number_of_digits! {impl_either; signed , (i64, u64), 64} impl_number_of_digits! {impl_either; signed , (i128, u128), 128} impl_number_of_digits! {impl_either; unsigned, (u8, u8), 8} impl_number_of_digits! {impl_either; unsigned, (u16, u16), 16} impl_number_of_digits! {impl_either; unsigned, (u32, u32), 32} impl_number_of_digits! {impl_either; unsigned, (u64, u64), 64} impl_number_of_digits! {impl_either; unsigned, (u128, u128), 128} #[cfg(target_pointer_width = "16")] type UWord = u16; #[cfg(target_pointer_width = "32")] type UWord = u32; #[cfg(target_pointer_width = "64")] type UWord = u64; #[cfg(target_pointer_width = "128")] type UWord = u128; #[cfg(target_pointer_width = "16")] type IWord = i16; #[cfg(target_pointer_width = "32")] type IWord = i32; #[cfg(target_pointer_width = "64")] type IWord = i64; #[cfg(target_pointer_width = "128")] type IWord = i128; macro_rules! impl_for_xsize { ($XSize:ident, $XWord:ident) => { impl PWrapper<$XSize> { /// Computes how long much space is necessary to write this integer as a literal. #[inline(always)] pub const fn compute_display_len(self, fmt: FormattingFlags) -> usize { PWrapper(self.0 as $XWord).compute_display_len(fmt) } /// Computes how long much space is necessary to write this integer as a literal. #[inline(always)] pub const fn compute_debug_len(self, fmt: FormattingFlags) -> usize { PWrapper(self.0 as $XWord).compute_debug_len(fmt) } /// Computes how long much space is necessary to /// write this integer as a hexadecimal literal. #[inline(always)] pub const fn hexadecimal_len(self, fmt: FormattingFlags) -> usize { PWrapper(self.0 as $XWord).hexadecimal_len(fmt) } /// Computes how long much space is necessary to /// write this integer as a binary literal. #[inline(always)] pub const fn binary_len(self, fmt: FormattingFlags) -> usize { PWrapper(self.0 as $XWord).binary_len(fmt) } } }; } impl_for_xsize! {usize, UWord} impl_for_xsize! {isize, IWord} impl PWrapper { /// Returns the absolute value of this integer. pub const fn unsigned_abs(self) -> usize { self.0 } } impl PWrapper { /// Returns the absolute value of this integer, as the equivalent unsigned type. pub const fn unsigned_abs(self) -> usize { self.0.wrapping_abs() as usize } } impl Integer { #[inline] const fn as_negative(self) -> i128 { (self.unsigned as i128).wrapping_neg() } } #[doc(hidden)] impl PWrapper { pub const fn to_start_array_binary(self, flags: FormattingFlags) -> StartAndArray<[u8; 130]> { let mut n = if self.0.is_negative { self.0.as_negative() as u128 } else { self.0.unsigned }; n &= *self.0.mask; let mut out = StartAndArray { start: 130, array: [0u8; 130], }; loop { out.start -= 1; let digit = (n & 1) as u8; out.array[out.start] = b'0' + digit; n >>= 1; if n == 0 { break; } } if flags.is_alternate() { out.start -= 1; out.array[out.start] = b'b'; out.start -= 1; out.array[out.start] = b'0'; } out } pub const fn to_start_array_hexadecimal( self, flags: FormattingFlags, ) -> StartAndArray<[u8; 34]> { let mut n = if self.0.is_negative { self.0.as_negative() as u128 } else { self.0.unsigned }; n &= *self.0.mask; let mut out = StartAndArray { start: 34, array: [0u8; 34], }; loop { out.start -= 1; let digit = (n & 0xF) as u8; out.array[out.start] = match digit { 0..=9 => b'0' + digit, _ => digit + flags.hex_fmt() as u8, }; n >>= 4; if n == 0 { break; } } if flags.is_alternate() { out.start -= 1; out.array[out.start] = b'x'; out.start -= 1; out.array[out.start] = b'0'; } out } pub const fn to_start_array_display(self) -> StartAndArray<[u8; 40]> { let mut out = StartAndArray { start: 40, array: [0u8; 40], }; let mut n = self.0.unsigned; loop { out.start -= 1; let digit = (n % 10) as u8; out.array[out.start] = b'0' + digit; n /= 10; if n == 0 { break; } } if self.0.is_negative { out.start -= 1; out.array[out.start] = b'-'; } out } #[inline(always)] pub const fn to_start_array_debug(self) -> StartAndArray<[u8; 40]> { self.to_start_array_display() } } impl PWrapper<&[u8]> { /// Computes how much space is necessary to write the wrapped `&[u8]` as a utf8 string, /// with debug formatting pub const fn compute_utf8_debug_len(self) -> usize { self.compute_utf8_debug_len_in_range(0..self.0.len()) } /// Computes how much space is necessary to write `&self.0[range]` as a utf8 string, /// with debug formatting pub const fn compute_utf8_debug_len_in_range(self, mut range: Range) -> usize { let mut sum = range.end - range.start; while range.start < range.end { let c = self.0[range.start]; if c < 128 { let shifted = 1 << c; if (FOR_ESCAPING.is_escaped & shifted) != 0 { sum += if (FOR_ESCAPING.is_backslash_escaped & shifted) == 0 { 3 // `\x01` only add 3 characters } else { 1 // Escaped with a backslash }; } } range.start += 1; } sum + 2 // The quote characters } } impl PWrapper<&str> { /// Computes how much space is necessary to write a `&str` with debug formatting #[inline(always)] #[doc(hidden)] pub const fn compute_debug_len(self, _: FormattingFlags) -> usize { PWrapper(self.0.as_bytes()).compute_utf8_debug_len() } /// Computes how much space is necessary to write a `&str` with display formatting #[inline(always)] #[doc(hidden)] pub const fn compute_display_len(self, _: FormattingFlags) -> usize { self.0.len() } } #[cfg(feature = "fmt")] const _: () = { use crate::marker_traits::{FormatMarker, IsNotStdKind}; impl

FormatMarker for PWrapper

{ type Kind = IsNotStdKind; type This = Self; } }; /////////////////////////////////////////////////////////////////////////// #[cfg(feature = "assertcp")] macro_rules! impl_eq_for_primitives { ( (l=$l:ident, r=$r:ident) $( impl[$($impl_:tt)*] $type:ty = $comparison:expr; )* ) => ( $( impl<$($impl_)*> PWrapper<$type> { /// This method is only available with the "assert" feature. pub const fn const_eq(&self, $r:&$type) -> bool { let $l = self.0; $comparison } } )* ) } #[cfg(feature = "assertcp")] impl_eq_for_primitives! { (l = l, r = r) impl[] u8 = l == *r; impl[] i8 = l == *r; impl[] u16 = l == *r; impl[] i16 = l == *r; impl[] u32 = l == *r; impl[] i32 = l == *r; impl[] u64 = l == *r; impl[] i64 = l == *r; impl[] u128 = l == *r; impl[] i128 = l == *r; impl[] usize = l == *r; impl[] isize = l == *r; impl[] bool = l == *r; impl[] char = l == *r; impl[] &str = crate::slice_cmp::str_eq(l, r); } const_format-0.2.32/src/wrapper_types/sliced.rs000064400000000000000000000131311046102023000177110ustar 00000000000000use crate::{ fmt::{Error, Formatter}, wrapper_types::AsciiStr, }; use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; /// Wrapper for writing a range of a string slice. /// /// This is a workaround for not being able to do `&string[start..end]` at compile-time. /// /// # Example /// /// ```rust /// #![feature(const_mut_refs)] /// /// use const_format::Sliced; /// use const_format::{concatc, formatc}; /// /// const NUMS: &str = "0123456789"; /// const SRC: &str = "foo bar baz"; /// /// assert_eq!(concatc!(Sliced(NUMS, 1..=4)), "1234"); /// assert_eq!(concatc!(Sliced(SRC, 0..5), "ros."), "foo bros."); /// /// assert_eq!(formatc!("{}", Sliced(NUMS, 4..)), "456789"); /// assert_eq!(formatc!("{}t", Sliced(SRC, 4..7)), "bart"); /// /// ``` #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] pub struct Sliced(pub T, pub R); impl_fmt! { impl['a,] Sliced<&'a str, Range>; impl['a,] Sliced<&'a str, RangeFrom>; impl['a,] Sliced<&'a str, RangeFull>; impl['a,] Sliced<&'a str, RangeInclusive>; impl['a,] Sliced<&'a str, RangeTo>; impl['a,] Sliced<&'a str, RangeToInclusive>; /// #[inline] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str_range_debug(self.0, self.range()) } /// #[inline] pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_str_range(self.0, self.range()) } } impl_fmt! { impl['a,] Sliced, Range>; impl['a,] Sliced, RangeFrom>; impl['a,] Sliced, RangeFull>; impl['a,] Sliced, RangeInclusive>; impl['a,] Sliced, RangeTo>; impl['a,] Sliced, RangeToInclusive>; /// #[inline] pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_ascii_range_debug(self.0, self.range()) } /// #[inline] pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { f.write_ascii_range(self.0, self.range()) } } impl Sliced> { #[inline(always)] const fn range(&self) -> Range { self.1.start..self.1.end } } impl Sliced> { #[inline(always)] const fn range(&self) -> Range { self.1.start..usize::MAX } } impl Sliced { #[inline(always)] const fn range(&self) -> Range { 0..usize::MAX } } const UM: usize = usize::MAX >> 1; impl Sliced> { // If people are somehow indexing over a thing that's larger than isize::MAX, // then we have problems. #[inline(always)] const fn range(&self) -> Range { *self.1.start()..(*self.1.end() & UM) + 1 } } impl Sliced> { #[inline(always)] const fn range(&self) -> Range { 0..self.1.end } } impl Sliced> { // If people are somehow indexing over a thing that's larger than isize::MAX, // then we have problems. #[inline(always)] const fn range(&self) -> Range { 0..(self.1.end & UM) + 1 } } #[cfg(test)] mod tests { use super::*; use crate::fmt::{FormattingFlags, StrWriter}; macro_rules! test_case { ( $writer:ident, $str_val:expr, $range:expr, $expected:expr ) => {{ const fn inner(fmt: &mut Formatter<'_>) -> Result<(), Error> { let string = Sliced($str_val, $range); try_!(string.const_display_fmt(fmt)); try_!(fmt.write_str(";")); try_!(string.const_debug_fmt(fmt)); Ok(()) } $writer.clear(); inner(&mut $writer.make_formatter(FormattingFlags::NEW)).unwrap(); assert_eq!($writer.as_str(), $expected, "range = {:?}", $range); }}; } const S: &str = "\x00\n\t3456789\x06\x07"; macro_rules! generate_test { ($str_val:expr) => { let writer: &mut StrWriter = &mut StrWriter::new([0u8; 512]); test_case!(writer, $str_val, 2..9, "\t345678;\"\\t345678\""); test_case!( writer, $str_val, 2.., "\t3456789\x06\x07;\"\\t3456789\\x06\\x07\"" ); test_case!( writer, $str_val, .., "\x00\n\t3456789\x06\x07;\"\\x00\\n\\t3456789\\x06\\x07\"" ); test_case!( writer, $str_val, ..9, "\x00\n\t345678;\"\\x00\\n\\t345678\"" ); test_case!(writer, $str_val, 2..=9, "\t3456789;\"\\t3456789\""); test_case!( writer, $str_val, 2..=!0, "\t3456789\x06\x07;\"\\t3456789\\x06\\x07\"" ); test_case!( writer, $str_val, ..=9, "\x00\n\t3456789;\"\\x00\\n\\t3456789\"" ); test_case!( writer, $str_val, ..=!0, "\x00\n\t3456789\x06\x07;\"\\x00\\n\\t3456789\\x06\\x07\"" ); }; } #[test] fn str_tests() { generate_test!(S); } #[test] fn asciistr_tests() { const ASCII: AsciiStr<'_> = ascii_str!(S); generate_test!(ASCII); } } const_format-0.2.32/src/wrapper_types.rs000064400000000000000000000006401046102023000164470ustar 00000000000000//! Some wrapper types. //! //! # Features //! //! This module is only exported with the "fmt" feature. #[cfg(feature = "fmt")] pub(crate) mod ascii_str; pub(crate) mod pwrapper; #[cfg(feature = "fmt")] pub(crate) mod sliced; #[cfg(feature = "fmt")] pub use self::ascii_str::NotAsciiError; #[doc(no_inline)] #[cfg(feature = "fmt")] pub use crate::{AsciiStr, Sliced}; #[doc(no_inline)] pub use crate::PWrapper;