├── .gitignore ├── Cargo.toml ├── const_format ├── tests │ ├── misc_tests │ │ ├── clippy_warnings.rs │ │ ├── concatc_macro_tests.rs │ │ ├── type_kind_coercion_macro_tests.rs │ │ ├── call_debug_fmt_macro.rs │ │ ├── inline_const_pattern_tests.rs │ │ ├── assertcp_tests.rs │ │ ├── assertc_tests.rs │ │ ├── shared_cp_macro_tests.rs │ │ ├── equality_tests.rs │ │ ├── derive_tests.rs │ │ └── writec_macro.rs │ ├── str_methods.rs │ ├── fmt_tests_modules.rs │ ├── misc_tests_modules.rs │ ├── str_methods_modules │ │ ├── conv_ascii_case.rs │ │ ├── str_replace.rs │ │ ├── str_splice.rs │ │ └── str_split_tests.rs │ └── fmt_tests │ │ ├── str_writer_mut.rs │ │ ├── std_impl_tests.rs │ │ └── display_formatting.rs ├── src │ ├── marker_traits.rs │ ├── wrapper_types.rs │ ├── for_assert_macros.rs │ ├── __hidden_utils.rs │ ├── const_generic_concatcp.rs │ ├── __str_methods │ │ ├── str_repeat.rs │ │ ├── pattern.rs │ │ ├── str_splice.rs │ │ ├── str_replace.rs │ │ ├── str_split.rs │ │ └── str_indexing.rs │ ├── macros │ │ ├── constructors.rs │ │ ├── assertions.rs │ │ ├── map_ascii_case.rs │ │ └── helper_macros.rs │ ├── for_examples.rs │ ├── __str_methods.rs │ ├── test_utils.rs │ ├── msg.rs │ ├── fmt │ │ ├── error.rs │ │ └── std_type_impls │ │ │ └── ranges.rs │ ├── char_encoding │ │ └── tests.rs │ ├── slice_cmp.rs │ ├── char_encoding.rs │ ├── doctests.rs │ ├── __ascii_case_conv │ │ └── word_iterator.rs │ ├── equality.rs │ ├── __ascii_case_conv.rs │ ├── pargument.rs │ └── utils.rs ├── LICENSE-ZLIB.md └── Cargo.toml ├── print_errors ├── src │ ├── lib.rs │ ├── using_assertc_macros.rs │ ├── using_writec_macro.rs │ └── formatc_macros.rs └── Cargo.toml ├── print_warnings ├── Cargo.toml └── src │ └── main.rs ├── commit.sh ├── const_format_proc_macros ├── src │ ├── spanned.rs │ ├── derive_debug │ │ ├── syntax.rs │ │ └── type_detection.rs │ ├── format_str.rs │ ├── test_utils.rs │ ├── macros.rs │ ├── respan_to_macro.rs │ ├── shared_arg_parsing.rs │ ├── format_macro │ │ └── tests.rs │ ├── datastructure │ │ └── field_map.rs │ ├── error.rs │ ├── format_str │ │ └── errors.rs │ ├── lib.rs │ ├── formatting.rs │ ├── format_args.rs │ └── utils.rs ├── LICENSE-ZLIB.md └── Cargo.toml ├── LICENSE-ZLIB.md └── .github └── workflows └── rust.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | **/*.7z 4 | Cargo.lock 5 | __ignored__* -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members=[ 3 | "const_format", 4 | "const_format_proc_macros", 5 | "print_errors", 6 | "print_warnings", 7 | ] 8 | resolver = "2" -------------------------------------------------------------------------------- /const_format/tests/misc_tests/clippy_warnings.rs: -------------------------------------------------------------------------------- 1 | #[deny(clippy::double_parens)] 2 | #[test] 3 | fn test_clippy_double_parens_not_triggered() { 4 | std::convert::identity(cfmt_b::formatcp!("hello")); 5 | } 6 | -------------------------------------------------------------------------------- /const_format/tests/str_methods.rs: -------------------------------------------------------------------------------- 1 | mod str_methods_modules { 2 | mod conv_ascii_case; 3 | 4 | mod str_replace; 5 | 6 | mod str_splice; 7 | 8 | #[cfg(feature = "rust_1_64")] 9 | mod str_split_tests; 10 | } 11 | -------------------------------------------------------------------------------- /print_errors/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | mod formatc_macros; 4 | 5 | const _: &str = cfmt::concatcp!(0, 1, ()); 6 | 7 | const _: &str = cfmt::concatc!(0, 1, ()); 8 | 9 | mod using_assertc_macros; 10 | 11 | mod using_writec_macro; 12 | -------------------------------------------------------------------------------- /print_warnings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "print_warnings" 3 | version = "0.1.0" 4 | authors = ["rodrimati1992 "] 5 | edition = "2018" 6 | 7 | [dependencies.const_format] 8 | path = "../const_format/" 9 | features = ["assertc", "assertcp", "rust_1_64"] -------------------------------------------------------------------------------- /print_errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "print_errors" 3 | version = "0.1.0" 4 | authors = ["rodrimati1992 "] 5 | edition = "2018" 6 | 7 | [dependencies.cfmt] 8 | path = "../const_format/" 9 | package = "const_format" 10 | features = ["assertc"] -------------------------------------------------------------------------------- /commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # You can use this script to format and commit the code all at once 5 | # 6 | # 7 | 8 | cargo fmt 9 | 10 | if [ $? -eq 0 ] 11 | then 12 | echo "ran cargo fmt!!!!" 13 | else 14 | exit 1 15 | fi 16 | 17 | 18 | git update-index --again 19 | 20 | git commit -------------------------------------------------------------------------------- /const_format_proc_macros/src/spanned.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | 3 | #[derive(Copy, Clone)] 4 | pub struct Spans { 5 | pub start: Span, 6 | pub end: Span, 7 | } 8 | 9 | impl Spans { 10 | pub fn joined(self) -> Span { 11 | self.start.join(self.end).unwrap_or(self.start) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /const_format/src/marker_traits.rs: -------------------------------------------------------------------------------- 1 | //! Marker traits for types that can be formatted and/or be written to. 2 | //! 3 | //! # Features 4 | //! 5 | //! This module is only exported with the "fmt" feature 6 | 7 | mod format_marker; 8 | mod write_marker; 9 | 10 | #[doc(inline)] 11 | pub use self::{ 12 | format_marker::{FormatMarker, IsAFormatMarker, IsArrayKind, IsNotStdKind, IsStdKind}, 13 | write_marker::{IsAStrWriter, IsAWriteMarker, IsNotAStrWriter, WriteMarker}, 14 | }; 15 | -------------------------------------------------------------------------------- /print_errors/src/using_assertc_macros.rs: -------------------------------------------------------------------------------- 1 | use cfmt::{assertc, assertc_eq, assertc_ne}; 2 | 3 | // uninferred argument 4 | assertc!(false, "{}", 0); 5 | 6 | assertc!(true, "{}"); 7 | 8 | assertc!(true, "{}", foo = "", 100u8); 9 | 10 | assertc_eq!(0u8, [0u8][10]); 11 | 12 | assertc_eq!(0, 0, "{}", 0u8); 13 | 14 | assertc_eq!((), (), "{}"); 15 | 16 | assertc_eq!((), (), "{}", foo = "", 100u8); 17 | 18 | assertc_eq!(0u8, 1u8, "{}", 0); 19 | 20 | assertc_eq!(0u8, 1u8, "{}", 0u8); 21 | 22 | assertc!(2 + 2 == 5, "{}", 0u8); 23 | -------------------------------------------------------------------------------- /const_format/src/wrapper_types.rs: -------------------------------------------------------------------------------- 1 | //! Some wrapper types. 2 | //! 3 | //! # Features 4 | //! 5 | //! This module is only exported with the "fmt" feature. 6 | 7 | #[cfg(feature = "fmt")] 8 | pub(crate) mod ascii_str; 9 | 10 | pub(crate) mod pwrapper; 11 | 12 | #[cfg(feature = "fmt")] 13 | pub(crate) mod sliced; 14 | 15 | #[cfg(feature = "fmt")] 16 | pub use self::ascii_str::NotAsciiError; 17 | 18 | #[doc(no_inline)] 19 | #[cfg(feature = "fmt")] 20 | pub use crate::{AsciiStr, Sliced}; 21 | 22 | #[doc(no_inline)] 23 | pub use crate::PWrapper; 24 | -------------------------------------------------------------------------------- /print_errors/src/using_writec_macro.rs: -------------------------------------------------------------------------------- 1 | use cfmt::{writec, StrWriter}; 2 | 3 | fn using_writec(writer: &mut StrWriter) -> cfmt::Result { 4 | // Trying to write to a non-writer 5 | writec!((), "")?; 6 | 7 | writec!(writer, "{}")?; 8 | 9 | writec!(writer, "{}", foo = "", 100u8)?; 10 | 11 | // trying to write an uninferred integer type 12 | writec!(writer, "{}", 0)?; 13 | 14 | let a = 0; 15 | writec!(writer, "{}", a)?; 16 | writec!(writer, "{b}", b = a)?; 17 | writec!(writer, "{a}")?; 18 | writec!(writer, "{a:?}")?; 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /const_format/src/for_assert_macros.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_fmt_panics)] 2 | 3 | use crate::pargument::PArgument; 4 | 5 | #[track_caller] 6 | pub const fn assert_(cond: bool, message: &'static str) { 7 | if cond { 8 | panic!("{}", message) 9 | } 10 | } 11 | 12 | // The `T` type parameter is there just so that the PARGUMENTS associated constant 13 | // is evaluated lazily. 14 | pub trait ConcatArgsIf { 15 | const PARGUMENTS: &'static [PArgument]; 16 | } 17 | 18 | impl ConcatArgsIf for S { 19 | const PARGUMENTS: &'static [PArgument] = &[]; 20 | } 21 | -------------------------------------------------------------------------------- /print_errors/src/formatc_macros.rs: -------------------------------------------------------------------------------- 1 | use cfmt::formatcp; 2 | 3 | use cfmt::formatc; 4 | 5 | const _: &str = formatcp!("{}"); 6 | 7 | const _: &str = formatcp!("{}", foo = "", 100u8 + 0); 8 | 9 | const _: &str = formatcp!("{}", 0 + 0); 10 | 11 | const _: &str = formatcp!("{}", 0u8, 0u8 + 1); 12 | 13 | const _: &str = formatcp!("{}", |fmt| 0 + 0); 14 | 15 | const _: () = { 16 | const _: &str = formatc!("{}"); 17 | 18 | const _: &str = formatc!("{}", foo = "", 100u8 + 0); 19 | 20 | const _: &str = formatc!("{}", 0 + 0); 21 | 22 | const _: &str = formatc!("{}", { 23 | let a = 0; 24 | let b = 0; 25 | a + b 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/derive_debug/syntax.rs: -------------------------------------------------------------------------------- 1 | use syn::{ 2 | parse::{Parse, ParseStream}, 3 | Generics, 4 | }; 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | pub(crate) struct ImplHeader { 9 | pub(crate) generics: Generics, 10 | pub(crate) self_ty: syn::Path, 11 | } 12 | 13 | impl Parse for ImplHeader { 14 | fn parse(input: ParseStream) -> Result { 15 | let mut generics = input.parse::()?; 16 | 17 | let self_ty = input.parse()?; 18 | 19 | generics.where_clause = input.parse()?; 20 | 21 | Ok(Self { generics, self_ty }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /const_format/src/__hidden_utils.rs: -------------------------------------------------------------------------------- 1 | pub(crate) const fn max_usize(l: usize, r: usize) -> usize { 2 | if l > r { 3 | l 4 | } else { 5 | r 6 | } 7 | } 8 | pub(crate) const fn saturating_add(l: usize, r: usize) -> usize { 9 | let (sum, overflowed) = l.overflowing_add(r); 10 | if overflowed { 11 | usize::MAX 12 | } else { 13 | sum 14 | } 15 | } 16 | 17 | pub(crate) const fn is_char_boundary_no_len_check(str: &[u8], index: usize) -> bool { 18 | index == str.len() || (str[index] as i8) >= -0x40 19 | } 20 | 21 | #[repr(C)] 22 | pub union PtrToRef<'a, T: ?Sized> { 23 | pub ptr: *const T, 24 | pub reff: &'a T, 25 | } 26 | -------------------------------------------------------------------------------- /const_format/src/const_generic_concatcp.rs: -------------------------------------------------------------------------------- 1 | //! Reimplements some stuff from concatcp to be const generic instead of macro generated 2 | 3 | use crate::pmr::{LenAndArray, PArgument, PVariant}; 4 | 5 | #[doc(hidden)] 6 | pub const fn __priv_concatenate(input: &[PArgument]) -> LenAndArray<[u8; LEN]> { 7 | let mut out = LenAndArray { 8 | len: 0, 9 | array: [0u8; LEN], 10 | }; 11 | 12 | crate::__for_range! { outer_i in 0..input.len() => 13 | let current = &input[outer_i]; 14 | 15 | match current.elem { 16 | PVariant::Str(s) => crate::__write_pvariant!(str, current, s => out), 17 | PVariant::Int(int) => crate::__write_pvariant!(int, current, int => out), 18 | PVariant::Char(c) => crate::__write_pvariant!(char, current, c => out), 19 | } 20 | } 21 | 22 | out 23 | } 24 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/format_str.rs: -------------------------------------------------------------------------------- 1 | use crate::{formatting::FormattingFlags, parse_utils::StrRawness}; 2 | 3 | mod errors; 4 | 5 | mod parsing; 6 | 7 | #[cfg(test)] 8 | mod tests; 9 | 10 | pub(crate) use self::errors::{ParseError, ParseErrorKind}; 11 | 12 | #[derive(Debug, PartialEq)] 13 | pub(crate) struct FormatStr { 14 | pub(crate) list: Vec, 15 | } 16 | 17 | #[derive(Debug, PartialEq)] 18 | pub(crate) enum FmtStrComponent { 19 | Str(String, StrRawness), 20 | Arg(FmtArg), 21 | } 22 | 23 | /// An argument in the format string eg: `"{foo:?}"` 24 | #[derive(Debug, PartialEq)] 25 | pub(crate) struct FmtArg { 26 | pub(crate) which_arg: WhichArg, 27 | pub(crate) formatting: FormattingFlags, 28 | pub(crate) rawness: StrRawness, 29 | } 30 | 31 | #[derive(Debug, PartialEq)] 32 | pub(crate) enum WhichArg { 33 | Ident(String), 34 | Positional(Option), 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Matias Rodriguez. 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /const_format/LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Matias Rodriguez. 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /const_format/tests/misc_tests/concatc_macro_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::Formatter; 2 | use cfmt_b::{ascii_str, concatc, impl_fmt, try_}; 3 | 4 | use std::num::NonZeroUsize; 5 | 6 | const STD_TYPES: &str = concatc!( 7 | r#"\\\-hello-\\\"#, 8 | 100u8, 9 | false, 10 | true, 11 | match NonZeroUsize::new(34) { 12 | Some(x) => x, 13 | None => loop {}, 14 | }, 15 | ); 16 | 17 | const USER_TYPES: &str = concatc!(Twice("hello "), ascii_str!("world!")); 18 | 19 | #[test] 20 | fn concatc_test() { 21 | assert_eq!(STD_TYPES, r#"\\\-hello-\\\100falsetrue34"#); 22 | assert_eq!(USER_TYPES, r#"hello hello world!"#); 23 | } 24 | 25 | struct Twice(&'static str); 26 | 27 | impl_fmt! { 28 | impl Twice; 29 | 30 | const fn const_display_fmt(&self, fmt:&mut Formatter<'_>) -> cfmt_b::Result { 31 | try_!(fmt.write_str(self.0)); 32 | try_!(fmt.write_str(self.0)); 33 | Ok(()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /const_format_proc_macros/LICENSE-ZLIB.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Matias Rodriguez. 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /const_format_proc_macros/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | pub trait StrExt { 2 | fn as_str(&self) -> &str; 3 | 4 | /// Checks that these needles exist consequtively in self. 5 | /// 6 | /// Example: `"hello world".consecutive_in_set(&["he", "wor"])` returns `true`. 7 | /// Example: `"hello world".consecutive_in_set(&["wor", "he"])` returns `false`. 8 | fn consecutive_in_self(&self, needles: &[&str]) -> bool { 9 | let mut rem = self.as_str(); 10 | for needle in needles { 11 | rem = match rem.find(needle) { 12 | Some(next) => &rem[next + needle.len()..], 13 | None => return false, 14 | }; 15 | } 16 | true 17 | } 18 | } 19 | 20 | impl StrExt for str { 21 | #[inline(always)] 22 | fn as_str(&self) -> &str { 23 | self 24 | } 25 | } 26 | 27 | impl StrExt for String { 28 | #[inline(always)] 29 | fn as_str(&self) -> &str { 30 | self 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /const_format/tests/fmt_tests_modules.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "fmt")] 2 | 3 | // Prevents importing from const_format, requiring importing from cfmt_b. 4 | extern crate const_format as cfmt_a; 5 | extern crate self as const_format; 6 | 7 | // Making sure that `const_format` points at this test crate. 8 | pub const NOT_CF: usize = 13; 9 | pub const _ASSERT_NOT_CF: [(); 13] = [(); const_format::NOT_CF]; 10 | 11 | cfmt_a::__declare_rng_ext! {} 12 | 13 | mod fmt_tests { 14 | #[cfg(not(feature = "__only_new_tests"))] 15 | mod display_formatting; 16 | 17 | #[cfg(not(feature = "__only_new_tests"))] 18 | mod formatted_writing; 19 | 20 | #[cfg(not(feature = "__only_new_tests"))] 21 | mod formatter_methods; 22 | 23 | #[cfg(not(feature = "__only_new_tests"))] 24 | mod std_impl_tests; 25 | 26 | #[cfg(not(feature = "__only_new_tests"))] 27 | mod str_writer_methods; 28 | 29 | #[cfg(not(feature = "__only_new_tests"))] 30 | mod str_writer_mut; 31 | } 32 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/str_repeat.rs: -------------------------------------------------------------------------------- 1 | pub struct StrRepeatArgs { 2 | pub str: &'static str, 3 | pub str_len: usize, 4 | pub out_len: usize, 5 | pub overflowed_len: Option, 6 | pub repeat: usize, 7 | } 8 | 9 | #[allow(non_snake_case)] 10 | pub const fn StrRepeatArgs(str: &'static str, repeat: usize) -> StrRepeatArgs { 11 | let str_len = str.len(); 12 | let (mul, overflowed) = str_len.overflowing_mul(repeat); 13 | 14 | let (out_len, overflowed_len, repeat) = if overflowed { 15 | (str_len, Some(mul), 1) 16 | } else { 17 | (mul, None, repeat) 18 | }; 19 | 20 | StrRepeatArgs { 21 | str, 22 | str_len, 23 | out_len, 24 | overflowed_len, 25 | repeat, 26 | } 27 | } 28 | 29 | impl StrRepeatArgs { 30 | pub const fn assert_valid(&self) { 31 | if let Some(overflowed_len) = self.overflowed_len { 32 | [/* the returned string is too large */][overflowed_len] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /const_format_proc_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const_format_proc_macros" 3 | version = "0.2.34" 4 | authors = ["rodrimati1992 "] 5 | rust-version = "1.57.0" 6 | edition = "2021" 7 | license = "Zlib" 8 | description = "Implementation detail of the `const_format` crate" 9 | keywords = ["no-std", "format", "concat"] 10 | categories = ["no-std", "text-processing"] 11 | repository = "https://github.com/rodrimati1992/const_format_crates/" 12 | include = [ 13 | "Cargo.toml", 14 | "src/**/*.rs", 15 | "../README.md", 16 | "LICENSE-ZLIB.md", 17 | ] 18 | 19 | [lib] 20 | proc-macro = true 21 | 22 | [features] 23 | default = [] 24 | derive = ["syn", "syn/derive", "syn/printing"] 25 | debug = ["syn/extra-traits"] 26 | all = ["derive"] 27 | 28 | [dependencies] 29 | quote = "1.0.7" 30 | proc-macro2 = "1.0.19" 31 | unicode-xid = "0.2" 32 | 33 | [dependencies.syn] 34 | version = "1.0.38" 35 | optional = true 36 | default-features = false 37 | features = ["parsing", "proc-macro"] 38 | 39 | [dev-dependencies] 40 | fastrand = "1.3.4" 41 | 42 | 43 | [package.metadata.docs.rs] 44 | rustc-args = ["--cfg", "feature = \"all\""] 45 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/macros.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | #[doc(hidden)] 4 | macro_rules! to_stream { 5 | ( $stream:ident ; $($expr:expr),* $(,)* ) => {{ 6 | // use quote::TokenStreamExt; 7 | 8 | $( $expr.to_tokens($stream); )* 9 | }} 10 | } 11 | 12 | #[doc(hidden)] 13 | macro_rules! spanned_err { 14 | ( $e:expr, $($fmt:tt)* ) => ({ 15 | $crate::utils::spanned_err( 16 | &$e, 17 | &format!($($fmt)*), 18 | ) 19 | }) 20 | } 21 | 22 | #[doc(hidden)] 23 | macro_rules! return_spanned_err { 24 | ( $e:expr, $($fmt:tt)* ) => ({ 25 | return Err($crate::utils::spanned_err( 26 | &$e, 27 | &format!($($fmt)*), 28 | )) 29 | }) 30 | } 31 | 32 | #[doc(hidden)] 33 | macro_rules! syn_err { 34 | ( $span:expr, $($fmt:tt)* ) => ({ 35 | $crate::utils::syn_err( 36 | $span, 37 | &format!($($fmt)*), 38 | ) 39 | }) 40 | } 41 | 42 | #[doc(hidden)] 43 | macro_rules! return_syn_err { 44 | ( $span:expr, $($fmt:tt)* ) => ({ 45 | return Err($crate::utils::syn_err( 46 | $span, 47 | &format!($($fmt)*), 48 | )) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/respan_to_macro.rs: -------------------------------------------------------------------------------- 1 | use crate::parse_utils::TokenTreeExt; 2 | 3 | use proc_macro2::{Delimiter, Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; 4 | 5 | const MSG: &str = "Expected the macro to be called as `respan_to!((tokens) more tokens)`"; 6 | 7 | fn parse_paren(tt: TokenTree2) -> TokenStream2 { 8 | match tt { 9 | TokenTree2::Group(group) if group.delimiter() == Delimiter::Parenthesis => group.stream(), 10 | _ => panic!("{}", MSG), 11 | } 12 | } 13 | 14 | fn get_span(ts: TokenStream2) -> Span { 15 | let mut iter = ts.into_iter(); 16 | 17 | match iter.next() { 18 | Some(TokenTree2::Group(group)) if group.delimiter() == Delimiter::None => { 19 | get_span(group.stream()) 20 | } 21 | Some(first_tt) => { 22 | let mut span = first_tt.span(); 23 | 24 | for tt in iter { 25 | span = span.join(tt.span()).unwrap_or(span); 26 | } 27 | span 28 | } 29 | None => Span::mixed_site(), 30 | } 31 | } 32 | 33 | pub(crate) fn implementation(ts: TokenStream2) -> TokenStream2 { 34 | let mut iter = ts.into_iter(); 35 | 36 | let span_to = get_span(parse_paren(iter.next().expect(MSG))); 37 | 38 | iter.map(|tt| tt.set_span_recursive(span_to)).collect() 39 | } 40 | -------------------------------------------------------------------------------- /const_format/src/macros/constructors.rs: -------------------------------------------------------------------------------- 1 | /// Constructs an [`AsciiStr`] constant from an ascii string, 2 | /// 3 | /// # Compile-time errors 4 | /// 5 | /// This macro produces a compile-time error by indexing an empty array with 6 | /// the index of the first non-ascii byte. 7 | /// 8 | /// # Example 9 | /// 10 | /// ```rust 11 | /// use const_format::ascii_str; 12 | /// 13 | /// let fooo = ascii_str!("hello"); 14 | /// 15 | /// assert_eq!(fooo.as_str(), "hello"); 16 | /// 17 | /// // You can pass constants as arguments! 18 | /// const BAR_S: &str = "world"; 19 | /// let bar = ascii_str!(BAR_S); 20 | /// 21 | /// assert_eq!(bar.as_str(), "world"); 22 | /// 23 | /// ``` 24 | /// 25 | /// ```compile_fail 26 | /// use const_format::ascii_str; 27 | /// 28 | /// let fooo = ascii_str!("Γειά σου Κόσμε!"); 29 | /// 30 | /// ``` 31 | /// 32 | /// [`AsciiStr`]: ./struct.AsciiStr.html 33 | /// 34 | #[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))] 35 | #[cfg(feature = "fmt")] 36 | #[macro_export] 37 | macro_rules! ascii_str { 38 | ($str:expr $(,)*) => {{ 39 | const __CF_ASCII_STR_CONSTANT: $crate::AsciiStr<'static> = { 40 | match $crate::AsciiStr::new($str.as_bytes()) { 41 | Ok(x) => x, 42 | $crate::pmr::Err(e) => [][e.invalid_from], 43 | } 44 | }; 45 | __CF_ASCII_STR_CONSTANT 46 | }}; 47 | } 48 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/type_kind_coercion_macro_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::fmt::{Error, Formatter, FormattingFlags, StrWriter}; 2 | use cfmt_b::{coerce_to_fmt, impl_fmt}; 3 | 4 | #[test] 5 | fn coercion() { 6 | let writer: &mut StrWriter = &mut StrWriter::new([0; 512]); 7 | 8 | let flags = FormattingFlags::NEW; 9 | 10 | writer.clear(); 11 | coerce_to_fmt!(&100u8) 12 | .const_debug_fmt(&mut writer.make_formatter(flags)) 13 | .unwrap(); 14 | assert_eq!(writer.as_str(), "100"); 15 | 16 | writer.clear(); 17 | coerce_to_fmt!(&UnitStruct) 18 | .const_debug_fmt(&mut writer.make_formatter(flags)) 19 | .unwrap(); 20 | assert_eq!(writer.as_str(), "UnitStruct"); 21 | 22 | writer.clear(); 23 | let array = [0u8, 1, 2, 3]; 24 | coerce_to_fmt!(&&&&&array) 25 | .const_debug_fmt(&mut writer.make_formatter(flags)) 26 | .unwrap(); 27 | assert_eq!(writer.as_str(), "[0, 1, 2, 3]"); 28 | 29 | writer.clear(); 30 | let array = [0u8, 1, 2, 3]; 31 | coerce_to_fmt!(&&&array[..]) 32 | .const_debug_fmt(&mut writer.make_formatter(flags)) 33 | .unwrap(); 34 | assert_eq!(writer.as_str(), "[0, 1, 2, 3]"); 35 | } 36 | 37 | struct UnitStruct; 38 | 39 | impl_fmt! { 40 | impl[] UnitStruct; 41 | 42 | const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 43 | f.write_str("UnitStruct") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /print_warnings/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(non_camel_case_types)] 2 | 3 | use const_format::{assertcp, concatcp, formatcp}; 4 | 5 | pub mod rust_1_83 { 6 | use const_format::{ 7 | assertc, assertc_eq, assertc_ne, concatc, for_examples::Unit, formatc, writec, StrWriter, 8 | StrWriterMut, 9 | }; 10 | 11 | pub const TWO: u32 = 2; 12 | pub const TEN: u32 = 10; 13 | 14 | assertc!(TWO != TEN, "{} != {}", TWO, TEN); 15 | assertc_eq!(TWO, TWO); 16 | assertc_ne!(TWO, TEN); 17 | 18 | pub const CONCATC_A: &str = concatc!("hello", "world"); 19 | pub const CONCATC_B: &str = concatc!(10u8, TWO); 20 | 21 | pub const FORMATC_A: &str = formatc!("{}hello{}{:?}", "foo", 100u8, Unit); 22 | 23 | const fn as_str_ctor() -> StrWriter<[u8; 100]> { 24 | let mut writer = StrWriter::new([0; 100]); 25 | 26 | let _ = writec!(writer, "{:#?}", Unit); 27 | { 28 | let mut writer = StrWriterMut::new(&mut writer); 29 | 30 | let _ = writec!(writer, "{0}{0:?}", 100u8); 31 | } 32 | writer 33 | } 34 | 35 | pub const __AS_STR: &StrWriter = &as_str_ctor(); 36 | pub const AS_STR: &str = __AS_STR.as_str_alt(); 37 | } 38 | 39 | pub const CONCATCP_A: &str = concatcp!("hello", "world"); 40 | pub const CONCATCP_B: &str = concatcp!(10u8, 20u8); 41 | 42 | pub const FORMATCP_A: &str = formatcp!("{}hello{:x?}", "foo", 100u8); 43 | 44 | assertcp! {1 == 1, "what the {}", "F"} 45 | 46 | fn main() {} 47 | -------------------------------------------------------------------------------- /const_format/src/for_examples.rs: -------------------------------------------------------------------------------- 1 | //! Types for the documentation examples. 2 | //! 3 | //! # Features 4 | //! 5 | //! This module is only exported with the "fmt" feature 6 | 7 | use crate::{impl_fmt, try_, Error, Formatter, PWrapper}; 8 | 9 | /// An example struct which implements const debug formatting. 10 | #[derive(Debug, Copy, Clone)] 11 | pub struct Point3 { 12 | /// 13 | pub x: u32, 14 | /// 15 | pub y: u32, 16 | /// 17 | pub z: u32, 18 | } 19 | 20 | impl_fmt! { 21 | impl Point3; 22 | 23 | /// 24 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 25 | let mut f = f.debug_struct("Point3"); 26 | try_!(PWrapper(self.x).const_debug_fmt(f.field("x"))); 27 | try_!(PWrapper(self.y).const_debug_fmt(f.field("y"))); 28 | try_!(PWrapper(self.z).const_debug_fmt(f.field("z"))); 29 | f.finish() 30 | } 31 | 32 | /// 33 | pub const fn const_eq(&self, other: &Self) -> bool { 34 | self.x == other.x && 35 | self.y == other.y && 36 | self.z == other.z 37 | } 38 | } 39 | 40 | /// An example unit struct which implements const debug formatting. 41 | #[derive(Debug, Copy, Clone)] 42 | pub struct Unit; 43 | 44 | impl_fmt! { 45 | impl Unit; 46 | 47 | /// 48 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 49 | f.debug_struct("Unit").finish() 50 | } 51 | 52 | /// 53 | pub const fn const_eq(&self, _other: &Self) -> bool { 54 | true 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/pattern.rs: -------------------------------------------------------------------------------- 1 | use super::AsciiByte; 2 | 3 | pub(crate) struct PatternCtor(pub(crate) T); 4 | 5 | impl PatternCtor { 6 | pub(crate) const fn conv(self) -> Pattern { 7 | Pattern::AsciiByte(AsciiByte::new(self.0)) 8 | } 9 | } 10 | 11 | impl PatternCtor<&'static str> { 12 | pub(crate) const fn conv(self) -> Pattern { 13 | if let [b @ 0..=127] = *self.0.as_bytes() { 14 | Pattern::AsciiByte(AsciiByte::new(b)) 15 | } else { 16 | Pattern::Str(self.0) 17 | } 18 | } 19 | } 20 | 21 | impl PatternCtor { 22 | pub(crate) const fn conv(self) -> Pattern { 23 | let code = self.0 as u32; 24 | if let c @ 0..=127 = code { 25 | Pattern::AsciiByte(AsciiByte::new(c as u8)) 26 | } else { 27 | Pattern::Char(crate::char_encoding::char_to_display(self.0)) 28 | } 29 | } 30 | } 31 | 32 | #[derive(Copy, Clone)] 33 | pub(crate) enum Pattern { 34 | AsciiByte(AsciiByte), 35 | Str(&'static str), 36 | Char(crate::char_encoding::FmtChar), 37 | } 38 | 39 | pub(crate) enum PatternNorm<'a> { 40 | AsciiByte(AsciiByte), 41 | Str(&'a [u8]), 42 | } 43 | 44 | impl Pattern { 45 | pub(crate) const fn normalize(&self) -> PatternNorm<'_> { 46 | match self { 47 | Pattern::AsciiByte(ab) => PatternNorm::AsciiByte(*ab), 48 | Pattern::Str(str) => PatternNorm::Str(str.as_bytes()), 49 | Pattern::Char(char) => PatternNorm::Str(char.as_bytes()), 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /const_format/src/macros/assertions.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "assertc")] 2 | mod assertc_macros; 3 | 4 | #[cfg(feature = "assertcp")] 5 | mod assertcp_macros; 6 | 7 | #[doc(hidden)] 8 | #[macro_export] 9 | macro_rules! __assertc_inner { 10 | ( 11 | $fmt_macro:ident 12 | ($($parameters:tt)*) 13 | ($cond:expr $(, $fmt_literal:expr $(,$fmt_arg:expr)*)? $(,)?) 14 | ) => { 15 | #[allow(non_snake_case)] 16 | const _: () = { 17 | use $crate::__cf_osRcTFl4A; 18 | 19 | $crate::__assertc_common!{ 20 | $fmt_macro 21 | ($($parameters)*) 22 | ($cond) 23 | ( 24 | concat!( 25 | "\nassertion failed.\n", 26 | $($fmt_literal,)? 27 | "\n", 28 | ) 29 | $($(,$fmt_arg)*)? 30 | ) 31 | } 32 | }; 33 | } 34 | } 35 | 36 | #[doc(hidden)] 37 | #[macro_export] 38 | macro_rules! __assertc_common { 39 | ( 40 | $fmt_macro:ident 41 | ($($span:tt)*) 42 | ($cond:expr) 43 | ($($fmt_literal:expr $(,$fmt_arg:expr)*)?) 44 | ) => ( 45 | const PANIC_IF_TRUE_NHPMWYD3NJA: bool = !($cond); 46 | 47 | const MSG_NHPMWYD3NJA: &str = $crate::pmr::$fmt_macro!( 48 | (PANIC_IF_TRUE_NHPMWYD3NJA) 49 | ($($fmt_literal,)?), 50 | $($($fmt_arg,)*)? 51 | ); 52 | 53 | __cf_osRcTFl4A::pmr::respan_to!{ 54 | ($($span)*) 55 | __cf_osRcTFl4A::pmr::assert_(PANIC_IF_TRUE_NHPMWYD3NJA, MSG_NHPMWYD3NJA) 56 | } 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests_modules.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "__inline_const_pat_tests", feature(inline_const_pat))] 2 | 3 | extern crate const_format as cfmt_b; 4 | extern crate self as const_format; 5 | 6 | // Making sure that `const_format` points at this test crate. 7 | pub const NOT_CF: usize = 13; 8 | pub const _ASSERT_NOT_CF: [(); 13] = [(); const_format::NOT_CF]; 9 | 10 | mod misc_tests { 11 | #[cfg(feature = "assertc")] 12 | #[cfg(not(feature = "__only_new_tests"))] 13 | mod assertc_tests; 14 | 15 | mod clippy_warnings; 16 | 17 | #[cfg(feature = "assertcp")] 18 | mod assertcp_tests; 19 | 20 | #[cfg(feature = "fmt")] 21 | #[cfg(not(feature = "__only_new_tests"))] 22 | mod call_debug_fmt_macro; 23 | 24 | #[cfg(feature = "fmt")] 25 | #[cfg(not(feature = "__only_new_tests"))] 26 | mod concatc_macro_tests; 27 | 28 | #[cfg(feature = "derive")] 29 | #[cfg(not(feature = "__only_new_tests"))] 30 | mod derive_tests; 31 | 32 | #[cfg(feature = "assertc")] 33 | #[cfg(not(feature = "__only_new_tests"))] 34 | mod equality_tests; 35 | 36 | #[cfg(not(feature = "__only_new_tests"))] 37 | mod formatc_macros; 38 | 39 | #[cfg(feature = "fmt")] 40 | #[cfg(not(feature = "__only_new_tests"))] 41 | mod impl_fmt_macro_tests; 42 | 43 | #[cfg(not(feature = "__only_new_tests"))] 44 | mod shared_cp_macro_tests; 45 | 46 | #[cfg(feature = "fmt")] 47 | #[cfg(not(feature = "__only_new_tests"))] 48 | mod type_kind_coercion_macro_tests; 49 | 50 | #[cfg(feature = "fmt")] 51 | //#[cfg(not(feature = "__only_new_tests"))] 52 | mod writec_macro; 53 | 54 | #[cfg(feature = "__inline_const_pat_tests")] 55 | mod inline_const_pattern_tests; 56 | } 57 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/derive_debug/type_detection.rs: -------------------------------------------------------------------------------- 1 | use super::HowToFmt; 2 | 3 | use syn::Type; 4 | 5 | pub(super) fn detect_type_formatting(ty: &Type) -> HowToFmt { 6 | let ty = unwrap_reference(ty); 7 | 8 | // println!("{:?} {}", ty, ty.to_token_stream()); 9 | 10 | match ty { 11 | Type::Array { .. } | Type::Slice { .. } => HowToFmt::Slice, 12 | Type::Path(ty) if ty.qself.is_none() && is_path_an_option(&ty.path) => HowToFmt::Option_, 13 | _ => HowToFmt::Regular, 14 | } 15 | } 16 | 17 | fn unwrap_reference(mut ty: &Type) -> &Type { 18 | loop { 19 | match ty { 20 | Type::Reference(next) => ty = &*next.elem, 21 | Type::Group(next) => ty = &*next.elem, 22 | Type::Paren(next) => ty = &*next.elem, 23 | _ => break, 24 | } 25 | } 26 | ty 27 | } 28 | 29 | fn is_path_an_option(path: &syn::Path) -> bool { 30 | let segments = &path.segments; 31 | 32 | if segments[0].ident == "Option" { 33 | return true; 34 | } 35 | 36 | if segments.len() < 3 { 37 | return false; 38 | } 39 | 40 | let thecrate = &segments[0].ident; 41 | 42 | (thecrate == "core" || thecrate == "alloc" || thecrate == "std") 43 | && segments[1].ident == "option" 44 | && segments[2].ident == "Option" 45 | } 46 | 47 | /// Parses the type as an identifier, or the last identifier of a path. 48 | pub(super) fn parse_type_as_ident(ty: &Type) -> Result<&syn::Ident, crate::Error> { 49 | let ty = unwrap_reference(ty); 50 | match ty { 51 | Type::Path(typath) if typath.qself.is_none() && !typath.path.segments.is_empty() => { 52 | Ok(&typath.path.segments.last().unwrap().ident) 53 | } 54 | _ => Err(spanned_err!(ty, "expected a struct or enum")), 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/shared_arg_parsing.rs: -------------------------------------------------------------------------------- 1 | //! Types for parsing arguments, shared by many of the macros 2 | 3 | use crate::{ 4 | parse_utils::{MyParse, ParseBuffer, ParseStream}, 5 | spanned::Spans, 6 | }; 7 | 8 | use proc_macro2::TokenStream as TokenStream2; 9 | 10 | use quote::ToTokens; 11 | 12 | //////////////////////////////////////////////// 13 | 14 | // An expression inside `(...)` 15 | pub(crate) struct ExprArg { 16 | pub(crate) span: Spans, 17 | /// Using a TokenStream2 because it is validated to be a valid expression in 18 | /// the macro_rules! macros that call these proc macros. 19 | pub(crate) expr: TokenStream2, 20 | } 21 | 22 | impl ToTokens for ExprArg { 23 | fn to_tokens(&self, ts: &mut TokenStream2) { 24 | self.expr.to_tokens(ts); 25 | } 26 | } 27 | 28 | /// A sequence of comma separated expressions wrapped in parentheses (with a trailing comma) 29 | pub(crate) struct ExprArgs { 30 | pub(crate) args: Vec, 31 | } 32 | 33 | //////////////////////////////////////////////// 34 | 35 | impl MyParse for ExprArg { 36 | fn parse(input: ParseStream<'_>) -> Result { 37 | let paren = input.parse_paren()?; 38 | 39 | let mut content = ParseBuffer::new(paren.contents); 40 | 41 | content.parse_unwrap_tt(|content| { 42 | let (expr, span) = content.parse_token_stream_and_span(); 43 | 44 | Ok(Self { span, expr }) 45 | }) 46 | } 47 | } 48 | 49 | //////////////////////////////////////////////// 50 | 51 | impl MyParse for ExprArgs { 52 | fn parse(input: ParseStream<'_>) -> Result { 53 | let mut args = Vec::new(); 54 | 55 | while !input.is_empty() { 56 | args.push(ExprArg::parse(input)?); 57 | 58 | if !input.is_empty() { 59 | input.parse_punct(',')?; 60 | } 61 | } 62 | 63 | Ok(Self { args }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /const_format/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const_format" 3 | version = "0.2.35" 4 | authors = ["rodrimati1992 "] 5 | rust-version = "1.57.0" 6 | edition = "2021" 7 | license = "Zlib" 8 | description = "Compile-time string formatting" 9 | documentation = "https://docs.rs/const_format/" 10 | readme="../README.md" 11 | keywords = ["no-std", "format", "concat"] 12 | categories = ["no-std", "text-processing"] 13 | repository = "https://github.com/rodrimati1992/const_format_crates/" 14 | include = [ 15 | "Cargo.toml", 16 | "src/**/*.rs", 17 | "../README.md", 18 | "LICENSE-ZLIB.md", 19 | ] 20 | 21 | [features] 22 | default = [] 23 | const_generics = ["rust_1_51"] 24 | nightly_const_generics = ["const_generics"] 25 | rust_1_51 = [] 26 | rust_1_64 = ["rust_1_51", "konst", "konst/rust_1_64"] 27 | rust_1_83 = ["rust_1_64"] 28 | fmt = ["rust_1_83"] 29 | derive = ["fmt", "const_format_proc_macros/derive"] 30 | 31 | # soft-deprecated, use assertc instead. 32 | assert = ["assertc"] 33 | 34 | assertc = ["fmt", "assertcp"] 35 | assertcp = ["rust_1_51"] 36 | constant_time_as_str = ["fmt"] 37 | more_str_macros = ["rust_1_64"] 38 | 39 | # enables all the features, requires (potentially) the latest nightly 40 | all = [ 41 | "fmt", 42 | "derive", 43 | "rust_1_64", 44 | "assert", 45 | ] 46 | 47 | ############## 48 | ### "private" features 49 | 50 | # 51 | __debug = ["const_format_proc_macros/debug"] 52 | __test = [] 53 | __only_new_tests = ["__test"] 54 | __inline_const_pat_tests = ["__test", "fmt"] 55 | __docsrs = [] 56 | 57 | [dependencies.const_format_proc_macros] 58 | version = "=0.2.34" 59 | path = "../const_format_proc_macros" 60 | 61 | [dependencies.konst] 62 | version = "0.2.13" 63 | default-features = false 64 | optional = true 65 | 66 | [dev-dependencies] 67 | fastrand = {version = "1.3.5", default-features = false} 68 | arrayvec = {version = "0.7.0", default-features = false} 69 | 70 | [package.metadata.docs.rs] 71 | features = ["all", "__docsrs"] 72 | 73 | -------------------------------------------------------------------------------- /const_format/tests/str_methods_modules/conv_ascii_case.rs: -------------------------------------------------------------------------------- 1 | use const_format::__ascii_case_conv::{convert_str, size_after_conversion}; 2 | use const_format::{map_ascii_case, Case}; 3 | 4 | macro_rules! assert_case { 5 | ($case:expr, $input:expr, $output:expr $(,)?) => {{ 6 | const IN: &str = $input; 7 | const OUT: &str = $output; 8 | const CASE: Case = $case; 9 | 10 | assert_eq!(size_after_conversion(CASE, IN), OUT.len()); 11 | 12 | assert_eq!( 13 | std::str::from_utf8(&convert_str::<{ OUT.len() }>(CASE, IN)).unwrap(), 14 | OUT, 15 | ); 16 | 17 | assert_eq!(map_ascii_case!(CASE, IN), OUT); 18 | }}; 19 | } 20 | 21 | #[test] 22 | fn test_lowercase() { 23 | assert_case!( 24 | Case::Lower, 25 | "helloazWORLDAZ 効率 \u{303}n\u{303}Nñ", 26 | "helloazworldaz 効率 \u{303}n\u{303}nñ", 27 | ); 28 | } 29 | 30 | #[test] 31 | fn test_uppercase() { 32 | assert_case!( 33 | Case::Upper, 34 | "helloazWORLDAZ 効率 \u{303}n\u{303}Nñ", 35 | "HELLOAZWORLDAZ 効率 \u{303}N\u{303}Nñ", 36 | ); 37 | } 38 | 39 | #[test] 40 | fn test_snake_kebab_case() { 41 | assert_case!(Case::Snake, " __ 100 hello_nnWorld ", "100_hello_nn_world"); 42 | assert_case!( 43 | Case::UpperSnake, 44 | " __ 100 hello_nnWorld ", 45 | "100_HELLO_NN_WORLD" 46 | ); 47 | 48 | // Kebab case 49 | assert_case!(Case::Kebab, " __ 100 hello_nnWorld ", "100-hello-nn-world"); 50 | assert_case!( 51 | Case::UpperKebab, 52 | " __ 100 hello_nnWorld ", 53 | "100-HELLO-NN-WORLD" 54 | ); 55 | } 56 | 57 | #[test] 58 | fn test_pascal_camel_case() { 59 | assert_case!( 60 | Case::Pascal, 61 | " _foo_ 100 hello_nnñWorld ", 62 | "Foo100HelloNnñWorld" 63 | ); 64 | assert_case!(Case::Pascal, "一门 foo 一门", "一门Foo一门"); 65 | 66 | assert_case!(Case::Pascal, "一门foo 一门", "一门foo一门"); 67 | 68 | // Camel case 69 | assert_case!( 70 | Case::Camel, 71 | " _bar_ 100一门 hello_nnñWorld ", 72 | "bar100一门HelloNnñWorld", 73 | ); 74 | assert_case!(Case::Camel, "一门 foo 一门", "一门Foo一门"); 75 | 76 | assert_case!(Case::Camel, "一门foo 一门", "一门foo一门"); 77 | } 78 | -------------------------------------------------------------------------------- /const_format/src/__str_methods.rs: -------------------------------------------------------------------------------- 1 | mod str_replace; 2 | 3 | pub use self::str_replace::{ReplaceInput, ReplaceInputConv}; 4 | 5 | mod str_repeat; 6 | pub use str_repeat::StrRepeatArgs; 7 | 8 | mod str_splice; 9 | pub use str_splice::{DecomposedString, SplicedStr, StrSplceArgsConv, StrSpliceArgs}; 10 | 11 | mod str_indexing; 12 | pub use str_indexing::{IndexValidity, StrIndexArgs, StrIndexArgsConv}; 13 | 14 | #[cfg(feature = "rust_1_64")] 15 | mod str_split; 16 | 17 | #[cfg(feature = "rust_1_64")] 18 | pub use str_split::{SplitInput, SplitInputConv}; 19 | 20 | mod pattern; 21 | 22 | use pattern::{Pattern, PatternCtor, PatternNorm}; 23 | 24 | mod ascii_byte { 25 | #[derive(Copy, Clone)] 26 | pub struct AsciiByte(u8); 27 | 28 | impl AsciiByte { 29 | #[inline(always)] 30 | pub const fn new(byte: u8) -> Self { 31 | if byte > 127 { 32 | let byte = byte as usize; 33 | let _: () = [/* byte isn't valid ascii */][byte]; 34 | loop {} 35 | } 36 | Self(byte) 37 | } 38 | #[inline(always)] 39 | pub const fn get(self) -> u8 { 40 | self.0 41 | } 42 | } 43 | } 44 | pub use ascii_byte::AsciiByte; 45 | 46 | // copied from the konst crate, if that implementation is wrong, this needs to be fixed 47 | const fn bytes_find(left: &[u8], right: &[u8], from: usize) -> Option { 48 | let mut matching = right; 49 | 50 | __for_range! {i in from..left.len() => 51 | match matching { 52 | [mb, m_rem @ ..] => { 53 | let b = left[i]; 54 | 55 | matching = if b == *mb { 56 | m_rem 57 | } else { 58 | match right { 59 | // For when the string is "lawlawn" and we are trying to find "lawn" 60 | [mb2, m_rem2 @ ..] if b == *mb2 => m_rem2, 61 | _ => right, 62 | } 63 | }; 64 | } 65 | [] => { 66 | return Some(i - right.len()) 67 | } 68 | } 69 | } 70 | 71 | if matching.is_empty() { 72 | Some(left.len() - right.len()) 73 | } else { 74 | None 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/call_debug_fmt_macro.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::fmt::{Error, Formatter, FormattingFlags, StrWriter}; 2 | use cfmt_b::{call_debug_fmt, impl_fmt, try_}; 3 | 4 | use core::{cmp::Reverse, num::Wrapping}; 5 | 6 | #[test] 7 | fn all_macro_branches() { 8 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 9 | call_debug_fmt!(slice, [Some(10u8), None], f); 10 | try_!(f.write_str("\n")); 11 | 12 | call_debug_fmt!(Option, Some(&(0..10)), f); 13 | try_!(f.write_str("\n")); 14 | 15 | call_debug_fmt!(newtype Wrapping, Wrapping("hello"), f); 16 | try_!(f.write_str("\n")); 17 | 18 | call_debug_fmt!(newtype Reverse, Reverse(&Some(10u8)), f); 19 | try_!(f.write_str("\n")); 20 | 21 | call_debug_fmt!(newtype Hello, Hello(false), f); 22 | try_!(f.write_str("\n")); 23 | 24 | call_debug_fmt!(std, 1000u16, f); 25 | try_!(f.write_str("\n")); 26 | 27 | call_debug_fmt!(other, TupleStruct(256), f); 28 | try_!(f.write_str("\n")); 29 | 30 | Ok(()) 31 | } 32 | 33 | let writer: &mut StrWriter = &mut StrWriter::new([0; 1024]); 34 | 35 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 36 | 37 | assert_eq!( 38 | writer.as_str(), 39 | "[Some(10), None]\n\ 40 | Some(0..10)\n\ 41 | Wrapping(\"hello\")\n\ 42 | Reverse(Some(10))\n\ 43 | Hello(false)\n\ 44 | 1000\n\ 45 | TupleStruct(256)\n\ 46 | ", 47 | ); 48 | } 49 | 50 | #[test] 51 | fn returns_error() { 52 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 53 | call_debug_fmt!(slice, [Some(10u8), None], f); 54 | Ok(()) 55 | } 56 | 57 | let writer: &mut StrWriter = &mut StrWriter::new([0; 4]); 58 | 59 | let err = inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap_err(); 60 | 61 | assert!(matches!(err, Error::NotEnoughSpace)); 62 | } 63 | 64 | struct Hello(T); 65 | 66 | struct TupleStruct(u32); 67 | 68 | impl_fmt! { 69 | impl TupleStruct; 70 | 71 | const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 72 | use cfmt_b::PWrapper; 73 | 74 | let mut f = f.debug_tuple("TupleStruct"); 75 | try_!(PWrapper(self.0).const_debug_fmt(f.field())); 76 | f.finish() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/inline_const_pattern_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::cfmt_b; 2 | 3 | use crate::cfmt_b::{ 4 | concatc, concatcp, formatc, formatcp, map_ascii_case, str_get, str_index, str_repeat, 5 | str_replace, str_splice_out, str_split_pat, 6 | }; 7 | 8 | #[test] 9 | fn concatc_inline_pat_tests() { 10 | assert!(matches!("foo", concatc!("fo", "o"))); 11 | assert!(!matches!("bar", concatc!("bar", "r"))); 12 | } 13 | 14 | #[test] 15 | fn concatcp_inline_pat_tests() { 16 | assert!(matches!("foo", concatcp!("fo", "o"))); 17 | assert!(!matches!("bar", concatcp!("bar", "r"))); 18 | } 19 | 20 | #[test] 21 | fn formatc_inline_pat_tests() { 22 | assert!(matches!("foo", formatc!("f{0}{0}", "o"))); 23 | assert!(!matches!("bar", formatc!("bar{}", "r"))); 24 | } 25 | 26 | #[test] 27 | fn formatcp_inline_pat_tests() { 28 | assert!(matches!("foo", formatcp!("f{0}{0}", "o"))); 29 | assert!(!matches!("bar", formatcp!("bar{}", "r"))); 30 | } 31 | 32 | #[test] 33 | fn map_ascii_case_inline_pat_tests() { 34 | assert!(matches!("foo", map_ascii_case!(cfmt_b::Case::Lower, "FOO"))); 35 | assert!(!matches!( 36 | "bar", 37 | map_ascii_case!(cfmt_b::Case::Upper, "bar") 38 | )); 39 | } 40 | 41 | #[test] 42 | fn str_get_inline_pat_tests() { 43 | assert!(matches!(Some("foo"), str_get!(" foobar", 1..4))); 44 | assert!(matches!(None, str_get!(" foobar", 10))); 45 | } 46 | 47 | #[test] 48 | fn str_splice_out_inline_pat_tests() { 49 | assert!(matches!("foo", str_splice_out!("fbar", 1.., "oo"))); 50 | assert!(!matches!("foo", str_splice_out!("foo", 1..3, "bar"))); 51 | } 52 | 53 | #[test] 54 | fn str_index_inline_pat_tests() { 55 | assert!(matches!("foo", str_index!(" foobar", 1..4))); 56 | assert!(!matches!("foo", str_index!(" foobar", 1..5))); 57 | } 58 | 59 | #[test] 60 | fn str_repeat_inline_pat_tests() { 61 | assert!(matches!("foofoofoo", str_repeat!("foo", 3))); 62 | assert!(!matches!("foo", str_repeat!("foo", 0))); 63 | } 64 | 65 | #[test] 66 | fn str_replace_inline_pat_tests() { 67 | assert!(matches!("fooo", str_replace!("fo", "o", "ooo"))); 68 | assert!(!matches!("foo", str_replace!("fo", "o", "bar"))); 69 | } 70 | 71 | #[test] 72 | fn str_split_pat_inline_pat_tests() { 73 | assert!(matches!( 74 | &["foo", "bar", "baz"][..], 75 | str_split_pat!("foo bar baz", " ") 76 | )); 77 | assert!(!matches!( 78 | &["foo", "bar", "baz"][..], 79 | str_split_pat!("foo bar", " ") 80 | )); 81 | } 82 | -------------------------------------------------------------------------------- /const_format/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | #[doc(hidden)] 4 | #[macro_export] 5 | macro_rules! __identity { 6 | ($($tt:tt)*) => {$($tt)*}; 7 | } 8 | 9 | #[doc(hidden)] 10 | #[macro_export] 11 | macro_rules! __declare_rng_ext { 12 | () => { 13 | pub trait RngExt { 14 | fn as_rng(&self) -> &fastrand::Rng; 15 | 16 | fn pick<'a, T>(&self, slice: &'a [T]) -> &'a T { 17 | &slice[self.as_rng().usize(0..slice.len())] 18 | } 19 | 20 | fn char_(&self, bounds: core::ops::RangeInclusive) -> char { 21 | let this = self.as_rng(); 22 | 23 | if let None = bounds.clone().next() { 24 | panic!("There are no chars in the {:?} bounds", bounds); 25 | } 26 | 27 | let u32_bounds = u32::from(*bounds.start())..=u32::from(*bounds.end()); 28 | 29 | loop { 30 | if let Some(x) = core::char::from_u32(this.u32(u32_bounds.clone())) { 31 | break x; 32 | } 33 | } 34 | } 35 | 36 | fn unicode_char(&self) -> char { 37 | let this = self.as_rng(); 38 | let range = this 39 | .pick(&[ 40 | '\u{0000}'..='\u{007F}', 41 | '\u{0080}'..='\u{07FF}', 42 | '\u{0800}'..='\u{FFFF}', 43 | '\u{10000}'..='\u{10FFFF}', 44 | ]) 45 | .clone(); 46 | this.char_(range) 47 | } 48 | } 49 | 50 | impl RngExt for fastrand::Rng { 51 | fn as_rng(&self) -> &fastrand::Rng { 52 | self 53 | } 54 | } 55 | }; 56 | } 57 | 58 | pub const ALL_ASCII: &str = "\ 59 | \x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\ 60 | \x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \ 61 | !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]\ 62 | ^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u{80}\u{81}\u{90}\u{91}\ 63 | "; 64 | 65 | pub const ALL_ASCII_ESCAPED: &str = "\ 66 | \\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0B\\x0C\\r\\x0E\\x0F\ 67 | \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1A\\x1B\\x1C\\x1D\\x1E\\x1F \ 68 | !\\\"#$%&\\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]\ 69 | ^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u{80}\u{81}\u{90}\u{91}\ 70 | "; 71 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/str_splice.rs: -------------------------------------------------------------------------------- 1 | use super::str_indexing::{pass_range_types, IndexValidity, StrIndexArgs, StrIndexArgsConv}; 2 | 3 | pub struct StrSplceArgsConv { 4 | pub arg: T, 5 | pub str: &'static str, 6 | pub insert: &'static str, 7 | } 8 | 9 | #[allow(non_snake_case)] 10 | pub const fn StrSplceArgsConv( 11 | str: &'static str, 12 | arg: T, 13 | insert: &'static str, 14 | ) -> StrSplceArgsConv { 15 | StrSplceArgsConv { str, arg, insert } 16 | } 17 | 18 | pub struct StrSpliceArgs { 19 | pub str: &'static str, 20 | pub insert: &'static str, 21 | pub index_validity: IndexValidity, 22 | pub used_rstart: usize, 23 | pub used_rlen: usize, 24 | pub insert_len: usize, 25 | pub suffix_len: usize, 26 | pub out_len: usize, 27 | } 28 | 29 | /// The return value of [`str_splice`](./macro.str_splice.html) 30 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 31 | pub struct SplicedStr { 32 | /// A string that had `removed` replaced with some other string. 33 | pub output: &'static str, 34 | /// The part of the string that was removed. 35 | pub removed: &'static str, 36 | } 37 | 38 | #[repr(C, packed)] 39 | pub struct DecomposedString { 40 | pub prefix: P, 41 | pub middle: M, 42 | pub suffix: S, 43 | } 44 | 45 | macro_rules! define_conversions { 46 | ( 47 | $( fn($self:ident, $ty:ty) $block:block )* 48 | ) => { 49 | $( 50 | impl StrSplceArgsConv<$ty> { 51 | pub const fn conv(self) -> StrSpliceArgs { 52 | let StrIndexArgs{ 53 | str, 54 | index_validity, 55 | used_rstart, 56 | used_rend, 57 | used_rlen, 58 | } = StrIndexArgsConv{ 59 | arg: self.arg, 60 | str: self.str, 61 | }.conv(); 62 | 63 | StrSpliceArgs{ 64 | str, 65 | index_validity, 66 | used_rstart, 67 | used_rlen, 68 | insert: self.insert, 69 | insert_len: self.insert.len(), 70 | suffix_len: str.len() - used_rend, 71 | out_len: str.len() - used_rlen + self.insert.len(), 72 | } 73 | } 74 | } 75 | )* 76 | }; 77 | } 78 | 79 | pass_range_types! {define_conversions} 80 | -------------------------------------------------------------------------------- /const_format/src/msg.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use crate::fmt::{Error, StrWriter}; 4 | 5 | use core::marker::PhantomData; 6 | 7 | macro_rules! type_level_error { 8 | ( 9 | arguments($($args:tt)*) 10 | 11 | $($error:ident => $error_ty:ident<$($error_param:ident),*> ,)* 12 | ) => { 13 | type_level_error!{ 14 | @inner 15 | arguments($($args)*) 16 | 17 | Result::Ok(()), Ok => Ok<>, 18 | $( 19 | Result::Err(Error::$error), $error => $error_ty<$($error_param),*>, 20 | )* 21 | } 22 | }; 23 | (@inner 24 | arguments($cap:ident) 25 | 26 | $($matched:pat , $error:ident => $error_ty:ident<$($error_param:ident),*> ,)* 27 | ) => { 28 | 29 | enum ErrorKind { 30 | $($error,)* 31 | } 32 | 33 | impl ErrorTuple { 34 | pub const EMPTY: ErrorTuple = ErrorTuple{ 35 | error_variant: ErrorKind::Ok as usize, 36 | capacity: 0, 37 | }; 38 | 39 | pub const fn new(opt: Result<(), Error>, writer: &StrWriter) -> Self{ 40 | let variant = match opt { 41 | $($matched => ErrorKind::$error as usize,)* 42 | }; 43 | 44 | Self{ 45 | error_variant: variant, 46 | capacity: writer.capacity(), 47 | } 48 | } 49 | 50 | } 51 | 52 | 53 | $( 54 | pub struct $error_ty<$($error_param,)*>(PhantomData<($($error_param,)*)>); 55 | 56 | impl<$($error_param,)*> $error_ty<$($error_param,)*> { 57 | pub const NEW: Self = Self(PhantomData); 58 | } 59 | 60 | impl<$cap> ErrorAsType for ErrorPicker<[(); ErrorKind::$error as usize], $cap> { 61 | type Type = $error_ty<$($error_param,)*>; 62 | } 63 | )* 64 | } 65 | } 66 | 67 | pub struct ErrorTupleAndStrWriter { 68 | pub error: ErrorTuple, 69 | pub writer: StrWriter, 70 | } 71 | 72 | pub struct ErrorPicker(PhantomData (E, Cap)>); 73 | 74 | pub struct ErrorTuple { 75 | pub error_variant: usize, 76 | pub capacity: usize, 77 | } 78 | 79 | pub trait ErrorAsType { 80 | type Type; 81 | } 82 | 83 | type_level_error! { 84 | arguments(cap) 85 | 86 | NotEnoughSpace => not_enough_space_to_write_text_in_StrWriter_with_this_capacity, 87 | 88 | NotAscii => input_text_was_not_ascii<>, 89 | 90 | NotOnCharBoundary => NotOnCharBoundary<>, 91 | } 92 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/assertcp_tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(unreachable_code)] 2 | #![allow(non_local_definitions)] 3 | 4 | use cfmt_b::{assertcp, assertcp_eq, assertcp_ne}; 5 | 6 | struct Foo; 7 | 8 | // no need for formatting string 9 | assertcp!(true); 10 | 11 | assertcp!({ 12 | impl Foo { 13 | const NINE: u32 = 9; 14 | } 15 | true 16 | }); 17 | 18 | assertcp!(true, "Hello, world {:?}", { 19 | impl Foo { 20 | const BAR: u32 = 10; 21 | } 22 | 1u8 23 | },); 24 | 25 | assertcp!(true, concat!("Hello", r#"world {:?}"#), { 26 | impl Foo { 27 | const BAZ: u32 = 11; 28 | } 29 | 1u8 30 | },); 31 | 32 | #[test] 33 | fn assertcp_emits_formatting() { 34 | assert_eq!(Foo::NINE, 9); 35 | assert_eq!(Foo::BAR, 10); 36 | assert_eq!(Foo::BAZ, 11); 37 | assert_eq!(Foo::QUX, 12); 38 | assert_eq!(Foo::SPAT, 13); 39 | assert_eq!(Foo::OOF, 14); 40 | assert_eq!(Foo::RAB, 15); 41 | } 42 | 43 | // The formatting code should not run if the assertion is true 44 | assertcp!(true, "{}", { 45 | let _x: u32 = loop {}; 46 | _x 47 | }); 48 | 49 | const X: u8 = 123; 50 | 51 | #[allow(unused_variables)] 52 | const _: () = { 53 | //////////////////////////////////////////////////////////////////////////////// 54 | //// assertcp_eq 55 | 56 | assertcp_eq!( 57 | { 58 | impl Foo { 59 | const QUX: u32 = 12; 60 | } 61 | 0u8 62 | }, 63 | 0u8, 64 | ); 65 | assertcp_eq!(false, { 66 | impl Foo { 67 | const SPAT: u32 = 13; 68 | } 69 | false 70 | },); 71 | assertcp_eq!(' ', ' '); 72 | assertcp_eq!("hello", "hello"); 73 | 74 | assertcp_eq!(0u8, 0u8, "world"); 75 | assertcp_eq!(false, false, "world{}", 1u8); 76 | assertcp_eq!(' ', ' ', "world{foo}", foo = 1u8); 77 | assertcp_eq!("hello", "hello", "world{X}"); 78 | 79 | //////////////////////////////////////////////////////////////////////////////// 80 | //// assertcp_ne 81 | 82 | assertcp_ne!("hello", "helo"); 83 | assertcp_ne!(0u8, 1u8, "world"); 84 | assertcp_ne!( 85 | { 86 | impl Foo { 87 | const OOF: u32 = 14; 88 | } 89 | false 90 | }, 91 | true, 92 | "world{}", 93 | { 94 | let x: u32 = loop {}; 95 | x 96 | }, 97 | ); 98 | assertcp_ne!( 99 | ' ', 100 | { 101 | impl Foo { 102 | const RAB: u32 = 15; 103 | } 104 | 'A' 105 | }, 106 | "world{foo}", 107 | foo = { 108 | #[allow(unconditional_panic)] 109 | let foo: u8 = [][0]; 110 | foo 111 | }, 112 | ); 113 | }; 114 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/format_macro/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{parse_utils::MyParse, test_utils::StrExt}; 2 | 3 | fn process_str(s: &str) -> Result { 4 | MyParse::parse_token_stream_2(s.parse().unwrap()) 5 | .and_then(crate::format_macro::formatcp_impl) 6 | .map(|x| x.to_string()) 7 | .map_err(|e| e.to_compile_error().to_string()) 8 | } 9 | 10 | macro_rules! assert_ret { 11 | ($call:expr, |$res:ident| $predicate:expr $(,)*) => { 12 | let $res = $call; 13 | let $res = $res.as_ref(); 14 | assert!($predicate, "\nreturned:\n{:?}\n\n", $res); 15 | }; 16 | } 17 | 18 | #[test] 19 | fn named_argument_followed_by_positional() { 20 | assert_ret!(process_str(r#"(""), (a=()), () "#), |s| { 21 | s.unwrap_err() 22 | .consecutive_in_self(&["named arguments", "cannot", "positional arguments"]) 23 | }); 24 | } 25 | 26 | #[test] 27 | fn access_formatter_error() { 28 | let cases = [ 29 | r#"("{}"), (|f| 100u8) "#, 30 | r#"("{}"), (|_| 100u8) "#, 31 | r#"("{foo}"), (foo = |f| 100u8) "#, 32 | r#"("{foo}"), (foo = |_| 100u8) "#, 33 | r#"("{foo}"), (foo = (|f| 100u8)) "#, 34 | r#"("{foo}"), (foo = (|_| 100u8)) "#, 35 | ]; 36 | 37 | for case in cases.iter().copied() { 38 | assert_ret!(process_str(case), |s| { 39 | s.unwrap_err().consecutive_in_self(&["custom formatting"]) 40 | }); 41 | } 42 | } 43 | 44 | #[test] 45 | fn nonexistent_argument() { 46 | assert_ret!(process_str(r#"("{1}"), () "#), |s| { 47 | s.unwrap_err() 48 | .consecutive_in_self(&["positional argument", "1"]) 49 | }); 50 | 51 | assert_ret!(process_str(r#"("{}{}"), () "#), |s| { 52 | s.unwrap_err() 53 | .consecutive_in_self(&["positional argument", "1"]) 54 | }); 55 | process_str(r#"("{}"), () "#).unwrap(); 56 | } 57 | 58 | #[test] 59 | fn unused_arguments() { 60 | assert_ret!(process_str(r#"("{}"), (), () "#), |s| { 61 | let e = s.unwrap_err(); 62 | e.consecutive_in_self(&["argument", "1", "unused"]) 63 | }); 64 | assert_ret!(process_str(r#"("{}"), (), () , () "#), |s| { 65 | let e = s.unwrap_err(); 66 | e.consecutive_in_self(&["argument", "1", "unused"]) 67 | && e.consecutive_in_self(&["argument", "2", "unused"]) 68 | }); 69 | 70 | assert_ret!(process_str(r#"("{}"), (), (foooo = "") "#), |s| { 71 | s.unwrap_err() 72 | .consecutive_in_self(&["foooo", "argument", "unused"]) 73 | }); 74 | 75 | assert_ret!( 76 | process_str(r#"("{}"), (), (), (foooo = ""), (bar = "") "#), 77 | |s| { 78 | let e = s.unwrap_err(); 79 | e.consecutive_in_self(&["argument", "1", "unused"]) 80 | && e.consecutive_in_self(&["foooo", "argument", "unused"]) 81 | && e.consecutive_in_self(&["bar", "argument", "unused"]) 82 | } 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/assertc_tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(unreachable_code)] 2 | #![allow(non_local_definitions)] 3 | 4 | use cfmt_b::for_examples::{Point3, Unit}; 5 | use cfmt_b::{assertc, assertc_eq, assertc_ne, call_debug_fmt}; 6 | 7 | struct Foo; 8 | 9 | assertc!(true, "Hello, world {:?}", { 10 | impl Foo { 11 | const BAR: u32 = 10; 12 | } 13 | },); 14 | 15 | assertc!(true, concat!("Hello", r#"world {:?}"#), { 16 | impl Foo { 17 | const BAZ: u32 = 11; 18 | } 19 | },); 20 | 21 | // braces in arguments that take a formatter should work 22 | assertc!( 23 | true, 24 | "{foo}\n{}", 25 | |fmt| { 26 | impl Foo { 27 | const FMT_FOO: u32 = 12; 28 | } 29 | call_debug_fmt!(array, [100u8], fmt) 30 | }, 31 | foo = |fmt| { 32 | impl Foo { 33 | const FMT_BAR: u32 = 13; 34 | } 35 | call_debug_fmt!(array, [Unit, Unit], fmt) 36 | }, 37 | ); 38 | 39 | // single expressions that take the formatter should also work 40 | assertc!( 41 | true, 42 | "{foo}\n{foo:#?}\n{}", 43 | |fmt| call_debug_fmt!(array, [100u8], fmt), 44 | foo = |fmt| call_debug_fmt!(array, [Unit, Unit], fmt), 45 | ); 46 | 47 | #[test] 48 | fn assertc_emits_formatting() { 49 | assert_eq!(Foo::BAR, 10); 50 | assert_eq!(Foo::BAZ, 11); 51 | assert_eq!(Foo::FMT_FOO, 12); 52 | assert_eq!(Foo::FMT_BAR, 13); 53 | } 54 | 55 | // The formatting code should not run if the assertion is true 56 | assertc!(true, "{}", { 57 | let _x: u32 = loop {}; 58 | _x 59 | }); 60 | 61 | #[allow(unused_variables)] 62 | const _: () = { 63 | const POINT: Point3 = Point3 { x: 3, y: 5, z: 8 }; 64 | const OTHER_POINT: Point3 = Point3 { 65 | x: 13, 66 | y: 21, 67 | z: 34, 68 | }; 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | //// assertc_eq 72 | 73 | assertc_eq!(POINT, POINT); 74 | assertc_eq!(OTHER_POINT, OTHER_POINT); 75 | 76 | assertc_eq!(Unit, Unit); 77 | 78 | assertc_eq!(0u8, 0u8); 79 | assertc_eq!("foo", "foo", "hello"); 80 | assertc_eq!(Some("foo"), Some("foo"), "hello {}", { 81 | let x: u32 = loop {}; 82 | x 83 | }); 84 | assertc_eq!([false], [false], "{}", |f| { 85 | loop {} 86 | f.write_str("hello") 87 | }); 88 | 89 | //////////////////////////////////////////////////////////////////////////////// 90 | //// assertc_ne 91 | assertc_ne!(POINT, OTHER_POINT); 92 | assertc_ne!(OTHER_POINT, POINT); 93 | 94 | assertc_ne!(0u8, 3u8); 95 | assertc_ne!("foo", "bar", "hello"); 96 | assertc_ne!(Some("foo"), Some("bar"), "hello {}", { 97 | let x: u32 = loop {}; 98 | x 99 | }); 100 | assertc_ne!([false], [true], "{}", |f| { 101 | loop {} 102 | f.write_str("hello") 103 | }); 104 | }; 105 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/datastructure/field_map.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::ops::{Index, IndexMut}; 4 | 5 | /** 6 | This is a map from fields to some value. 7 | 8 | If you put this in a type,and use Default to initialize it, 9 | you must remember to replace the `FieldMap` using either `FieldMap::defaulted` or `FieldMap::with` 10 | 11 | */ 12 | #[derive(Debug)] 13 | pub struct FieldMap { 14 | // The outer vec is the enum variant (if it's a struct/union it's a single element Vec), 15 | // the inner one is the field within a variant/struct/union. 16 | fields: Vec>, 17 | } 18 | 19 | impl FieldMap { 20 | /// Constructs an FieldMap which maps each field in the DataStructure to a value 21 | /// (obtained by mapping each individual field to `T` using a closure). 22 | pub fn with<'a, F>(ds: &'a DataStructure<'a>, mut f: F) -> Self 23 | where 24 | F: FnMut(&'a Field<'a>) -> T, 25 | { 26 | Self { 27 | fields: ds 28 | .variants 29 | .iter() 30 | .map(|vari| vari.fields.iter().map(&mut f).collect::>()) 31 | .collect::>(), 32 | } 33 | } 34 | 35 | #[allow(dead_code)] 36 | pub fn iter(&self) -> impl Iterator + Clone + '_ { 37 | self.fields.iter().enumerate().flat_map(|(v_i, v)| { 38 | v.iter().enumerate().map(move |(f_i, f)| { 39 | let index = FieldIndex { 40 | variant: v_i as _, 41 | pos: f_i as _, 42 | }; 43 | (index, f) 44 | }) 45 | }) 46 | } 47 | 48 | #[allow(dead_code)] 49 | pub fn iter_mut(&mut self) -> impl Iterator + '_ { 50 | self.fields.iter_mut().enumerate().flat_map(|(v_i, v)| { 51 | v.iter_mut().enumerate().map(move |(f_i, f)| { 52 | let index = FieldIndex { 53 | variant: v_i as _, 54 | pos: f_i as _, 55 | }; 56 | (index, f) 57 | }) 58 | }) 59 | } 60 | } 61 | 62 | impl<'a, T> Index for FieldMap { 63 | type Output = T; 64 | 65 | fn index(&self, index: FieldIndex) -> &T { 66 | &self.fields[index.variant][index.pos] 67 | } 68 | } 69 | 70 | impl<'a, T> IndexMut for FieldMap { 71 | fn index_mut(&mut self, index: FieldIndex) -> &mut T { 72 | &mut self.fields[index.variant][index.pos] 73 | } 74 | } 75 | 76 | impl<'a, T> Index<&'a Field<'a>> for FieldMap { 77 | type Output = T; 78 | 79 | fn index(&self, field: &'a Field<'a>) -> &T { 80 | let index = field.index; 81 | &self.fields[index.variant][index.pos] 82 | } 83 | } 84 | 85 | impl<'a, T> IndexMut<&'a Field<'a>> for FieldMap { 86 | fn index_mut(&mut self, field: &'a Field<'a>) -> &mut T { 87 | let index = field.index; 88 | &mut self.fields[index.variant][index.pos] 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /const_format/src/fmt/error.rs: -------------------------------------------------------------------------------- 1 | // <_< clippy you silly 2 | #![allow(clippy::enum_variant_names)] 3 | 4 | use core::fmt::{self, Display}; 5 | 6 | /// An error while trying to write into a StrWriter. 7 | #[non_exhaustive] 8 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 9 | pub enum Error { 10 | /// Attempted to write something into the buffer when there isn't enough space to write it. 11 | NotEnoughSpace, 12 | /// For compatibility with [`NotAsciiError`](../wrapper_types/struct.NotAsciiError.html) 13 | NotAscii, 14 | /// Attempted to index a string arguent by an range where one of the bounds 15 | /// was not on a char boundary. 16 | NotOnCharBoundary, 17 | } 18 | 19 | impl Display for Error { 20 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 21 | match self { 22 | Self::NotEnoughSpace => { 23 | fmt.write_str("The was not enough space to write the formatted output") 24 | } 25 | Self::NotAscii => fmt.write_str("Attempted to write non-ascii text"), 26 | Self::NotOnCharBoundary => { 27 | fmt.write_str("Attempted to index a byte that's not on a char boundary.") 28 | } 29 | } 30 | } 31 | } 32 | 33 | macro_rules! index_vars{ 34 | ($self:ident, $index:ident; $($variant:ident),* $(,)? ) => ( 35 | enum Index{ 36 | $($variant,)* 37 | } 38 | 39 | let $index = match &$self { 40 | $(Error::$variant{..} => 3300 + Index::$variant as usize,)* 41 | }; 42 | ) 43 | } 44 | 45 | impl Error { 46 | /// For panicking at compile-time, with a compile-time error that says what the error is. 47 | #[track_caller] 48 | pub const fn unwrap(&self) -> T { 49 | index_vars! { 50 | self,i; 51 | NotEnoughSpace, 52 | NotAscii, 53 | NotOnCharBoundary, 54 | }; 55 | 56 | match self { 57 | Error::NotEnoughSpace => ["The was not enough space to write the formatted output"][i], 58 | Error::NotAscii => ["Attempted to write non-ascii text"][i], 59 | Error::NotOnCharBoundary => { 60 | ["Attempted to index a byte that's not on a char boundary."][i] 61 | } 62 | }; 63 | loop {} 64 | } 65 | } 66 | 67 | //////////////////////////////////////////////////////////////////////////////// 68 | 69 | /// The return type of most formatting functions 70 | pub type Result = core::result::Result; 71 | 72 | //////////////////////////////////////////////////////////////////////////////// 73 | 74 | /// For converting types to [`const_format::Result`] 75 | /// 76 | /// [`const_format::Result`]: ./type.Result.html 77 | pub struct ToResult(pub T); 78 | 79 | impl ToResult<()> { 80 | /// 81 | #[inline(always)] 82 | pub const fn to_result(self) -> Result { 83 | Ok(()) 84 | } 85 | } 86 | 87 | impl ToResult { 88 | /// 89 | #[inline(always)] 90 | pub const fn to_result(self) -> Result { 91 | self.0 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /const_format/tests/str_methods_modules/str_replace.rs: -------------------------------------------------------------------------------- 1 | use const_format::__str_methods::{ReplaceInput, ReplaceInputConv}; 2 | use const_format::str_replace; 3 | 4 | macro_rules! assert_case { 5 | ($input:expr, $patt:expr, $replace_with:expr, $output:expr $(,)*) => {{ 6 | const IN: &str = $input; 7 | const ARGS: ReplaceInput = ReplaceInputConv(IN, $patt, REPLACE_WITH).conv(); 8 | const REPLACE_WITH: &str = $replace_with; 9 | const OUT: &str = $output; 10 | 11 | assert_eq!(ARGS.replace_length(), OUT.len()); 12 | 13 | assert_eq!( 14 | std::str::from_utf8(&ARGS.replace::<{ OUT.len() }>()).unwrap(), 15 | OUT, 16 | ); 17 | 18 | assert_eq!(str_replace!(IN, $patt, REPLACE_WITH), OUT); 19 | }}; 20 | } 21 | 22 | #[test] 23 | fn test_small_pattern() { 24 | assert_case! {"hequ", "q", "XY", "heXYu"} 25 | assert_case! {"hequx", "q", "XYZ", "heXYZux"} 26 | assert_case! {"hequq", "q", "XY", "heXYuXY"} 27 | assert_case! {"hequxq", "q", "XYZ", "heXYZuxXYZ"} 28 | 29 | assert_case! {"hequ", "qu", "XY", "heXY"} 30 | assert_case! {"hequ", "qu", "XYZ", "heXYZ"} 31 | assert_case! {"hequx", "qu", "XYZ", "heXYZx"} 32 | } 33 | 34 | #[test] 35 | fn test_char_pattern() { 36 | { 37 | const C: char = 'q'; 38 | assert_eq!(C.len_utf8(), 1); 39 | 40 | assert_case! {"hequ", C, "XY", "heXYu"} 41 | assert_case! {"hequx", C, "XYZ", "heXYZux"} 42 | assert_case! {"hequq", C, "XY", "heXYuXY"} 43 | assert_case! {"hequxq", C, "XYZ", "heXYZuxXYZ"} 44 | } 45 | { 46 | const C: char = 'ñ'; 47 | assert_eq!(C.len_utf8(), 2); 48 | 49 | assert_case! {"heñu", C, "XY", "heXYu"} 50 | assert_case! {"heñux", C, "XYZ", "heXYZux"} 51 | assert_case! {"heñuñ", C, "XY", "heXYuXY"} 52 | assert_case! {"heñuxñ", C, "XYZ", "heXYZuxXYZ"} 53 | } 54 | { 55 | const C: char = '₀'; 56 | assert_eq!(C.len_utf8(), 3); 57 | 58 | assert_case! {"he₀u", C, "XY", "heXYu"} 59 | assert_case! {"he₀ux", C, "XYZ", "heXYZux"} 60 | assert_case! {"he₀u₀", C, "XY", "heXYuXY"} 61 | assert_case! {"he₀ux₀", C, "XYZ", "heXYZuxXYZ"} 62 | } 63 | { 64 | const C: char = '🧡'; 65 | assert_eq!(C.len_utf8(), 4); 66 | 67 | assert_case! {"he🧡u", C, "XY", "heXYu"} 68 | assert_case! {"he🧡ux", C, "XYZ", "heXYZux"} 69 | assert_case! {"he🧡u🧡", C, "XY", "heXYuXY"} 70 | assert_case! {"he🧡ux🧡", C, "XYZ", "heXYZuxXYZ"} 71 | } 72 | } 73 | 74 | #[test] 75 | fn test_replace_overlapping() { 76 | assert_case! {"helololololol", "lol", "XY", "heXYoXYoXY"} 77 | 78 | assert_case! {"hequ", "qux", "XY", "hequ"} 79 | assert_case! {"hequx", "qux", "XYZA", "heXYZA"} 80 | assert_case! {"heququx", "qux", "XYZAB", "hequXYZAB"} 81 | assert_case! {"hequxqu", "qux", "XYZABC", "heXYZABCqu"} 82 | } 83 | 84 | #[test] 85 | fn test_replace_empty() { 86 | assert_case! {"", "qux", "-------", ""} 87 | 88 | assert_case! {"hequxqu", "", "-------------", "hequxqu"} 89 | 90 | assert_case! {"hequxqu", "qux", "", "hequ"} 91 | } 92 | -------------------------------------------------------------------------------- /const_format/src/char_encoding/tests.rs: -------------------------------------------------------------------------------- 1 | use super::{char_debug_len, char_display_len, char_to_debug, char_to_display}; 2 | 3 | #[test] 4 | fn char_to_utf8_encoding_test() { 5 | for c in '\0'..=core::char::MAX { 6 | let mut utf8_std = [0u8; 4]; 7 | let utf8_std = c.encode_utf8(&mut utf8_std); 8 | 9 | let utf8_here = char_to_display(c); 10 | assert_eq!(utf8_here.len(), char_display_len(c)); 11 | 12 | assert_eq!(utf8_std.as_bytes(), utf8_here.as_bytes()); 13 | } 14 | } 15 | 16 | #[test] 17 | fn char_to_utf8_display_test() { 18 | for c in '\0'..=core::char::MAX { 19 | let mut utf8_std = [0u8; 4]; 20 | let utf8_std = c.encode_utf8(&mut utf8_std); 21 | 22 | let utf8_here = char_to_display(c); 23 | assert_eq!(utf8_here.len(), char_display_len(c)); 24 | 25 | assert_eq!(utf8_std.as_bytes(), utf8_here.as_bytes()); 26 | } 27 | } 28 | 29 | #[test] 30 | fn char_to_utf8_debug_test() { 31 | let first_escapes = [ 32 | ('\x00', r#"'\x00'"#), 33 | ('\x01', r#"'\x01'"#), 34 | ('\x02', r#"'\x02'"#), 35 | ('\x03', r#"'\x03'"#), 36 | ('\x04', r#"'\x04'"#), 37 | ('\x05', r#"'\x05'"#), 38 | ('\x06', r#"'\x06'"#), 39 | ('\x07', r#"'\x07'"#), 40 | ('\x08', r#"'\x08'"#), 41 | ('\t', r#"'\t'"#), 42 | ('\n', r#"'\n'"#), 43 | ('\x0B', r#"'\x0B'"#), 44 | ('\x0C', r#"'\x0C'"#), 45 | ('\r', r#"'\r'"#), 46 | ('\x0E', r#"'\x0E'"#), 47 | ('\x0F', r#"'\x0F'"#), 48 | ('\x10', r#"'\x10'"#), 49 | ('\x11', r#"'\x11'"#), 50 | ('\x12', r#"'\x12'"#), 51 | ('\x13', r#"'\x13'"#), 52 | ('\x14', r#"'\x14'"#), 53 | ('\x15', r#"'\x15'"#), 54 | ('\x16', r#"'\x16'"#), 55 | ('\x17', r#"'\x17'"#), 56 | ('\x18', r#"'\x18'"#), 57 | ('\x19', r#"'\x19'"#), 58 | ('\x1A', r#"'\x1A'"#), 59 | ('\x1B', r#"'\x1B'"#), 60 | ('\x1C', r#"'\x1C'"#), 61 | ('\x1D', r#"'\x1D'"#), 62 | ('\x1E', r#"'\x1E'"#), 63 | ('\x1F', r#"'\x1F'"#), 64 | ]; 65 | 66 | for (c, expected) in first_escapes.iter().copied() { 67 | let utf8_here = char_to_debug(c); 68 | assert_eq!(expected.as_bytes(), utf8_here.as_bytes(), "{:?}", c); 69 | assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); 70 | } 71 | 72 | let other_escapes = [('\'', r#"'\''"#), ('\"', r#"'\"'"#), ('\\', r#"'\\'"#)]; 73 | 74 | let mut buffer = arrayvec::ArrayString::<12>::new(); 75 | for c in '\x20'..=core::char::MAX { 76 | let utf8_here = char_to_debug(c); 77 | 78 | if let Some((_, expected)) = Some(c) 79 | .filter(|c| *c <= '\x7F') 80 | .and_then(|c| other_escapes.iter().copied().find(|x| x.0 == c)) 81 | { 82 | assert_eq!(expected.as_bytes(), utf8_here.as_bytes(), "{:?}", c); 83 | assert_eq!(expected.len(), char_debug_len(c), "{:?}", c); 84 | } else { 85 | buffer.clear(); 86 | buffer.push('\''); 87 | buffer.push(c); 88 | buffer.push('\''); 89 | assert_eq!(buffer.as_bytes(), utf8_here.as_bytes(), "{:?}", c); 90 | assert_eq!(buffer.len(), char_debug_len(c), "{:?}", c); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /const_format/src/macros/map_ascii_case.rs: -------------------------------------------------------------------------------- 1 | /// Converts the casing style of a `&'static str` constant, 2 | /// ignoring non-ascii unicode characters. 3 | /// 4 | /// This nacro is equivalent to a function with this signature: 5 | /// 6 | /// ```rust 7 | /// const fn map_ascii_case(case: const_format::Case, input: &'static str) -> &'static str 8 | /// # {""} 9 | /// ``` 10 | /// 11 | /// The [`Case`](enum.Case.html) parameter determines the casing style of the returned string. 12 | /// 13 | /// # Ascii 14 | /// 15 | /// This only transforms ascii characters because broader unicode case conversion, 16 | /// while possible, is much harder to implement purely with `const fn`s. 17 | /// 18 | /// Non-ascii characters are treated as though they're alphabetic ascii characters. 19 | /// 20 | /// # Ignored characters 21 | /// 22 | /// These casing styles treat non-alphanumeric ascii characters as spaces, 23 | /// removing them from the returned string: 24 | /// 25 | /// - `Case::Pascal` 26 | /// - `Case::Camel` 27 | /// - `Case::Snake` 28 | /// - `Case::UpperSnake` 29 | /// - `Case::Kebab` 30 | /// - `Case::UpperKebab` 31 | /// 32 | /// # Example 33 | /// 34 | /// ```rust 35 | /// use const_format::{Case, map_ascii_case}; 36 | /// 37 | /// { 38 | /// const LOW: &str = map_ascii_case!(Case::Lower, "hello WORLD"); 39 | /// assert_eq!(LOW, "hello world"); 40 | /// } 41 | /// { 42 | /// const IN: &str = "hello WORLD каждому"; 43 | /// const OUT: &str = map_ascii_case!(Case::Upper, IN); 44 | /// // non-ascii characters are ignored by map_ascii_case. 45 | /// assert_eq!(OUT, "HELLO WORLD каждому"); 46 | /// } 47 | /// 48 | /// const IN2: &str = "hello fooкаждому100Bar#qux"; 49 | /// { 50 | /// const OUT: &str = map_ascii_case!(Case::Pascal, IN2); 51 | /// assert_eq!(OUT, "HelloFooкаждому100BarQux"); 52 | /// } 53 | /// { 54 | /// const OUT: &str = map_ascii_case!(Case::Camel, IN2); 55 | /// assert_eq!(OUT, "helloFooкаждому100BarQux"); 56 | /// } 57 | /// { 58 | /// const OUT: &str = map_ascii_case!(Case::Snake, IN2); 59 | /// assert_eq!(OUT, "hello_fooкаждому_100_bar_qux"); 60 | /// } 61 | /// { 62 | /// const OUT: &str = map_ascii_case!(Case::UpperSnake, IN2); 63 | /// assert_eq!(OUT, "HELLO_FOOкаждому_100_BAR_QUX"); 64 | /// } 65 | /// { 66 | /// const OUT: &str = map_ascii_case!(Case::Kebab, IN2); 67 | /// assert_eq!(OUT, "hello-fooкаждому-100-bar-qux"); 68 | /// } 69 | /// { 70 | /// const OUT: &str = map_ascii_case!(Case::UpperKebab, IN2); 71 | /// assert_eq!(OUT, "HELLO-FOOкаждому-100-BAR-QUX"); 72 | /// } 73 | /// 74 | /// 75 | /// ``` 76 | #[macro_export] 77 | macro_rules! map_ascii_case { 78 | ($case:expr, $str:expr) => { 79 | $crate::__str_const! {{ 80 | const S_OSRCTFL4A: &$crate::pmr::str = $str; 81 | const CASE_OSRCTFL4A: $crate::Case = $case; 82 | { 83 | const L: $crate::pmr::usize = 84 | $crate::__ascii_case_conv::size_after_conversion(CASE_OSRCTFL4A, S_OSRCTFL4A); 85 | 86 | const OB: &[$crate::pmr::u8; L] = 87 | &$crate::__ascii_case_conv::convert_str::(CASE_OSRCTFL4A, S_OSRCTFL4A); 88 | 89 | const OS: &$crate::pmr::str = unsafe { $crate::__priv_transmute_bytes_to_str!(OB) }; 90 | 91 | OS 92 | } 93 | }} 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /const_format/tests/str_methods_modules/str_splice.rs: -------------------------------------------------------------------------------- 1 | use const_format::{str_splice, str_splice_out, SplicedStr}; 2 | 3 | fn ss(output: &'static str, removed: &'static str) -> SplicedStr { 4 | SplicedStr { output, removed } 5 | } 6 | 7 | const IN: &str = "abcdefghij"; 8 | const RW: &str = "_.-"; 9 | 10 | #[test] 11 | fn splice_ranges() { 12 | assert_eq!(str_splice!(IN, 2, RW), ss("ab_.-defghij", "c")); 13 | assert_eq!(str_splice!(IN, 4, RW), ss("abcd_.-fghij", "e")); 14 | 15 | assert_eq!(str_splice!(IN, 2..4, RW), ss("ab_.-efghij", "cd")); 16 | assert_eq!(str_splice!(IN, 4..4, RW), ss("abcd_.-efghij", "")); 17 | assert_eq!(str_splice!(IN, 4..0, RW), ss("abcd_.-efghij", "")); 18 | 19 | assert_eq!(str_splice!(IN, 2..=4, RW), ss("ab_.-fghij", "cde")); 20 | assert_eq!(str_splice!(IN, 4..=4, RW), ss("abcd_.-fghij", "e")); 21 | 22 | assert_eq!(str_splice!(IN, ..2, RW), ss("_.-cdefghij", "ab")); 23 | assert_eq!(str_splice!(IN, ..4, RW), ss("_.-efghij", "abcd")); 24 | 25 | assert_eq!(str_splice!(IN, ..=1, RW), ss("_.-cdefghij", "ab")); 26 | assert_eq!(str_splice!(IN, ..=3, RW), ss("_.-efghij", "abcd")); 27 | 28 | assert_eq!(str_splice!(IN, 5.., RW), ss("abcde_.-", "fghij")); 29 | assert_eq!(str_splice!(IN, 5..IN.len(), RW), ss("abcde_.-", "fghij")); 30 | assert_eq!(str_splice!(IN, 7.., RW), ss("abcdefg_.-", "hij")); 31 | 32 | assert_eq!(str_splice!(IN, .., RW), ss("_.-", "abcdefghij")); 33 | } 34 | 35 | #[test] 36 | fn replacements() { 37 | assert_eq!(str_splice!("abcde", 2..4, ""), ss("abe", "cd")); 38 | assert_eq!(str_splice!("abcde", 2..4, "h"), ss("abhe", "cd")); 39 | assert_eq!(str_splice!("abcde", 2..4, "he"), ss("abhee", "cd")); 40 | assert_eq!(str_splice!("abcde", 2..4, "hel"), ss("abhele", "cd")); 41 | assert_eq!(str_splice!("abcde", 2..4, "hell"), ss("abhelle", "cd")); 42 | assert_eq!(str_splice!("abcde", 2..4, "hello"), ss("abhelloe", "cd")); 43 | } 44 | 45 | #[test] 46 | fn splice_out_ranges() { 47 | assert_eq!(str_splice_out!(IN, 2, RW), "ab_.-defghij"); 48 | assert_eq!(str_splice_out!(IN, 4, RW), "abcd_.-fghij"); 49 | 50 | assert_eq!(str_splice_out!(IN, 2..4, RW), "ab_.-efghij"); 51 | assert_eq!(str_splice_out!(IN, 4..4, RW), "abcd_.-efghij"); 52 | assert_eq!(str_splice_out!(IN, 4..0, RW), "abcd_.-efghij"); 53 | 54 | assert_eq!(str_splice_out!(IN, 2..=4, RW), "ab_.-fghij"); 55 | assert_eq!(str_splice_out!(IN, 4..=4, RW), "abcd_.-fghij"); 56 | 57 | assert_eq!(str_splice_out!(IN, ..2, RW), "_.-cdefghij"); 58 | assert_eq!(str_splice_out!(IN, ..4, RW), "_.-efghij"); 59 | 60 | assert_eq!(str_splice_out!(IN, ..=1, RW), "_.-cdefghij"); 61 | assert_eq!(str_splice_out!(IN, ..=3, RW), "_.-efghij"); 62 | 63 | assert_eq!(str_splice_out!(IN, 5.., RW), "abcde_.-"); 64 | assert_eq!(str_splice_out!(IN, 5..IN.len(), RW), "abcde_.-"); 65 | assert_eq!(str_splice_out!(IN, 7.., RW), "abcdefg_.-"); 66 | 67 | assert_eq!(str_splice_out!(IN, .., RW), "_.-"); 68 | } 69 | 70 | #[test] 71 | fn splice_out_replacements() { 72 | assert_eq!(str_splice_out!("abcde", 2..4, ""), "abe"); 73 | assert_eq!(str_splice_out!("abcde", 2..4, "h"), "abhe"); 74 | assert_eq!(str_splice_out!("abcde", 2..4, "he"), "abhee"); 75 | assert_eq!(str_splice_out!("abcde", 2..4, "hel"), "abhele"); 76 | assert_eq!(str_splice_out!("abcde", 2..4, "hell"), "abhelle"); 77 | assert_eq!(str_splice_out!("abcde", 2..4, "hello"), "abhelloe"); 78 | } 79 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | max-parallel: 2 14 | matrix: 15 | rust: [stable, beta, nightly, 1.60.0, 1.64.0, 1.83.0] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: ci-all-versions 20 | run: | 21 | rustup override set ${{ matrix.rust }} 22 | cargo update 23 | 24 | cd "${{github.workspace}}/const_format_proc_macros/" 25 | cargo test 26 | 27 | cd "${{github.workspace}}/const_format/" 28 | cargo test --features "__test" 29 | 30 | cargo test --features "__test assertcp" 31 | 32 | - uses: actions/checkout@v2 33 | - name: ci-stable 34 | if: ${{ matrix.rust == '1.64.0' }} 35 | run: | 36 | cargo update 37 | 38 | cd "${{github.workspace}}/const_format/" 39 | 40 | cargo test --features "__test rust_1_64" 41 | 42 | - uses: actions/checkout@v2 43 | - name: ci-nighly 44 | if: ${{ matrix.rust != '1.60.0' && matrix.rust != '1.64.0' }} 45 | run: | 46 | rustup override set ${{ matrix.rust }} 47 | 48 | cargo update 49 | 50 | cd "${{github.workspace}}/const_format/" 51 | 52 | cargo test --features "__test" 53 | cargo test --features "__test more_str_macros" 54 | cargo test --features "__test assertcp" 55 | cargo test --features "__test fmt" 56 | cargo test --features "__test assertc" 57 | cargo test --features "__test derive" 58 | cargo test --features "__test constant_time_as_str" 59 | cargo test --features "__test rust_1_83" 60 | cargo test --features "__test derive constant_time_as_str" 61 | cargo test --features "__test derive constant_time_as_str assertc" 62 | cargo test --features "__test derive constant_time_as_str assertc more_str_macros rust_1_83" 63 | 64 | - uses: actions/checkout@v2 65 | - name: ci-nighly 66 | if: ${{ matrix.rust == 'nightly' && runner.os == 'Linux' }} 67 | run: | 68 | rustup override set ${{ matrix.rust }} 69 | 70 | cargo update -Z minimal-versions 71 | 72 | cd "${{github.workspace}}/const_format_proc_macros/" 73 | cargo test 74 | 75 | cd "${{github.workspace}}/const_format/" 76 | 77 | # enable `__inline_const_pat_tests` feature if `const {...}` pats are allowed 78 | cargo test --features "__test derive constant_time_as_str assertc more_str_macros rust_1_83" 79 | 80 | MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) 81 | MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-check-number-validity -Zmiri-symbolic-alignment-check" 82 | echo "Installing latest nightly with Miri" 83 | echo "$MIRI_NIGHTLY" 84 | rustup set profile minimal 85 | rustup default "$MIRI_NIGHTLY" 86 | rustup override set "$MIRI_NIGHTLY" 87 | rustup component add miri 88 | cargo miri setup 89 | 90 | cargo clean 91 | 92 | # tests the crate without the constant_time_as_str feature 93 | # (and also without any feature that implies it) 94 | cargo miri test --tests --features "__test derive fmt assertc" 95 | 96 | # tests the crate with every feature, including constant_time_as_str 97 | # enable `__inline_const_pat_tests` feature if `const {...}` pats are allowed 98 | cargo miri test \ 99 | --features "__test derive constant_time_as_str assertc more_str_macros rust_1_83" 100 | -------------------------------------------------------------------------------- /const_format/src/slice_cmp.rs: -------------------------------------------------------------------------------- 1 | /// A const equivalent of `&str` equality comparison. 2 | /// 3 | /// # Example 4 | /// 5 | #[cfg_attr(feature = "fmt", doc = "```rust")] 6 | #[cfg_attr(not(feature = "fmt"), doc = "```ignore")] 7 | /// use const_format::utils::str_eq; 8 | /// 9 | /// const STRS: &[&str] = &[ 10 | /// "foo", 11 | /// "fooooo", 12 | /// "bar", 13 | /// "baz", 14 | /// ]; 15 | /// 16 | /// const ARE_0_0_EQ: bool = str_eq(STRS[0], STRS[0]); 17 | /// const ARE_0_1_EQ: bool = str_eq(STRS[0], STRS[1]); 18 | /// 19 | /// const ARE_1_1_EQ: bool = str_eq(STRS[1], STRS[1]); 20 | /// const ARE_1_2_EQ: bool = str_eq(STRS[1], STRS[2]); 21 | /// 22 | /// const ARE_2_2_EQ: bool = str_eq(STRS[2], STRS[2]); 23 | /// const ARE_2_3_EQ: bool = str_eq(STRS[2], STRS[3]); 24 | /// 25 | /// assert!( ARE_0_0_EQ ); 26 | /// assert!( !ARE_0_1_EQ ); 27 | /// 28 | /// assert!( ARE_1_1_EQ ); 29 | /// assert!( !ARE_1_2_EQ ); 30 | /// 31 | /// assert!( ARE_2_2_EQ ); 32 | /// assert!( !ARE_2_3_EQ ); 33 | /// 34 | /// ``` 35 | /// 36 | pub const fn str_eq(left: &str, right: &str) -> bool { 37 | u8_slice_eq(left.as_bytes(), right.as_bytes()) 38 | } 39 | 40 | /// A const equivalent of `&[u8]` equality comparison. 41 | /// 42 | /// # Example 43 | /// 44 | #[cfg_attr(feature = "fmt", doc = "```rust")] 45 | #[cfg_attr(not(feature = "fmt"), doc = "```ignore")] 46 | /// use const_format::utils::u8_slice_eq; 47 | /// 48 | /// const SLICES: &[&[u8]] = &[ 49 | /// &[10, 20], 50 | /// &[10, 20, 30, 40], 51 | /// &[3, 5, 8, 13], 52 | /// &[4, 9, 16, 25], 53 | /// ]; 54 | /// 55 | /// const ARE_0_0_EQ: bool = u8_slice_eq(SLICES[0], SLICES[0]); 56 | /// const ARE_0_1_EQ: bool = u8_slice_eq(SLICES[0], SLICES[1]); 57 | /// 58 | /// const ARE_1_1_EQ: bool = u8_slice_eq(SLICES[1], SLICES[1]); 59 | /// const ARE_1_2_EQ: bool = u8_slice_eq(SLICES[1], SLICES[2]); 60 | /// 61 | /// const ARE_2_2_EQ: bool = u8_slice_eq(SLICES[2], SLICES[2]); 62 | /// const ARE_2_3_EQ: bool = u8_slice_eq(SLICES[2], SLICES[3]); 63 | /// 64 | /// assert!( ARE_0_0_EQ ); 65 | /// assert!( !ARE_0_1_EQ ); 66 | /// 67 | /// assert!( ARE_1_1_EQ ); 68 | /// assert!( !ARE_1_2_EQ ); 69 | /// 70 | /// assert!( ARE_2_2_EQ ); 71 | /// assert!( !ARE_2_3_EQ ); 72 | /// 73 | /// ``` 74 | /// 75 | pub const fn u8_slice_eq(left: &[u8], right: &[u8]) -> bool { 76 | if left.len() != right.len() { 77 | return false; 78 | } 79 | 80 | let mut i = 0; 81 | while i != left.len() { 82 | if left[i] != right[i] { 83 | return false; 84 | } 85 | i += 1; 86 | } 87 | 88 | true 89 | } 90 | 91 | #[cfg(test)] 92 | mod tests { 93 | use super::*; 94 | 95 | #[test] 96 | fn slice_eq_test() { 97 | assert!(u8_slice_eq(&[], &[])); 98 | assert!(!u8_slice_eq(&[], &[0])); 99 | assert!(!u8_slice_eq(&[0], &[])); 100 | assert!(u8_slice_eq(&[0], &[0])); 101 | assert!(!u8_slice_eq(&[0], &[1])); 102 | assert!(!u8_slice_eq(&[1], &[0])); 103 | assert!(!u8_slice_eq(&[0], &[0, 1])); 104 | assert!(!u8_slice_eq(&[0, 1], &[0])); 105 | assert!(u8_slice_eq(&[0, 1], &[0, 1])); 106 | assert!(!u8_slice_eq(&[0, 1], &[0, 2])); 107 | } 108 | 109 | #[test] 110 | fn str_eq_test() { 111 | assert!(str_eq("", "")); 112 | assert!(!str_eq("", "0")); 113 | assert!(!str_eq("0", "")); 114 | assert!(str_eq("0", "0")); 115 | assert!(!str_eq("0", "1")); 116 | assert!(!str_eq("1", "0")); 117 | assert!(!str_eq("0", "0, 1")); 118 | assert!(!str_eq("0, 1", "0")); 119 | assert!(!str_eq("0, 1", "1")); 120 | assert!(str_eq("0, 1", "0, 1")); 121 | assert!(!str_eq("0, 1", "0, 2")); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::spanned::Spans; 2 | 3 | use proc_macro2::{ 4 | Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream as TokenStream2, 5 | TokenTree as TokenTree2, 6 | }; 7 | 8 | use std::fmt::Display; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct Error { 12 | messages: Vec, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | enum CompileError { 17 | Basic { 18 | start_span: Span, 19 | end_span: Span, 20 | msg: String, 21 | }, 22 | #[cfg(feature = "derive")] 23 | Syn(TokenStream2), 24 | } 25 | 26 | impl Error { 27 | pub fn new(span: Span, msg: T) -> Self { 28 | Error { 29 | messages: vec![CompileError::Basic { 30 | start_span: span, 31 | end_span: span, 32 | msg: msg.to_string(), 33 | }], 34 | } 35 | } 36 | 37 | pub fn spanned(spans: Spans, msg: T) -> Self { 38 | Error { 39 | messages: vec![CompileError::Basic { 40 | start_span: spans.start, 41 | end_span: spans.end, 42 | msg: msg.to_string(), 43 | }], 44 | } 45 | } 46 | 47 | pub fn to_compile_error(&self) -> TokenStream2 { 48 | macro_rules! tokenstream{ 49 | ($($tt:expr),* $(,)*) => ({ 50 | let list: Vec = vec![ 51 | $($tt.into(),)* 52 | ]; 53 | list.into_iter().collect::() 54 | }) 55 | } 56 | 57 | self.messages 58 | .iter() 59 | .map(|em| match em { 60 | CompileError::Basic { 61 | start_span, 62 | end_span, 63 | msg, 64 | } => { 65 | let ts = tokenstream![ 66 | Ident::new("compile_error", *start_span), 67 | { 68 | let mut this = Punct::new('!', Spacing::Alone); 69 | this.set_span(*start_span); 70 | this 71 | }, 72 | { 73 | let mut group = Group::new( 74 | Delimiter::Parenthesis, 75 | tokenstream![{ 76 | let mut lit = Literal::string(msg); 77 | lit.set_span(*end_span); 78 | TokenTree2::Literal(lit) 79 | }], 80 | ); 81 | group.set_span(*end_span); 82 | group 83 | }, 84 | ]; 85 | 86 | // Still have no idea why the compile_error has to be wrapped in parentheses 87 | // so that the spans point at the stuff between start_span and end_span. 88 | let mut this = Group::new(Delimiter::Parenthesis, ts); 89 | this.set_span(*end_span); 90 | tokenstream![this] 91 | } 92 | #[cfg(feature = "derive")] 93 | CompileError::Syn(x) => x.clone(), 94 | }) 95 | .collect() 96 | } 97 | 98 | pub fn combine(&mut self, another: Error) { 99 | self.messages.extend(another.messages) 100 | } 101 | } 102 | 103 | #[cfg(feature = "derive")] 104 | impl From for Error { 105 | fn from(err: syn::Error) -> Self { 106 | Self { 107 | messages: vec![CompileError::Syn(err.to_compile_error())], 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/format_str/errors.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | 3 | use std::{ 4 | fmt::{self, Display}, 5 | ops::Range, 6 | }; 7 | 8 | #[derive(Debug, PartialEq)] 9 | pub(crate) struct ParseError { 10 | pub(crate) pos: usize, 11 | pub(crate) kind: ParseErrorKind, 12 | } 13 | 14 | #[derive(Debug, PartialEq)] 15 | pub(crate) enum ParseErrorKind { 16 | /// A `{` that wasn't closed. 17 | UnclosedArg, 18 | /// A `}` that doesn't close an argument. 19 | InvalidClosedArg, 20 | /// When parsing the number of a positional arguments 21 | NotANumber { 22 | what: String, 23 | }, 24 | /// When parsing the identifier of a named argument 25 | NotAnIdent { 26 | what: String, 27 | }, 28 | UnknownFormatting { 29 | what: String, 30 | }, 31 | } 32 | 33 | #[allow(dead_code)] 34 | impl ParseErrorKind { 35 | pub fn not_a_number(what: &str) -> Self { 36 | Self::NotANumber { 37 | what: what.to_string(), 38 | } 39 | } 40 | pub fn not_an_ident(what: &str) -> Self { 41 | Self::NotAnIdent { 42 | what: what.to_string(), 43 | } 44 | } 45 | pub fn unknown_formatting(what: &str) -> Self { 46 | Self::UnknownFormatting { 47 | what: what.to_string(), 48 | } 49 | } 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | #[derive(Debug, PartialEq)] 55 | pub(crate) struct DisplayParseError<'a> { 56 | pub(crate) str: &'a str, 57 | pub(crate) error_span: Range, 58 | pub(crate) kind: ParseErrorKind, 59 | } 60 | impl ParseError { 61 | fn error_span(&self) -> Range { 62 | let len = match &self.kind { 63 | ParseErrorKind::UnclosedArg => 0, 64 | ParseErrorKind::InvalidClosedArg => 0, 65 | ParseErrorKind::NotANumber { what } => what.len(), 66 | ParseErrorKind::NotAnIdent { what } => what.len(), 67 | ParseErrorKind::UnknownFormatting { what } => what.len(), 68 | }; 69 | 70 | self.pos..self.pos + len 71 | } 72 | 73 | pub(crate) fn into_crate_err(self, span: Span, original_str: &str) -> crate::Error { 74 | let display = DisplayParseError { 75 | str: original_str, 76 | error_span: self.error_span(), 77 | kind: self.kind, 78 | }; 79 | 80 | crate::Error::new(span, display) 81 | } 82 | } 83 | 84 | impl Display for ParseErrorKind { 85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 86 | match self { 87 | ParseErrorKind::UnclosedArg => f.write_str("unclosed argument"), 88 | ParseErrorKind::InvalidClosedArg => f.write_str("`}` closing a nonexistent argument"), 89 | ParseErrorKind::NotANumber { what } => writeln!(f, "not a number: \"{}\"", what), 90 | ParseErrorKind::NotAnIdent { what } => { 91 | writeln!(f, "not a valid identifier: \"{}\"", what) 92 | } 93 | ParseErrorKind::UnknownFormatting { what } => { 94 | writeln!(f, "unknown formatting: \"{}\"", what) 95 | } 96 | } 97 | } 98 | } 99 | 100 | impl Display for DisplayParseError<'_> { 101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 102 | f.write_str("failed to parse the format string ")?; 103 | 104 | // Gets the amount of chars up to the error, 105 | // this is good enough for most cases, 106 | // but doesn't acount for multi-char characters. 107 | let chars = self.str[..self.error_span.start].chars().count(); 108 | writeln!(f, "at the character number {}, ", chars)?; 109 | 110 | Display::fmt(&self.kind, f)?; 111 | 112 | Ok(()) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/str_replace.rs: -------------------------------------------------------------------------------- 1 | use super::{bytes_find, Pattern, PatternCtor, PatternNorm}; 2 | 3 | pub struct ReplaceInputConv(pub &'static str, pub T, pub &'static str); 4 | 5 | macro_rules! ctor { 6 | ($ty:ty) => { 7 | impl ReplaceInputConv<$ty> { 8 | pub const fn conv(self) -> ReplaceInput { 9 | ReplaceInput { 10 | str: self.0, 11 | pattern: PatternCtor(self.1).conv(), 12 | replaced_with: self.2, 13 | } 14 | } 15 | } 16 | }; 17 | } 18 | 19 | ctor! {u8} 20 | ctor! {&'static str} 21 | ctor! {char} 22 | 23 | pub struct ReplaceInput { 24 | str: &'static str, 25 | pattern: Pattern, 26 | replaced_with: &'static str, 27 | } 28 | 29 | impl ReplaceInput { 30 | pub const fn replace_length(&self) -> usize { 31 | str_replace_length(self.str, self.pattern, self.replaced_with) 32 | } 33 | pub const fn replace(&self) -> [u8; L] { 34 | str_replace(self.str, self.pattern, self.replaced_with) 35 | } 36 | } 37 | 38 | const fn str_replace_length(inp: &str, r: Pattern, replaced_with: &str) -> usize { 39 | let inp = inp.as_bytes(); 40 | 41 | let replaced_len = replaced_with.len(); 42 | let mut out_len = 0; 43 | 44 | match r.normalize() { 45 | PatternNorm::AsciiByte(byte) => { 46 | let byte = byte.get(); 47 | iter_copy_slice! {b in inp => 48 | out_len += if b == byte { replaced_len } else { 1 }; 49 | } 50 | } 51 | PatternNorm::Str(str) => { 52 | if str.is_empty() { 53 | return inp.len(); 54 | } 55 | let str_len = str.len(); 56 | let mut i = 0; 57 | while let Some(next_match) = bytes_find(inp, str, i) { 58 | out_len += (next_match - i) + replaced_len; 59 | i = next_match + str_len; 60 | } 61 | out_len += inp.len() - i; 62 | } 63 | } 64 | 65 | out_len 66 | } 67 | 68 | const fn str_replace(inp: &str, r: Pattern, replaced_with: &str) -> [u8; L] { 69 | let inp = inp.as_bytes(); 70 | 71 | let replaced_with_bytes = replaced_with.as_bytes(); 72 | let mut out = [0u8; L]; 73 | let mut out_i = 0; 74 | 75 | macro_rules! write_replaced { 76 | () => { 77 | iter_copy_slice! {b in replaced_with_bytes => 78 | out[out_i] = b; 79 | out_i += 1; 80 | } 81 | }; 82 | } 83 | macro_rules! write_byte { 84 | ($byte:expr) => { 85 | out[out_i] = $byte; 86 | out_i += 1; 87 | }; 88 | } 89 | 90 | match r.normalize() { 91 | PatternNorm::AsciiByte(byte) => { 92 | let byte = byte.get(); 93 | iter_copy_slice! {b in inp => 94 | if b == byte { 95 | write_replaced!{} 96 | } else { 97 | write_byte!{b} 98 | } 99 | } 100 | } 101 | PatternNorm::Str(str) => { 102 | if str.is_empty() { 103 | iter_copy_slice! {b in inp => 104 | write_byte!(b); 105 | } 106 | return out; 107 | } 108 | let str_len = str.len(); 109 | let mut i = 0; 110 | while let Some(next_match) = bytes_find(inp, str, i) { 111 | __for_range! {j in i..next_match => 112 | write_byte!(inp[j]); 113 | } 114 | write_replaced! {} 115 | 116 | i = next_match + str_len; 117 | } 118 | __for_range! {j in i..inp.len() => 119 | write_byte!(inp[j]); 120 | } 121 | } 122 | } 123 | out 124 | } 125 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::or_fun_call)] 2 | #![allow(clippy::derive_partial_eq_without_eq)] 3 | 4 | use proc_macro::TokenStream as TokenStream1; 5 | 6 | use proc_macro2::TokenStream as TokenStream2; 7 | 8 | #[cfg(feature = "derive")] 9 | #[macro_use] 10 | mod macros; 11 | 12 | #[cfg(feature = "derive")] 13 | mod datastructure; 14 | 15 | mod error; 16 | 17 | #[cfg(feature = "derive")] 18 | mod derive_debug; 19 | 20 | mod format_args; 21 | 22 | mod format_str; 23 | 24 | mod format_macro; 25 | 26 | mod formatting; 27 | 28 | mod parse_utils; 29 | 30 | mod respan_to_macro; 31 | 32 | mod shared_arg_parsing; 33 | 34 | mod spanned; 35 | 36 | mod utils; 37 | 38 | #[cfg(test)] 39 | mod test_utils; 40 | 41 | use crate::error::Error; 42 | use crate::parse_utils::MyParse; 43 | 44 | fn compile_err_empty_str(e: crate::Error) -> TokenStream2 { 45 | let e = e.to_compile_error(); 46 | quote::quote!({ 47 | #e; 48 | "" 49 | }) 50 | } 51 | 52 | #[doc(hidden)] 53 | #[proc_macro] 54 | pub fn __concatcp_impl(input: TokenStream1) -> TokenStream1 { 55 | MyParse::parse_token_stream_1(input) 56 | .and_then(format_macro::concatcp_impl) 57 | .unwrap_or_else(compile_err_empty_str) 58 | .into() 59 | } 60 | 61 | /// Input syntax: `"format string", (arg0), (name = arg1)` (with optional trailing comma). 62 | /// 63 | /// The arguments are parenthesized to not require syn to parse `arg0` and `arg1` as syn::Expr, 64 | /// they're just parsed as a `TokenStream2`. 65 | /// 66 | /// They're guaranteed to be expressions when this macro is invoked by `const_format` macros, 67 | /// which should be the only ones to do so. 68 | #[doc(hidden)] 69 | #[proc_macro] 70 | pub fn __formatcp_impl(input: TokenStream1) -> TokenStream1 { 71 | MyParse::parse_token_stream_1(input) 72 | .and_then(format_macro::formatcp_impl) 73 | .unwrap_or_else(compile_err_empty_str) 74 | .into() 75 | } 76 | 77 | #[doc(hidden)] 78 | #[proc_macro] 79 | pub fn __formatc_impl(input: TokenStream1) -> TokenStream1 { 80 | MyParse::parse_token_stream_1(input) 81 | .and_then(format_macro::formatc_macro_impl) 82 | .unwrap_or_else(compile_err_empty_str) 83 | .into() 84 | } 85 | 86 | #[doc(hidden)] 87 | #[proc_macro] 88 | pub fn __formatc_if_impl(input: TokenStream1) -> TokenStream1 { 89 | MyParse::parse_token_stream_1(input) 90 | .and_then(format_macro::formatc_if_macro_impl) 91 | .unwrap_or_else(compile_err_empty_str) 92 | .into() 93 | } 94 | 95 | #[doc(hidden)] 96 | #[proc_macro] 97 | pub fn __formatcp_if_impl(input: TokenStream1) -> TokenStream1 { 98 | MyParse::parse_token_stream_1(input) 99 | .and_then(format_macro::formatcp_if_macro_impl) 100 | .unwrap_or_else(compile_err_empty_str) 101 | .into() 102 | } 103 | 104 | #[doc(hidden)] 105 | #[proc_macro] 106 | pub fn __writec_impl(input: TokenStream1) -> TokenStream1 { 107 | MyParse::parse_token_stream_1(input) 108 | .and_then(format_macro::writec_macro_impl) 109 | .unwrap_or_else(|e| { 110 | let e = e.to_compile_error(); 111 | quote::quote!({ 112 | #e; 113 | ::core::result::Result::Ok(()) 114 | }) 115 | }) 116 | .into() 117 | } 118 | 119 | #[cfg(feature = "derive")] 120 | #[proc_macro_derive(ConstDebug, attributes(cdeb))] 121 | pub fn derive_const_debug(input: TokenStream1) -> TokenStream1 { 122 | syn::parse(input) 123 | .map_err(crate::Error::from) 124 | .and_then(derive_debug::derive_constdebug_impl) 125 | .unwrap_or_else(|e| e.to_compile_error()) 126 | .into() 127 | } 128 | 129 | /// `__respan_to!(( foo tokens ) bar tokens )` 130 | /// Respan all the bar tokens to the span of the foo tokens 131 | #[proc_macro] 132 | pub fn respan_to(input: TokenStream1) -> TokenStream1 { 133 | crate::respan_to_macro::implementation(input.into()).into() 134 | } 135 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/shared_cp_macro_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::{__identity, concatcp, formatcp}; 2 | 3 | #[cfg(feature = "fmt")] 4 | use cfmt_b::{concatc, formatc}; 5 | 6 | use arrayvec::ArrayString; 7 | 8 | use core::fmt::Write; 9 | 10 | macro_rules! tests { 11 | ( 12 | format_str = $format_str:literal, 13 | $($expr:expr,)* 14 | ) => ( 15 | const ALL_TYS_F: &'static str = formatcp!( $format_str, $($expr,)* ); 16 | const ALL_TYS: &'static str = concatcp!( $($expr,)* ); 17 | 18 | #[cfg(feature = "fmt")] 19 | const ALL_TYS_C: &'static str = concatc!( $($expr,)* ); 20 | 21 | #[test] 22 | fn all_types() { 23 | let mut string = ArrayString::<1024>::new(); 24 | $( 25 | write!(string, "{}", $expr).unwrap(); 26 | )* 27 | assert_eq!(string.as_str(), ALL_TYS); 28 | 29 | assert_eq!(string.as_str(), ALL_TYS_F); 30 | 31 | #[cfg(feature = "fmt")] 32 | assert_eq!(string.as_str(), ALL_TYS_C); 33 | } 34 | 35 | #[test] 36 | fn each_type(){ 37 | $({ 38 | const VALUE_F: &'static str = formatcp!("{}", $expr); 39 | const VALUE: &'static str = concatcp!($expr); 40 | 41 | let mut string = ArrayString::<64>::new(); 42 | write!(string, "{}", $expr).unwrap(); 43 | 44 | assert_eq!(string.as_str(), VALUE); 45 | assert_eq!(string.as_str(), VALUE_F); 46 | })* 47 | } 48 | ) 49 | } 50 | 51 | tests! { 52 | format_str = "\ 53 | {}{}{}{}{}{}{}{}\ 54 | {}{}{}{}{}{}{}{}\ 55 | {}{}{}{}{}{}{}{}\ 56 | {}{}{}{}{}{}{}{}\ 57 | {}{}{}{}{}{}{}{}\ 58 | {}{}{}{}{}{}{}{}\ 59 | {}{}{}{}{}{}{}{}\ 60 | ", 61 | 62 | i8::MIN, " ", i8::MAX, " ", 63 | i16::MIN, " ", i16::MAX, " ", 64 | i32::MIN, " ", i32::MAX, " ", 65 | i64::MIN, " ", i64::MAX, " ", 66 | i128::MIN, " ", i128::MAX, " ", 67 | isize::MIN, " ", isize::MAX, " ", 68 | "!Aq¡€🧡🧠₀₁", 69 | "", 70 | u8::MIN, " ", u8::MAX, " ", 71 | u16::MIN, " ", u16::MAX, " ", 72 | u32::MIN, " ", u32::MAX, " ", 73 | u64::MIN, " ", u64::MAX, " ", 74 | u128::MIN, " ", u128::MAX, " ", 75 | usize::MIN, " ", usize::MAX, " ", 76 | false, true, 77 | 'o', 'ñ', '个', '\u{100000}', 78 | } 79 | 80 | #[test] 81 | fn other_tests() { 82 | { 83 | const EMPTY: &str = concatcp!(); 84 | assert_eq!(EMPTY, ""); 85 | } 86 | 87 | #[cfg(feature = "fmt")] 88 | { 89 | const EMPTY: &str = concatc!(); 90 | assert_eq!(EMPTY, ""); 91 | } 92 | } 93 | 94 | // test that this error doesn't happen: 95 | // https://github.com/rodrimati1992/const_format_crates/issues/36 96 | #[test] 97 | fn call_in_braced_macro() { 98 | { 99 | assert_eq!( 100 | { 101 | __identity! {concatcp!("a", "b")} 102 | }, 103 | "ab" 104 | ); 105 | { 106 | __identity! {concatcp!("a", "b")} 107 | }; 108 | __identity! {concatcp!("a", "b")}; 109 | } 110 | 111 | #[cfg(feature = "fmt")] 112 | { 113 | assert_eq!( 114 | { 115 | __identity! {concatc!("a", "b")} 116 | }, 117 | "ab" 118 | ); 119 | { 120 | __identity! {concatc!("a", "b")} 121 | }; 122 | __identity! {concatc!("a", "b")}; 123 | } 124 | 125 | { 126 | assert_eq!( 127 | { 128 | __identity! {formatcp!("ab")} 129 | }, 130 | "ab" 131 | ); 132 | { 133 | __identity! {formatcp!("ab")} 134 | }; 135 | __identity! {formatcp!("ab")}; 136 | } 137 | 138 | #[cfg(feature = "fmt")] 139 | { 140 | assert_eq!( 141 | { 142 | __identity! {formatc!("ab")} 143 | }, 144 | "ab" 145 | ); 146 | { 147 | __identity! {formatc!("ab")} 148 | }; 149 | __identity! {formatc!("ab")}; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/formatting.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; 2 | 3 | use quote::{quote, ToTokens, TokenStreamExt}; 4 | 5 | #[derive(Debug, Copy, Clone, PartialEq)] 6 | pub(crate) enum Formatting { 7 | Debug(NumberFormatting), 8 | Display, 9 | } 10 | 11 | #[derive(Debug, Copy, Clone, PartialEq)] 12 | pub(crate) enum NumberFormatting { 13 | Decimal, 14 | Hexadecimal, 15 | LowerHexadecimal, 16 | Binary, 17 | } 18 | 19 | impl NumberFormatting { 20 | pub(crate) fn is_regular(self) -> bool { 21 | matches!(self, NumberFormatting::Decimal) 22 | } 23 | } 24 | 25 | impl ToTokens for NumberFormatting { 26 | fn to_tokens(&self, ts: &mut TokenStream2) { 27 | ts.append_all(match self { 28 | Self::Decimal => return, 29 | Self::Hexadecimal => quote!(.set_hexadecimal()), 30 | Self::LowerHexadecimal => quote!(.set_lower_hexadecimal()), 31 | Self::Binary => quote!(.set_binary()), 32 | }); 33 | } 34 | } 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | #[derive(Debug, Copy, Clone, PartialEq)] 39 | pub(crate) enum IsAlternate { 40 | Yes, 41 | No, 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | 46 | #[derive(Debug, Copy, Clone, PartialEq)] 47 | pub(crate) struct FormattingFlags { 48 | pub(crate) formatting: Formatting, 49 | pub(crate) is_alternate: IsAlternate, 50 | } 51 | 52 | impl FormattingFlags { 53 | #[inline] 54 | pub(crate) const fn display(is_alternate: IsAlternate) -> Self { 55 | Self { 56 | formatting: Formatting::Display, 57 | is_alternate, 58 | } 59 | } 60 | 61 | #[inline] 62 | pub(crate) const fn debug(num_fmt: NumberFormatting, is_alternate: IsAlternate) -> Self { 63 | Self { 64 | formatting: Formatting::Debug(num_fmt), 65 | is_alternate, 66 | } 67 | } 68 | } 69 | 70 | impl FormattingFlags { 71 | pub(crate) fn to_pargument_method_name(self) -> Ident { 72 | let name = match self.formatting { 73 | Formatting::Display => "to_pargument_display", 74 | Formatting::Debug { .. } => "to_pargument_debug", 75 | }; 76 | 77 | Ident::new(name, Span::mixed_site()) 78 | } 79 | 80 | #[allow(dead_code)] 81 | pub(crate) fn fmt_method_name(self) -> Ident { 82 | let name = match self.formatting { 83 | Formatting::Display => "const_display_fmt", 84 | Formatting::Debug { .. } => "const_debug_fmt", 85 | }; 86 | 87 | Ident::new(name, Span::mixed_site()) 88 | } 89 | 90 | #[allow(dead_code)] 91 | pub(crate) fn len_method_name(self) -> Ident { 92 | let name = match self.formatting { 93 | Formatting::Display => "const_display_fmt", 94 | Formatting::Debug { .. } => "const_debug_fmt", 95 | }; 96 | 97 | Ident::new(name, Span::mixed_site()) 98 | } 99 | } 100 | 101 | impl ToTokens for FormattingFlags { 102 | fn to_tokens(&self, ts: &mut TokenStream2) { 103 | use self::{IsAlternate as IA, NumberFormatting as FM}; 104 | 105 | let formatting = match self.formatting { 106 | Formatting::Display => NumberFormatting::Decimal, 107 | Formatting::Debug(num_fmt) => num_fmt, 108 | }; 109 | 110 | ts.append_all(match (self.is_alternate, formatting) { 111 | (IA::No, FM::Decimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__REG), 112 | (IA::No, FM::Hexadecimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__HEX), 113 | (IA::No, FM::LowerHexadecimal) => { 114 | quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__LOWHEX) 115 | } 116 | (IA::No, FM::Binary) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__BIN), 117 | (IA::Yes, FM::Decimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_REG), 118 | (IA::Yes, FM::Hexadecimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_HEX), 119 | (IA::Yes, FM::LowerHexadecimal) => { 120 | quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_LOWHEX) 121 | } 122 | (IA::Yes, FM::Binary) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_BIN), 123 | }); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /const_format/tests/str_methods_modules/str_split_tests.rs: -------------------------------------------------------------------------------- 1 | use const_format::{str_split, str_split_pat}; 2 | 3 | #[test] 4 | fn test_str_split_pat_basic_equivalence() { 5 | assert_eq!(str_split_pat!("fob", "XY"), &["fob"][..]); 6 | assert_eq!(str_split_pat!("XYfob", "XY"), &["", "fob"][..]); 7 | assert_eq!(str_split_pat!("XYfobXY", "XY"), &["", "fob", ""][..]); 8 | assert_eq!( 9 | str_split_pat!("fooXYbarXYbaz", "XY"), 10 | &["foo", "bar", "baz"][..] 11 | ); 12 | assert_eq!( 13 | str_split_pat!("fooXY bar XYbaz", "XY"), 14 | &["foo", " bar ", "baz"][..] 15 | ); 16 | } 17 | 18 | #[test] 19 | fn test_str_split_with_empty_str_arg() { 20 | assert_eq!(str_split!("", ""), ["", ""]); 21 | assert_eq!(str_split!("f", ""), ["", "f", ""]); 22 | assert_eq!(str_split!("fo", ""), ["", "f", "o", ""]); 23 | assert_eq!(str_split!("fob", ""), ["", "f", "o", "b", ""]); 24 | 25 | assert_eq!( 26 | str_split!("!Aq¡€🧡🧠₀₁oñ个", ""), 27 | ["", "!", "A", "q", "¡", "", "€", "🧡", "🧠", "₀", "₁", "o", "ñ", "个", ""], 28 | ); 29 | } 30 | 31 | #[test] 32 | fn test_str_split_with_space_str_arg() { 33 | assert_eq!(str_split!("fob", " "), ["fob"]); 34 | assert_eq!(str_split!(" fob", " "), ["", "fob"]); 35 | assert_eq!(str_split!(" fob ", " "), ["", "fob", ""]); 36 | assert_eq!(str_split!("foo bar baz", " "), ["foo", "bar", "baz"]); 37 | assert_eq!(str_split!("foo bar baz", " "), ["foo", "", "bar", "baz"]); 38 | } 39 | 40 | #[test] 41 | fn test_str_split_with_dash_str_arg() { 42 | assert_eq!(str_split!("fob", "-"), ["fob"]); 43 | assert_eq!(str_split!("-fob", "-"), ["", "fob"]); 44 | assert_eq!(str_split!("-fob-", "-"), ["", "fob", ""]); 45 | assert_eq!(str_split!("foo-bar-baz", "-"), ["foo", "bar", "baz"]); 46 | assert_eq!(str_split!("foo--bar-baz", "-"), ["foo", "", "bar", "baz"]); 47 | } 48 | 49 | #[test] 50 | fn test_str_split_with_word_arg() { 51 | assert_eq!(str_split!("fob", "XY"), ["fob"]); 52 | assert_eq!(str_split!("XYfob", "XY"), ["", "fob"]); 53 | assert_eq!(str_split!("XYfobXY", "XY"), ["", "fob", ""]); 54 | assert_eq!(str_split!("fooXYbarXYbaz", "XY"), ["foo", "bar", "baz"]); 55 | assert_eq!(str_split!("fooXY bar XYbaz", "XY"), ["foo", " bar ", "baz"]); 56 | } 57 | 58 | #[test] 59 | fn test_str_split_with_ascii_char_arg() { 60 | assert_eq!(str_split!("fob", '-'), ["fob"]); 61 | assert_eq!(str_split!("-fob", '-'), ["", "fob"]); 62 | assert_eq!(str_split!("-fob-", '-'), ["", "fob", ""]); 63 | assert_eq!(str_split!("foo-bar-baz", '-'), ["foo", "bar", "baz"]); 64 | assert_eq!(str_split!("foo- bar -baz", '-'), ["foo", " bar ", "baz"]); 65 | } 66 | 67 | #[test] 68 | fn test_str_split_with_non_ascii_char_arg() { 69 | { 70 | assert_eq!(''.len_utf8(), 1); 71 | assert_eq!(str_split!("fob", ''), ["fob"]); 72 | assert_eq!(str_split!("fob", ''), ["", "fob"]); 73 | assert_eq!(str_split!("fob", ''), ["", "fob", ""]); 74 | assert_eq!(str_split!("foobarbaz", ''), ["foo", "bar", "baz"]); 75 | assert_eq!(str_split!("foo bar baz", ''), ["foo", " bar ", "baz"]); 76 | } 77 | { 78 | assert_eq!('ñ'.len_utf8(), 2); 79 | assert_eq!(str_split!("fob", 'ñ'), ["fob"]); 80 | assert_eq!(str_split!("ñfob", 'ñ'), ["", "fob"]); 81 | assert_eq!(str_split!("ñfobñ", 'ñ'), ["", "fob", ""]); 82 | assert_eq!(str_split!("fooñbarñbaz", 'ñ'), ["foo", "bar", "baz"]); 83 | assert_eq!(str_split!("fooñ bar ñbaz", 'ñ'), ["foo", " bar ", "baz"]); 84 | } 85 | { 86 | assert_eq!('₀'.len_utf8(), 3); 87 | assert_eq!(str_split!("fob", '₀'), ["fob"]); 88 | assert_eq!(str_split!("₀fob", '₀'), ["", "fob"]); 89 | assert_eq!(str_split!("₀fob₀", '₀'), ["", "fob", ""]); 90 | assert_eq!(str_split!("foo₀bar₀baz", '₀'), ["foo", "bar", "baz"]); 91 | assert_eq!(str_split!("foo₀ bar ₀baz", '₀'), ["foo", " bar ", "baz"]); 92 | } 93 | { 94 | assert_eq!('🧡'.len_utf8(), 4); 95 | assert_eq!(str_split!("fob", '🧡'), ["fob"]); 96 | assert_eq!(str_split!("🧡fob", '🧡'), ["", "fob"]); 97 | assert_eq!(str_split!("🧡fob🧡", '🧡'), ["", "fob", ""]); 98 | assert_eq!(str_split!("foo🧡bar🧡baz", '🧡'), ["foo", "bar", "baz"]); 99 | assert_eq!(str_split!("foo🧡 bar 🧡baz", '🧡'), ["foo", " bar ", "baz"]); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/format_args.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | format_str::FormatStr, formatting::FormattingFlags, parse_utils::StrRawness, 3 | parse_utils::TokenStream2Ext, shared_arg_parsing::ExprArg, spanned::Spans, 4 | }; 5 | 6 | use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; 7 | 8 | use quote::{quote_spanned, TokenStreamExt}; 9 | 10 | //////////////////////////////////////////////// 11 | 12 | mod parsing; 13 | 14 | //////////////////////////////////////////////// 15 | 16 | struct UncheckedFormatArgs { 17 | literal: FormatStr, 18 | args: Vec, 19 | } 20 | 21 | struct UncheckedFormatArg { 22 | pub(crate) spans: Spans, 23 | pub(crate) ident: Option, 24 | // The identifier for the Formatter passed to format the argument. 25 | // If this is Some, then `expr` is expanded directly, 26 | pub(crate) fmt_ident: Option, 27 | /// Using a TokenStream2 because it is validated to be a valid expression in 28 | /// the macro_rules! macros that call these proc macros. 29 | pub(crate) expr: TokenStream2, 30 | } 31 | 32 | pub(crate) struct FormatArgs { 33 | pub(crate) condition: Option, 34 | pub(crate) local_variables: Vec, 35 | pub(crate) expanded_into: Vec, 36 | } 37 | 38 | pub(crate) struct FormatIfArgs { 39 | pub(crate) inner: FormatArgs, 40 | } 41 | 42 | /// The arguments of `writec` 43 | pub(crate) struct WriteArgs { 44 | pub(crate) writer_expr: TokenStream2, 45 | pub(crate) writer_span: Span, 46 | pub(crate) format_args: FormatArgs, 47 | } 48 | 49 | pub(crate) enum ExpandInto { 50 | Str(String, StrRawness), 51 | Formatted(ExpandFormatted), 52 | WithFormatter(ExpandWithFormatter), 53 | } 54 | 55 | pub(crate) struct ExpandFormatted { 56 | pub(crate) format: FormattingFlags, 57 | pub(crate) local_variable: Ident, 58 | } 59 | 60 | pub(crate) struct ExpandWithFormatter { 61 | pub(crate) format: FormattingFlags, 62 | pub(crate) fmt_ident: Ident, 63 | pub(crate) expr: TokenStream2, 64 | } 65 | 66 | pub(crate) struct LocalVariable { 67 | // The local variable that the macro will output for this argument, 68 | // so that it is not evaluated multiple times when it's used multiple times 69 | // in the format string. 70 | pub(crate) ident: Ident, 71 | /// Using a TokenStream2 because it is validated to be a valid expression in 72 | /// the macro_rules! macros that call these proc macros. 73 | pub(crate) expr: TokenStream2, 74 | } 75 | 76 | pub(crate) enum FormatArg { 77 | WithFormatter { 78 | // The identifier for the Formatter passed to format the argument. 79 | // If this is Some, then `expr` is expanded directly, 80 | fmt_ident: Ident, 81 | /// Using a TokenStream2 because it is validated to be a valid expression in 82 | /// the macro_rules! macros that call these proc macros. 83 | expr: TokenStream2, 84 | }, 85 | WithLocal(Ident), 86 | } 87 | 88 | //////////////////////////////////////////////// 89 | 90 | impl ExpandInto { 91 | pub(crate) fn fmt_call(&self, formatter: &Ident) -> TokenStream2 { 92 | match self { 93 | ExpandInto::Str(str, rawness) => { 94 | let str_tokens = rawness.tokenize_sub(str); 95 | 96 | quote_spanned!(rawness.span()=> #formatter.write_str(#str_tokens) ) 97 | } 98 | ExpandInto::Formatted(fmted) => { 99 | let flags = fmted.format; 100 | let fmt_method = fmted.format.fmt_method_name(); 101 | let local_variable = &fmted.local_variable; 102 | let span = local_variable.span(); 103 | 104 | let mut tokens = quote::quote!( 105 | __cf_osRcTFl4A::coerce_to_fmt!(&#local_variable) 106 | .#fmt_method 107 | ) 108 | .set_span_recursive(span); 109 | 110 | tokens.append_all(quote::quote!( (&mut #formatter.make_formatter(#flags)) )); 111 | 112 | tokens 113 | } 114 | ExpandInto::WithFormatter(ExpandWithFormatter { 115 | format, 116 | fmt_ident, 117 | expr, 118 | }) => quote::quote!({ 119 | let #fmt_ident = &mut #formatter.make_formatter(#format); 120 | __cf_osRcTFl4A::pmr::ToResult( #expr ).to_result() 121 | }), 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/equality_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::coerce_to_fmt; 2 | 3 | use core::{ 4 | cmp::Ordering, 5 | num::{NonZeroU128, NonZeroU8, NonZeroUsize}, 6 | sync::atomic::Ordering as AtomicOrdering, 7 | }; 8 | 9 | macro_rules! compare_array_case { 10 | ( $first:expr, $second:expr, $third:expr; $foo:expr, $bar:expr ) => {{ 11 | assert!(!coerce_to_fmt!(&[$first, $second]).const_eq(&[$foo, $bar])); 12 | assert!(!coerce_to_fmt!(&[$first, $second]).const_eq(&[$first, $foo])); 13 | assert!(!coerce_to_fmt!(&[$first, $second]).const_eq(&[$first])); 14 | assert!(!coerce_to_fmt!(&[$first, $second]).const_eq(&[$first, $second, $third])); 15 | assert!(coerce_to_fmt!(&[$first, $second]).const_eq(&[$first, $second])); 16 | }}; 17 | } 18 | 19 | #[test] 20 | fn compare_arrays() { 21 | compare_array_case!("hello", "world", "cool"; "foo", "bar"); 22 | compare_array_case! {'a', 'A', 'Ñ'; '3', '2'} 23 | 24 | compare_array_case!(3u8, 5u8, 8u8; 13u8, 21u8); 25 | compare_array_case!(3u16, 5u16, 8u16; 13u16, 21u16); 26 | compare_array_case!(3usize, 5usize, 8usize; 13usize, 21usize); 27 | 28 | { 29 | assert!(!coerce_to_fmt!(&[false, false]).const_eq(&[false, true])); 30 | assert!(!coerce_to_fmt!(&[false, false]).const_eq(&[true, false])); 31 | assert!(!coerce_to_fmt!(&[true, true]).const_eq(&[false, true])); 32 | assert!(!coerce_to_fmt!(&[true, true]).const_eq(&[true, false])); 33 | assert!(!coerce_to_fmt!(&[false]).const_eq(&[true])); 34 | 35 | assert!(coerce_to_fmt!(&[true, true]).const_eq(&[true, true])); 36 | assert!(coerce_to_fmt!(&[false, false]).const_eq(&[false, false])); 37 | assert!(coerce_to_fmt!(&[false]).const_eq(&[false])); 38 | assert!(coerce_to_fmt!(&[] as &[bool]).const_eq(&[])); 39 | } 40 | } 41 | 42 | macro_rules! compare_option_case { 43 | ( $ty:ty ; $first:expr, $second:expr ) => {{ 44 | let first: $ty = $first; 45 | let second: $ty = $second; 46 | { 47 | assert!(!coerce_to_fmt!(first).const_eq(&second)); 48 | assert!(!coerce_to_fmt!(second).const_eq(&first)); 49 | 50 | assert!(coerce_to_fmt!(first).const_eq(&first)); 51 | assert!(coerce_to_fmt!(second).const_eq(&second)); 52 | } 53 | { 54 | assert!(!coerce_to_fmt!(Some(first)).const_eq(&Some(second))); 55 | 56 | assert!(!coerce_to_fmt!(Some(first)).const_eq(&Some(second))); 57 | assert!(!coerce_to_fmt!(Some(first)).const_eq(&None)); 58 | assert!(!coerce_to_fmt!(None::<$ty>).const_eq(&Some(first))); 59 | 60 | assert!(coerce_to_fmt!(Some(first)).const_eq(&Some(first))); 61 | assert!(coerce_to_fmt!(None::<$ty>).const_eq(&None)); 62 | } 63 | }}; 64 | } 65 | 66 | #[test] 67 | fn compare_options() { 68 | compare_option_case!(u8; 3, 5); 69 | compare_option_case!(u128; 3, 5); 70 | compare_option_case!(usize; 3, 5); 71 | 72 | compare_option_case!(NonZeroU8; NonZeroU8::new(3).unwrap(), NonZeroU8::new(5).unwrap()); 73 | 74 | compare_option_case!(NonZeroU128; NonZeroU128::new(3).unwrap(), NonZeroU128::new(5).unwrap()); 75 | 76 | compare_option_case!(NonZeroUsize; NonZeroUsize::new(3).unwrap(), NonZeroUsize::new(5).unwrap()); 77 | 78 | compare_option_case!(bool; false, true); 79 | 80 | compare_option_case!(&str; "foo", "bar"); 81 | } 82 | 83 | macro_rules! compare_cases { 84 | ($($value:expr),* $(,)* ) => ({ 85 | let cases = [$($value,)*]; 86 | 87 | for left in cases.iter() { 88 | for right in cases.iter() { 89 | assert_eq!(coerce_to_fmt!(left).const_eq(&right), left==right); 90 | } 91 | } 92 | }) 93 | } 94 | 95 | #[test] 96 | fn enums() { 97 | compare_cases! {Ordering::Less, Ordering::Equal, Ordering::Greater} 98 | compare_cases! { 99 | AtomicOrdering::Relaxed, 100 | AtomicOrdering::Acquire, 101 | AtomicOrdering::Release, 102 | AtomicOrdering::AcqRel, 103 | AtomicOrdering::SeqCst, 104 | } 105 | } 106 | 107 | #[test] 108 | fn char_cases() { 109 | compare_cases! {'a', 'A', 'Ñ', 'ñ'} 110 | } 111 | 112 | #[test] 113 | fn ranges() { 114 | compare_cases! {0..10, 5..10, 5..15, 0..15} 115 | compare_cases! {0..=10, 5..=10, 5..=15, 0..=15} 116 | compare_cases! {0.., 5..} 117 | compare_cases! {..} 118 | compare_cases! {..0, ..5} 119 | compare_cases! {..=0, ..=5} 120 | } 121 | -------------------------------------------------------------------------------- /const_format/src/char_encoding.rs: -------------------------------------------------------------------------------- 1 | use crate::formatting::{hex_as_ascii, HexFormatting}; 2 | 3 | #[cfg(any(test, feature = "fmt"))] 4 | pub(crate) const fn char_display_len(c: char) -> usize { 5 | match c as u32 { 6 | 0..=127 => 1, 7 | 0x80..=0x7FF => 2, 8 | 0x800..=0xFFFF => 3, 9 | 0x10000..=u32::MAX => 4, 10 | } 11 | } 12 | 13 | #[cfg(any(test, feature = "fmt"))] 14 | pub(crate) const fn char_debug_len(c: char) -> usize { 15 | let inner = match c { 16 | '\t' | '\r' | '\n' | '\\' | '\'' | '\"' => 2, 17 | '\x00'..='\x1F' => 4, 18 | _ => char_display_len(c), 19 | }; 20 | inner + 2 21 | } 22 | 23 | const fn char_to_utf8(char: char) -> ([u8; 4], usize) { 24 | let u32 = char as u32; 25 | match u32 { 26 | 0..=127 => ([u32 as u8, 0, 0, 0], 1), 27 | 0x80..=0x7FF => { 28 | let b0 = 0b1100_0000 | (u32 >> 6) as u8; 29 | let b1 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; 30 | ([b0, b1, 0, 0], 2) 31 | } 32 | 0x800..=0xFFFF => { 33 | let b0 = 0b1110_0000 | (u32 >> 12) as u8; 34 | let b1 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; 35 | let b2 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; 36 | ([b0, b1, b2, 0], 3) 37 | } 38 | 0x10000..=u32::MAX => { 39 | let b0 = 0b1111_0000 | (u32 >> 18) as u8; 40 | let b1 = 0b1000_0000 | ((u32 >> 12) & 0b0011_1111) as u8; 41 | let b2 = 0b1000_0000 | ((u32 >> 6) & 0b0011_1111) as u8; 42 | let b3 = 0b1000_0000 | (u32 & 0b0011_1111) as u8; 43 | ([b0, b1, b2, b3], 4) 44 | } 45 | } 46 | } 47 | 48 | pub(crate) const fn char_to_display(char: char) -> FmtChar { 49 | let ([b0, b1, b2, b3], len) = char_to_utf8(char); 50 | FmtChar { 51 | encoded: [b0, b1, b2, b3, 0, 0], 52 | len: len as u8, 53 | } 54 | } 55 | 56 | pub(crate) const fn char_to_debug(c: char) -> FmtChar { 57 | let ([b0, b1, b2, b3], len) = match c { 58 | '\t' => (*br#"\t "#, 2), 59 | '\r' => (*br#"\r "#, 2), 60 | '\n' => (*br#"\n "#, 2), 61 | '\\' => (*br#"\\ "#, 2), 62 | '\'' => (*br#"\' "#, 2), 63 | '\"' => (*br#"\" "#, 2), 64 | '\x00'..='\x1F' => { 65 | let n = c as u8; 66 | ( 67 | [ 68 | b'\\', 69 | b'x', 70 | hex_as_ascii(n >> 4, HexFormatting::Upper), 71 | hex_as_ascii(n & 0b1111, HexFormatting::Upper), 72 | ], 73 | 4, 74 | ) 75 | } 76 | _ => char_to_utf8(c), 77 | }; 78 | 79 | let mut encoded = [b'\'', b0, b1, b2, b3, 0]; 80 | encoded[len + 1] = b'\''; 81 | 82 | FmtChar { 83 | encoded, 84 | len: (len as u8) + 2, 85 | } 86 | } 87 | 88 | #[derive(Copy, Clone)] 89 | pub struct FmtChar { 90 | encoded: [u8; 6], 91 | len: u8, 92 | } 93 | 94 | impl FmtChar { 95 | /// Array which contains the pre-len display/debug-formatted `char`, 96 | /// only `&self.encoded[][..self.len()]` should be copied. 97 | pub const fn encoded(&self) -> &[u8; 6] { 98 | &self.encoded 99 | } 100 | 101 | pub const fn len(&self) -> usize { 102 | self.len as usize 103 | } 104 | 105 | pub(crate) const fn as_bytes(&self) -> &[u8] { 106 | #[cfg(not(feature = "rust_1_64"))] 107 | { 108 | match self.len() { 109 | 1 => { 110 | let [ret @ .., _, _, _, _, _] = &self.encoded; 111 | ret 112 | } 113 | 2 => { 114 | let [ret @ .., _, _, _, _] = &self.encoded; 115 | ret 116 | } 117 | 3 => { 118 | let [ret @ .., _, _, _] = &self.encoded; 119 | ret 120 | } 121 | 4 => { 122 | let [ret @ .., _, _] = &self.encoded; 123 | ret 124 | } 125 | 5 => { 126 | let [ret @ .., _] = &self.encoded; 127 | ret 128 | } 129 | 6 => &self.encoded, 130 | x => [/*bug WTF*/][x], 131 | } 132 | } 133 | 134 | #[cfg(feature = "rust_1_64")] 135 | { 136 | ::konst::slice::slice_up_to(&self.encoded, self.len()) 137 | } 138 | } 139 | } 140 | 141 | #[cfg(all(test, not(miri)))] 142 | mod tests; 143 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/str_split.rs: -------------------------------------------------------------------------------- 1 | use super::{Pattern, PatternCtor, PatternNorm}; 2 | 3 | use konst::slice::{bytes_find, bytes_find_skip}; 4 | 5 | pub struct SplitInputConv(pub &'static str, pub T); 6 | 7 | macro_rules! ctor { 8 | ($ty:ty) => { 9 | impl SplitInputConv<$ty> { 10 | pub const fn conv(self) -> SplitInput { 11 | SplitInput { 12 | str: self.0, 13 | pattern: PatternCtor(self.1).conv(), 14 | length: usize::MAX, 15 | } 16 | .compute_length() 17 | } 18 | } 19 | }; 20 | } 21 | 22 | ctor! {u8} 23 | ctor! {&'static str} 24 | ctor! {char} 25 | 26 | #[derive(Copy, Clone)] 27 | pub struct SplitInput { 28 | str: &'static str, 29 | pattern: Pattern, 30 | length: usize, 31 | } 32 | 33 | impl SplitInput { 34 | const fn compute_length(mut self) -> Self { 35 | self.length = count_splits(self); 36 | self 37 | } 38 | 39 | pub const fn split_it(self) -> [&'static str; LEN] { 40 | split_it(self) 41 | } 42 | 43 | pub const fn length(&self) -> usize { 44 | self.length 45 | } 46 | } 47 | 48 | pub const fn count_splits(SplitInput { str, pattern, .. }: SplitInput) -> usize { 49 | let mut count = 1; 50 | 51 | match pattern.normalize() { 52 | PatternNorm::AsciiByte(ascii_c) => { 53 | let mut bytes = str.as_bytes(); 54 | let ascii_c = ascii_c.get(); 55 | 56 | while let [byte, rem @ ..] = bytes { 57 | bytes = rem; 58 | 59 | if *byte == ascii_c { 60 | count += 1; 61 | } 62 | } 63 | } 64 | PatternNorm::Str(str_pat) => { 65 | if str_pat.is_empty() { 66 | let mut char_i = 0; 67 | count += 1; 68 | while let Some(next) = find_next_char_boundary(str, char_i) { 69 | char_i = next; 70 | count += 1; 71 | } 72 | } else { 73 | let mut str = str.as_bytes(); 74 | while let Some(next) = bytes_find_skip(str, str_pat) { 75 | str = next; 76 | count += 1; 77 | } 78 | } 79 | } 80 | } 81 | 82 | count 83 | } 84 | 85 | const fn find_u8(mut slice: &[u8], byte: u8) -> Option { 86 | let mut i = 0; 87 | 88 | while let [b, ref rem @ ..] = *slice { 89 | if byte == b { 90 | return Some(i); 91 | } 92 | slice = rem; 93 | i += 1; 94 | } 95 | None 96 | } 97 | 98 | const fn find_next_char_boundary(str: &str, mut index: usize) -> Option { 99 | if index == str.len() { 100 | None 101 | } else { 102 | loop { 103 | index += 1; 104 | if index == str.len() || (str.as_bytes()[index] as i8) >= -0x40 { 105 | break Some(index); 106 | } 107 | } 108 | } 109 | } 110 | 111 | pub const fn split_it(args: SplitInput) -> [&'static str; LEN] { 112 | let SplitInput { 113 | mut str, 114 | pattern, 115 | length: _, 116 | } = args; 117 | 118 | let mut out = [""; LEN]; 119 | let mut out_i = 0; 120 | 121 | macro_rules! write_out { 122 | ($string:expr) => { 123 | out[out_i] = $string; 124 | out_i += 1; 125 | }; 126 | } 127 | 128 | match pattern.normalize() { 129 | PatternNorm::AsciiByte(ascii_c) => { 130 | let ascii_c = ascii_c.get(); 131 | 132 | while let Some(found_at) = find_u8(str.as_bytes(), ascii_c) { 133 | write_out! {konst::string::str_up_to(str, found_at)} 134 | str = konst::string::str_from(str, found_at + 1); 135 | } 136 | } 137 | PatternNorm::Str(str_pat) => { 138 | if str_pat.is_empty() { 139 | out_i += 1; 140 | while let Some(next) = find_next_char_boundary(str, 0) { 141 | write_out! {konst::string::str_up_to(str, next)} 142 | str = konst::string::str_from(str, next); 143 | } 144 | } else { 145 | while let Some(found_at) = bytes_find(str.as_bytes(), str_pat, 0) { 146 | write_out! {konst::string::str_up_to(str, found_at)} 147 | str = konst::string::str_from(str, found_at + str_pat.len()); 148 | } 149 | } 150 | } 151 | } 152 | 153 | write_out! {str} 154 | 155 | assert!(out_i == LEN); 156 | out 157 | } 158 | -------------------------------------------------------------------------------- /const_format_proc_macros/src/utils.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | 3 | #[cfg(feature = "derive")] 4 | use quote::ToTokens; 5 | 6 | use std::{ 7 | collections::VecDeque, 8 | iter::Fuse, 9 | mem, 10 | ops::{Deref, DerefMut}, 11 | }; 12 | 13 | pub(crate) fn dummy_ident() -> proc_macro2::Ident { 14 | proc_macro2::Ident::new("__dummy__", Span::mixed_site()) 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////////// 18 | 19 | #[cfg(feature = "derive")] 20 | pub fn spanned_err(tokens: &dyn ToTokens, display: &dyn std::fmt::Display) -> crate::Error { 21 | use syn::spanned::Spanned; 22 | crate::Error::new(tokens.span(), display) 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////////// 26 | 27 | #[derive(Clone, Debug)] 28 | pub struct Peekable2 { 29 | iter: Fuse, 30 | queue: VecDeque, 31 | } 32 | 33 | impl Peekable2> { 34 | #[allow(clippy::new_ret_no_self)] 35 | pub fn new(iter: I) -> Peekable2 { 36 | Peekable2 { 37 | iter: iter.into_iter().fuse(), 38 | queue: VecDeque::new(), 39 | } 40 | } 41 | } 42 | 43 | impl Peekable2 { 44 | pub fn is_empty(&mut self) -> bool { 45 | self.peek().is_none() 46 | } 47 | 48 | pub fn peek(&mut self) -> Option<&I::Item> { 49 | if self.queue.is_empty() { 50 | self.queue.push_back(self.iter.next()?); 51 | } 52 | Some(&self.queue[0]) 53 | } 54 | pub fn peek2(&mut self) -> Option<&I::Item> { 55 | while self.queue.len() < 2 { 56 | self.queue.push_back(self.iter.next()?); 57 | } 58 | Some(&self.queue[1]) 59 | } 60 | } 61 | 62 | impl Iterator for Peekable2 63 | where 64 | I: Iterator, 65 | { 66 | type Item = I::Item; 67 | 68 | fn next(&mut self) -> Option { 69 | if let opt @ Some(_) = self.queue.pop_front() { 70 | opt 71 | } else { 72 | self.iter.next() 73 | } 74 | } 75 | 76 | fn size_hint(&self) -> (usize, Option) { 77 | let (low, high) = self.iter.size_hint(); 78 | let len = self.queue.len(); 79 | (low + len, high.map(|x| x.saturating_add(len))) 80 | } 81 | } 82 | 83 | //////////////////////////////////////////////////////////////////////////////////// 84 | 85 | /// A result wrapper which panics if it's the error variant is not handled, 86 | /// by calling `.into_result()`. 87 | #[derive(Debug, Clone)] 88 | pub struct LinearResult { 89 | errors: Result<(), crate::Error>, 90 | } 91 | 92 | impl Drop for LinearResult { 93 | fn drop(&mut self) { 94 | mem::replace(&mut self.errors, Ok(())).expect("Expected LinearResult to be handled"); 95 | } 96 | } 97 | 98 | impl LinearResult { 99 | #[inline] 100 | pub fn new(res: Result<(), crate::Error>) -> Self { 101 | Self { errors: res } 102 | } 103 | 104 | #[inline] 105 | pub fn ok() -> Self { 106 | Self::new(Ok(())) 107 | } 108 | } 109 | 110 | impl From> for LinearResult { 111 | #[inline] 112 | fn from(res: Result<(), crate::Error>) -> Self { 113 | Self::new(res) 114 | } 115 | } 116 | 117 | impl Deref for LinearResult { 118 | type Target = Result<(), crate::Error>; 119 | 120 | fn deref(&self) -> &Result<(), crate::Error> { 121 | &self.errors 122 | } 123 | } 124 | 125 | impl DerefMut for LinearResult { 126 | fn deref_mut(&mut self) -> &mut Result<(), crate::Error> { 127 | &mut self.errors 128 | } 129 | } 130 | 131 | #[allow(dead_code)] 132 | impl LinearResult { 133 | #[inline] 134 | pub fn into_result(mut self) -> Result<(), crate::Error> { 135 | mem::replace(&mut self.errors, Ok(())) 136 | } 137 | 138 | #[inline] 139 | pub fn take(&mut self) -> Result<(), crate::Error> { 140 | self.replace(Ok(())) 141 | } 142 | 143 | #[inline] 144 | pub fn replace(&mut self, other: Result<(), crate::Error>) -> Result<(), crate::Error> { 145 | mem::replace(&mut self.errors, other) 146 | } 147 | 148 | #[inline] 149 | pub fn push_err(&mut self, err: E) 150 | where 151 | E: Into, 152 | { 153 | let err = err.into(); 154 | match &mut self.errors { 155 | this @ Ok(_) => *this = Err(err), 156 | Err(e) => e.combine(err), 157 | } 158 | } 159 | 160 | #[inline] 161 | pub fn combine_err(&mut self, res: Result<(), E>) 162 | where 163 | E: Into, 164 | { 165 | if let Err(err) = res { 166 | let err = err.into(); 167 | self.push_err(err); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/derive_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_b::{ 2 | fmt::{Error, Formatter, FormattingFlags, StrWriter}, 3 | try_, writec, ConstDebug, 4 | }; 5 | 6 | use core::marker::PhantomData; 7 | 8 | mod is_a_attributes; 9 | 10 | /////////////////////////////////////////////////////////////////////////////// 11 | 12 | struct Dummy; 13 | 14 | #[derive(ConstDebug)] 15 | #[cdeb(crate = "::cfmt_b")] 16 | struct Braced { 17 | x: u32, 18 | y: Option<&'static str>, 19 | #[allow(dead_code)] 20 | #[cdeb(ignore)] 21 | z: (u32, u32), 22 | a: bool, 23 | } 24 | 25 | #[derive(ConstDebug)] 26 | #[cdeb(crate = "::cfmt_b")] 27 | #[cdeb(impls( 28 | " Tupled", 29 | " Tupled", 30 | " Tupled where U: 'static,", 31 | ))] 32 | struct Tupled( 33 | u32, 34 | #[cdeb(ignore)] 35 | #[allow(dead_code)] 36 | Option<&'static str>, 37 | T, 38 | PhantomData, 39 | ); 40 | 41 | #[derive(ConstDebug)] 42 | #[cdeb(crate = "::cfmt_b")] 43 | struct Unit; 44 | 45 | #[test] 46 | fn struct_formatting() { 47 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 48 | let braced = Braced { 49 | x: 100, 50 | y: Some("hello"), 51 | z: (0, 1), 52 | a: true, 53 | }; 54 | try_!(writec!(f, "{0:?}\n{0:x?}\n{0:X?}\n", braced)); 55 | 56 | let tupled_a: Tupled = Tupled(13, Some("hello"), 21, PhantomData); 57 | let tupled_b: Tupled = Tupled(32, Some("hello"), 33, PhantomData); 58 | let tupled_c: Tupled = Tupled(48, Some("hello"), false, PhantomData); 59 | try_!(writec!( 60 | f, 61 | "{0:?}\n{0:x?}\n{0:X?}\n{1:?}\n{1:x?}\n{1:X?}\n{2:?}\n{2:x?}\n{2:X?}\n", 62 | tupled_a, 63 | tupled_b, 64 | tupled_c, 65 | )); 66 | 67 | try_!(writec!(f, "{:?}", Unit)); 68 | 69 | Ok(()) 70 | } 71 | 72 | let writer: &mut StrWriter = &mut StrWriter::new([0; 1024]); 73 | 74 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 75 | 76 | assert_eq!( 77 | writer.as_str(), 78 | "\ 79 | Braced { x: 100, y: Some(\"hello\"), a: true }\n\ 80 | Braced { x: 64, y: Some(\"hello\"), a: true }\n\ 81 | Braced { x: 64, y: Some(\"hello\"), a: true }\n\ 82 | Tupled(13, 21, PhantomData)\n\ 83 | Tupled(d, 15, PhantomData)\n\ 84 | Tupled(D, 15, PhantomData)\n\ 85 | Tupled(32, 33, PhantomData)\n\ 86 | Tupled(20, 21, PhantomData)\n\ 87 | Tupled(20, 21, PhantomData)\n\ 88 | Tupled(48, false, PhantomData)\n\ 89 | Tupled(30, false, PhantomData)\n\ 90 | Tupled(30, false, PhantomData)\n\ 91 | Unit\ 92 | ", 93 | ); 94 | } 95 | 96 | /////////////////////////////////////////////////////////////////////////////// 97 | 98 | #[derive(ConstDebug)] 99 | #[cdeb(crate = "::cfmt_b")] 100 | enum Enum { 101 | Braced { 102 | x: u32, 103 | y: Option<&'static str>, 104 | #[allow(dead_code)] 105 | #[cdeb(ignore)] 106 | z: (u32, u32), 107 | a: bool, 108 | }, 109 | Tupled( 110 | u32, 111 | #[cdeb(ignore)] 112 | #[allow(dead_code)] 113 | Option<&'static str>, 114 | u32, 115 | PhantomData<()>, 116 | ), 117 | Unit, 118 | } 119 | 120 | #[test] 121 | fn enum_formatting() { 122 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 123 | let braced = Enum::Braced { 124 | x: 100, 125 | y: Some("hello"), 126 | z: (0, 1), 127 | a: true, 128 | }; 129 | try_!(writec!(f, "{0:?}\n{0:x?}\n{0:X?}\n", braced)); 130 | 131 | let tupled_a = Enum::Tupled(13, Some("hello"), 21, PhantomData); 132 | let tupled_b = Enum::Tupled(32, Some("hello"), 33, PhantomData); 133 | try_!(writec!( 134 | f, 135 | "{0:?}\n{0:x?}\n{0:X?}\n{1:?}\n{1:x?}\n{1:X?}\n", 136 | tupled_a, 137 | tupled_b 138 | )); 139 | 140 | try_!(writec!(f, "{:?}", Enum::Unit)); 141 | 142 | Ok(()) 143 | } 144 | 145 | let writer: &mut StrWriter = &mut StrWriter::new([0; 1024]); 146 | 147 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 148 | 149 | assert_eq!( 150 | writer.as_str(), 151 | "\ 152 | Braced { x: 100, y: Some(\"hello\"), a: true }\n\ 153 | Braced { x: 64, y: Some(\"hello\"), a: true }\n\ 154 | Braced { x: 64, y: Some(\"hello\"), a: true }\n\ 155 | Tupled(13, 21, PhantomData)\n\ 156 | Tupled(d, 15, PhantomData)\n\ 157 | Tupled(D, 15, PhantomData)\n\ 158 | Tupled(32, 33, PhantomData)\n\ 159 | Tupled(20, 21, PhantomData)\n\ 160 | Tupled(20, 21, PhantomData)\n\ 161 | Unit\ 162 | ", 163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /const_format/src/doctests.rs: -------------------------------------------------------------------------------- 1 | //! This module tests for errors that happen in the expanded code, 2 | //! errors detectable by the macro itself are tested in the proc macro crate. 3 | 4 | #![allow(non_camel_case_types)] 5 | 6 | /// 7 | /// ```rust 8 | /// 9 | /// struct Foo(T); 10 | /// 11 | /// const_format::impl_fmt!{ 12 | /// impl[T,] Foo 13 | /// where[T: 'static,]; 14 | /// 15 | /// fn foo(){} 16 | /// 17 | /// } 18 | /// ``` 19 | /// 20 | /// ```compile_fail 21 | /// 22 | /// struct Foo(T); 23 | /// 24 | /// const_format::impl_fmt!{ 25 | /// impl[T,] Foo 26 | /// where[asodkaspodaoskd,]; 27 | /// 28 | /// fn foo(){} 29 | /// } 30 | /// ``` 31 | /// 32 | /// ```compile_fail 33 | /// 34 | /// struct Foo(T); 35 | /// 36 | /// const_format::impl_fmt!{ 37 | /// impl[T,] Foo 38 | /// where[T: T]; 39 | /// 40 | /// fn foo(){} 41 | /// } 42 | /// ``` 43 | /// 44 | #[cfg(feature = "fmt")] 45 | pub struct ImplFmtWhereClause; 46 | 47 | /// 48 | /// ```rust 49 | /// 50 | /// #[derive(const_format::ConstDebug)] 51 | /// struct Foo(*const T) 52 | /// where T: 'static; 53 | /// 54 | /// fn main(){} 55 | /// ``` 56 | /// 57 | /// ```compile_fail 58 | /// 59 | /// #[derive(const_format::ConstDebug)] 60 | /// struct Foo(*const T) 61 | /// where AAAA: AAAA; 62 | /// 63 | /// fn main(){} 64 | /// ``` 65 | /// 66 | #[cfg(feature = "derive")] 67 | pub struct ConstDebugWhereClause; 68 | 69 | /// ```rust 70 | /// 71 | /// use const_format::StrWriterMut; 72 | /// 73 | /// let mut len = 0; 74 | /// let mut buffer = [0; 128]; 75 | /// 76 | /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); 77 | /// 78 | /// writer.write_str("hello").unwrap(); 79 | /// 80 | /// assert_eq!(writer.as_bytes(), b"hello") 81 | /// 82 | /// ``` 83 | /// 84 | /// ```compile_fail 85 | /// 86 | /// use const_format::StrWriterMut; 87 | /// 88 | /// let mut len = 0; 89 | /// let mut buffer = [0; 128]; 90 | /// 91 | /// let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); 92 | /// 93 | /// writer.write_str("hello").unwrap(); 94 | /// 95 | /// assert_eq!(writer.as_str(), "hello") 96 | /// 97 | /// ``` 98 | /// 99 | #[cfg(feature = "fmt")] 100 | pub struct AsStr_For_StrWriterMut_NoEncoding; 101 | 102 | /// ```rust 103 | /// 104 | /// const_format::assertc!(true, "foo"); 105 | /// 106 | /// ``` 107 | /// 108 | /// ```compile_fail 109 | /// 110 | /// const_format::assertc!(false, "foo"); 111 | /// 112 | /// ``` 113 | /// 114 | /// # With a Formatting argument 115 | /// 116 | /// ```rust 117 | /// 118 | /// const_format::assertc!( 119 | /// true, 120 | /// "{foo}\n{foo:#?}\n{}", 121 | /// |fmt| { const_format::call_debug_fmt!(array, [100u8], fmt ) }, 122 | /// foo = |fmt| { const_format::call_debug_fmt!(array, [(), ()], fmt ) }, 123 | /// ); 124 | /// 125 | /// const_format::assertc!( 126 | /// true, 127 | /// "{foo}\n{foo:#?}\n{}", 128 | /// |fmt| const_format::call_debug_fmt!(array, [100u8], fmt ), 129 | /// foo = |fmt| const_format::call_debug_fmt!(array, [(), ()], fmt ), 130 | /// ); 131 | /// 132 | /// ``` 133 | /// 134 | /// ```compile_fail 135 | /// 136 | /// const_format::assertc!( 137 | /// false, 138 | /// "{foo}\n{foo:#?}\n{}", 139 | /// |fmt| { const_format::call_debug_fmt!(array, [100u8], fmt ) }, 140 | /// foo = |fmt| { const_format::call_debug_fmt!(array, [(), ()], fmt ) }, 141 | /// ); 142 | /// 143 | /// const_format::assertc!( 144 | /// false, 145 | /// "{foo}\n{foo:#?}\n{}", 146 | /// |fmt| const_format::call_debug_fmt!(array, [100u8], fmt ), 147 | /// foo = |fmt| const_format::call_debug_fmt!(array, [(), ()], fmt ), 148 | /// ); 149 | /// 150 | /// ``` 151 | /// 152 | #[cfg(feature = "assertc")] 153 | pub struct Assert; 154 | 155 | /// # assert_eq 156 | /// 157 | /// ```rust 158 | /// 159 | /// const_format::assertc_eq!(0u8, 0u8, "foo"); 160 | /// 161 | /// ``` 162 | /// 163 | /// ```compile_fail 164 | /// 165 | /// const_format::assertc_eq!(0u8, 10u8, "foo"); 166 | /// 167 | /// ``` 168 | /// 169 | /// # assert_ne 170 | /// 171 | /// ```rust 172 | /// 173 | /// const_format::assertc_ne!(0u8, 10u8, "foo"); 174 | /// 175 | /// ``` 176 | /// 177 | /// ```compile_fail 178 | /// 179 | /// const_format::assertc_ne!(0u8, 0u8, "foo"); 180 | /// 181 | /// ``` 182 | /// 183 | #[cfg(feature = "assertc")] 184 | pub struct AssertCmp; 185 | 186 | /// ```rust 187 | /// const_format::assertcp!(true, "foo"); 188 | /// ``` 189 | /// 190 | /// ```compile_fail 191 | /// const_format::assertcp!(false, "foo"); 192 | /// ``` 193 | /// 194 | #[cfg(feature = "assertcp")] 195 | pub struct AssertCP; 196 | 197 | /// # assert_eq 198 | /// 199 | /// ```rust 200 | /// const_format::assertcp_eq!(0u8, 0u8, "foo"); 201 | /// ``` 202 | /// 203 | /// ```compile_fail 204 | /// const_format::assertcp_eq!(0u8, 10u8, "foo"); 205 | /// ``` 206 | /// 207 | /// # assert_ne 208 | /// 209 | /// ```rust 210 | /// const_format::assertcp_ne!(0u8, 10u8, "foo"); 211 | /// ``` 212 | /// 213 | /// ```compile_fail 214 | /// const_format::assertcp_ne!(0u8, 0u8, "foo"); 215 | /// ``` 216 | /// 217 | #[cfg(feature = "assertcp")] 218 | pub struct AssertCPCmp; 219 | -------------------------------------------------------------------------------- /const_format/tests/misc_tests/writec_macro.rs: -------------------------------------------------------------------------------- 1 | // Don't need the tests for this macro to be thorough, 2 | // since this uses a lot of the same machinery as `formatcp` and `formatc` 3 | 4 | use cfmt_b::fmt::{Error, Formatter, FormattingFlags, StrWriter}; 5 | use cfmt_b::{try_, writec}; 6 | 7 | struct Foo { 8 | x: u32, 9 | y: &'static str, 10 | } 11 | 12 | #[test] 13 | fn basic() { 14 | const fn inner_0(writer: &mut StrWriter) -> Result<(), Error> { 15 | writer.clear(); 16 | try_!(writec!(writer, "10")); 17 | try_!(writec!(writer, "-")); 18 | try_!(writec!(writer, "20")); 19 | Ok(()) 20 | } 21 | const fn inner_1(writer: &mut StrWriter) -> Result<(), Error> { 22 | writer.clear(); 23 | try_!(writec!(writer, "")); 24 | Ok(()) 25 | } 26 | 27 | let writer: &mut StrWriter = &mut StrWriter::new([0; 40]); 28 | inner_0(writer).unwrap(); 29 | assert_eq!(writer.as_str(), "10-20"); 30 | 31 | inner_1(writer).unwrap(); 32 | assert_eq!(writer.as_str(), ""); 33 | } 34 | 35 | #[test] 36 | fn repeated_positional_args() { 37 | const fn inner(foo: &Foo, writer: &mut StrWriter) -> Result<(), Error> { 38 | writer.clear(); 39 | try_!(writec!( 40 | writer, 41 | "{0:},{0:?},{0:#x},{0:#X},{0:#b},{1},{1:?}", 42 | foo.x, 43 | foo.y 44 | )); 45 | Ok(()) 46 | } 47 | 48 | let foo = Foo { 49 | x: 13, 50 | y: "foo\nbar\tbaz\x00", 51 | }; 52 | 53 | let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); 54 | inner(&foo, writer).unwrap(); 55 | assert_eq!( 56 | writer.as_str(), 57 | "13,13,0xd,0xD,0b1101,foo\nbar\tbaz\x00,\"foo\\nbar\\tbaz\\x00\"" 58 | ); 59 | } 60 | 61 | #[test] 62 | fn write_from_consts() { 63 | const FOO: Foo = Foo { 64 | x: 13, 65 | y: "foo\nbar\tbaz\x00", 66 | }; 67 | 68 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 69 | const X: u32 = FOO.x; 70 | const Y: &str = FOO.y; 71 | try_!(writec!(f, "{X:},{X:?},{X:#x},{X:#X},{X:#b},{Y},{Y:?}")); 72 | Ok(()) 73 | } 74 | 75 | let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); 76 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 77 | assert_eq!( 78 | writer.as_str(), 79 | "13,13,0xd,0xD,0b1101,foo\nbar\tbaz\x00,\"foo\\nbar\\tbaz\\x00\"" 80 | ); 81 | } 82 | 83 | #[test] 84 | fn named_parameters() { 85 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 86 | try_!(writec!( 87 | f, 88 | "{x},{y},{},{},{x:b},{y:x},{y:X},{:?}", 89 | 21u8, 90 | 34u8, 91 | 55..89, 92 | x = 8u8, 93 | y = 13u8 94 | )); 95 | Ok(()) 96 | } 97 | 98 | let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); 99 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 100 | assert_eq!(writer.as_str(), "8,13,21,34,1000,d,D,55..89"); 101 | } 102 | 103 | #[test] 104 | fn write_from_locals() { 105 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 106 | let foo = 13u8; 107 | let bar = "58"; 108 | 109 | innerb(f, foo, bar) 110 | } 111 | const fn innerb(f: &mut Formatter<'_>, foo: u8, bar: &str) -> Result<(), Error> { 112 | writec!( 113 | f, 114 | "{foo},{bar},{foo:?},{bar:?},{foo:x},{bar:x},{foo:X},{bar:X},{foo:b},{bar:b}" 115 | ) 116 | } 117 | 118 | let writer: &mut StrWriter = &mut StrWriter::new([0; 96]); 119 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 120 | assert_eq!(writer.as_str(), r#"13,58,13,"58",d,"58",D,"58",1101,"58""#); 121 | 122 | writer.clear(); 123 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 124 | assert_eq!(writer.as_str(), r#"13,58,13,"58",d,"58",D,"58",1101,"58""#); 125 | } 126 | 127 | #[test] 128 | #[cfg(feature = "fmt")] 129 | fn access_formatter() { 130 | use cfmt_b::call_debug_fmt; 131 | 132 | const fn inner(f: &mut Formatter<'_>) -> Result<(), Error> { 133 | let mut n = 0u64; 134 | 135 | try_!(writec!(f, "{0};;;", |fmt| { 136 | n += 1; 137 | call_debug_fmt!(array, [(), ()], fmt) 138 | })); 139 | 140 | try_!(writec!(f, "{0}; {0}; {0};;;", |fmt| { 141 | n += 100; 142 | call_debug_fmt!(array, [n, n], fmt) 143 | })); 144 | 145 | try_!(writec!(f, "{0};;;", |fmt| call_debug_fmt!( 146 | array, 147 | [(), ()], 148 | fmt 149 | ))); 150 | 151 | try_!(writec!(f, "{0}; {0};;;", |fmt| call_debug_fmt!( 152 | array, 153 | [(), ()], 154 | fmt 155 | ))); 156 | 157 | Ok(()) 158 | } 159 | 160 | let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); 161 | inner(&mut writer.make_formatter(FormattingFlags::NEW)).unwrap(); 162 | 163 | assert_eq!( 164 | writer.as_str(), 165 | "\ 166 | [(), ()];;;\ 167 | [101, 101]; [201, 201]; [301, 301];;;\ 168 | [(), ()];;;\ 169 | [(), ()]; [(), ()];;;\ 170 | " 171 | ); 172 | } 173 | -------------------------------------------------------------------------------- /const_format/src/macros/helper_macros.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | #[macro_export] 3 | macro_rules! __for_range{ 4 | ( $var:ident in $range:expr => $($for_body:tt)* )=>({ 5 | let $crate::pmr::Range{start: mut $var, end} = $range; 6 | while $var < end { 7 | {$($for_body)*} 8 | $var+=1; 9 | } 10 | }) 11 | } 12 | 13 | #[allow(unused_macros)] 14 | macro_rules! identity { 15 | ($($tt:tt)*) => { $($tt)* }; 16 | } 17 | 18 | #[cfg(not(feature = "rust_1_83"))] 19 | #[doc(hidden)] 20 | #[macro_export] 21 | macro_rules! __str_const { 22 | ($e:expr) => { 23 | $crate::pmr::__AssertStr { x: $e }.x 24 | }; 25 | } 26 | 27 | #[cfg(feature = "rust_1_83")] 28 | #[doc(hidden)] 29 | #[macro_export] 30 | macro_rules! __str_const { 31 | ($e:expr) => { 32 | const { $crate::pmr::__AssertStr { x: $e }.x } 33 | }; 34 | } 35 | 36 | #[cfg(not(feature = "rust_1_83"))] 37 | #[doc(hidden)] 38 | #[macro_export] 39 | macro_rules! __const { 40 | ($ty:ty => $e:expr) => { 41 | $crate::pmr::__AssertType::<$ty> { x: $e }.x 42 | }; 43 | } 44 | 45 | #[cfg(feature = "rust_1_83")] 46 | #[doc(hidden)] 47 | #[macro_export] 48 | macro_rules! __const { 49 | ($ty:ty => $e:expr) => { 50 | const { $crate::pmr::__AssertType::<$ty> { x: $e }.x } 51 | }; 52 | } 53 | 54 | #[doc(hidden)] 55 | #[macro_export] 56 | macro_rules! iter_copy_slice{ 57 | ( $var:ident in $array:expr => $($for_body:tt)* )=>({ 58 | let mut array: &[_] = &$array; 59 | while let [$var, ref rem @ ..] = *array { 60 | {$($for_body)*} 61 | array = rem; 62 | } 63 | }) 64 | } 65 | 66 | #[doc(hidden)] 67 | #[macro_export] 68 | macro_rules! __write_pvariant { 69 | (char, $parg:expr, $elem:ident => $out:ident) => {{ 70 | let encoded = $elem.encoded(); 71 | let len = $elem.len(); 72 | 73 | let mut start = 0; 74 | while start < len { 75 | $out.array[$out.len] = encoded[start]; 76 | $out.len += 1; 77 | start += 1; 78 | } 79 | }}; 80 | (int, $parg:expr, $elem:ident => $out:ident) => {{ 81 | let wrapper = $crate::pmr::PWrapper($elem); 82 | 83 | let debug_display; 84 | let bin; 85 | let hex; 86 | 87 | let sa: &$crate::pmr::StartAndArray<[_]> = match $parg.fmt { 88 | $crate::pmr::Formatting::Display => { 89 | debug_display = wrapper.to_start_array_display(); 90 | &debug_display 91 | } 92 | $crate::pmr::Formatting::Debug => match $parg.fmt_flags.num_fmt() { 93 | $crate::pmr::NumberFormatting::Decimal => { 94 | debug_display = wrapper.to_start_array_debug(); 95 | &debug_display 96 | } 97 | $crate::pmr::NumberFormatting::Binary => { 98 | bin = wrapper.to_start_array_binary($parg.fmt_flags); 99 | &bin 100 | } 101 | $crate::pmr::NumberFormatting::Hexadecimal => { 102 | hex = wrapper.to_start_array_hexadecimal($parg.fmt_flags); 103 | &hex 104 | } 105 | }, 106 | }; 107 | 108 | let mut start = sa.start; 109 | while start < sa.array.len() { 110 | $out.array[$out.len] = sa.array[start]; 111 | $out.len += 1; 112 | start += 1; 113 | } 114 | }}; 115 | (str, $parg:expr, $elem:ident => $out:ident) => {{ 116 | let str = $elem.as_bytes(); 117 | let is_display = $parg.fmt.is_display(); 118 | let mut i = 0; 119 | if is_display { 120 | while i < str.len() { 121 | $out.array[$out.len] = str[i]; 122 | $out.len += 1; 123 | i += 1; 124 | } 125 | } else { 126 | $out.array[$out.len] = b'"'; 127 | $out.len += 1; 128 | while i < str.len() { 129 | use $crate::pmr::{hex_as_ascii, ForEscaping, FOR_ESCAPING}; 130 | 131 | let c = str[i]; 132 | let mut written_c = c; 133 | if c < 128 { 134 | let shifted = 1 << c; 135 | 136 | if (FOR_ESCAPING.is_escaped & shifted) != 0 { 137 | $out.array[$out.len] = b'\\'; 138 | $out.len += 1; 139 | if (FOR_ESCAPING.is_backslash_escaped & shifted) == 0 { 140 | $out.array[$out.len] = b'x'; 141 | $out.array[$out.len + 1] = 142 | hex_as_ascii(c >> 4, $crate::pmr::HexFormatting::Upper); 143 | $out.len += 2; 144 | written_c = hex_as_ascii(c & 0b1111, $crate::pmr::HexFormatting::Upper); 145 | } else { 146 | written_c = ForEscaping::get_backslash_escape(c); 147 | }; 148 | } 149 | } 150 | $out.array[$out.len] = written_c; 151 | $out.len += 1; 152 | i += 1; 153 | } 154 | $out.array[$out.len] = b'"'; 155 | $out.len += 1; 156 | } 157 | }}; 158 | } 159 | -------------------------------------------------------------------------------- /const_format/src/fmt/std_type_impls/ranges.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fmt::{Error, Formatter}, 3 | marker_traits::{FormatMarker, IsAFormatMarker, IsStdKind}, 4 | wrapper_types::PWrapper, 5 | }; 6 | 7 | use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | impl FormatMarker for Range { 12 | type Kind = IsStdKind; 13 | type This = Self; 14 | } 15 | 16 | impl IsAFormatMarker, T> { 17 | #[inline(always)] 18 | pub const fn coerce(self, range: &Range) -> PWrapper> { 19 | PWrapper(Range { 20 | start: range.start, 21 | end: range.end, 22 | }) 23 | } 24 | } 25 | 26 | impl PWrapper> { 27 | const RANGE: &'static str = ".."; 28 | 29 | #[inline(always)] 30 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 31 | try_!(PWrapper(self.0.start).const_debug_fmt(f)); 32 | try_!(PWrapper(Self::RANGE).const_display_fmt(f)); 33 | try_!(PWrapper(self.0.end).const_debug_fmt(f)); 34 | Ok(()) 35 | } 36 | } 37 | 38 | /////////////////////////////////////////////////////////////////////////////// 39 | 40 | impl FormatMarker for RangeFrom { 41 | type Kind = IsStdKind; 42 | type This = Self; 43 | } 44 | 45 | impl IsAFormatMarker, T> { 46 | #[inline(always)] 47 | pub const fn coerce(self, range: &RangeFrom) -> PWrapper> { 48 | PWrapper(RangeFrom { start: range.start }) 49 | } 50 | } 51 | 52 | impl PWrapper> { 53 | const RANGE: &'static str = ".."; 54 | 55 | #[inline(always)] 56 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 57 | try_!(PWrapper(self.0.start).const_debug_fmt(f)); 58 | try_!(PWrapper(Self::RANGE).const_display_fmt(f)); 59 | Ok(()) 60 | } 61 | } 62 | 63 | /////////////////////////////////////////////////////////////////////////////// 64 | 65 | impl FormatMarker for RangeTo { 66 | type Kind = IsStdKind; 67 | type This = Self; 68 | } 69 | 70 | impl IsAFormatMarker, T> { 71 | #[inline(always)] 72 | pub const fn coerce(self, range: &RangeTo) -> PWrapper> { 73 | PWrapper(RangeTo { end: range.end }) 74 | } 75 | } 76 | 77 | impl PWrapper> { 78 | const RANGE: &'static str = ".."; 79 | 80 | #[inline(always)] 81 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 82 | try_!(PWrapper(Self::RANGE).const_display_fmt(f)); 83 | try_!(PWrapper(self.0.end).const_debug_fmt(f)); 84 | Ok(()) 85 | } 86 | } 87 | 88 | /////////////////////////////////////////////////////////////////////////////// 89 | 90 | impl FormatMarker for RangeToInclusive { 91 | type Kind = IsStdKind; 92 | type This = Self; 93 | } 94 | 95 | impl IsAFormatMarker, T> { 96 | #[inline(always)] 97 | pub const fn coerce( 98 | self, 99 | range: &RangeToInclusive, 100 | ) -> PWrapper> { 101 | PWrapper(RangeToInclusive { end: range.end }) 102 | } 103 | } 104 | 105 | impl PWrapper> { 106 | const RANGE: &'static str = "..="; 107 | 108 | #[inline(always)] 109 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 110 | try_!(PWrapper(Self::RANGE).const_display_fmt(f)); 111 | try_!(PWrapper(self.0.end).const_debug_fmt(f)); 112 | Ok(()) 113 | } 114 | } 115 | 116 | /////////////////////////////////////////////////////////////////////////////// 117 | 118 | impl FormatMarker for RangeInclusive { 119 | type Kind = IsStdKind; 120 | type This = Self; 121 | } 122 | 123 | impl IsAFormatMarker, T> { 124 | #[inline(always)] 125 | pub const fn coerce(self, range: &RangeInclusive) -> PWrapper> { 126 | PWrapper(RangeInclusive::new(*range.start(), *range.end())) 127 | } 128 | } 129 | 130 | impl PWrapper> { 131 | const RANGE: &'static str = "..="; 132 | 133 | #[inline(always)] 134 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 135 | try_!(PWrapper(*self.0.start()).const_debug_fmt(f)); 136 | try_!(PWrapper(Self::RANGE).const_display_fmt(f)); 137 | try_!(PWrapper(*self.0.end()).const_debug_fmt(f)); 138 | Ok(()) 139 | } 140 | } 141 | 142 | /////////////////////////////////////////////////////////////////////////////// 143 | 144 | impl FormatMarker for RangeFull { 145 | type Kind = IsStdKind; 146 | type This = Self; 147 | } 148 | 149 | impl IsAFormatMarker { 150 | #[inline(always)] 151 | pub const fn coerce(self, _: &RangeFull) -> PWrapper { 152 | PWrapper(..) 153 | } 154 | } 155 | 156 | impl PWrapper { 157 | const RANGE: &'static str = ".."; 158 | 159 | #[inline(always)] 160 | pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 161 | PWrapper(Self::RANGE).const_display_fmt(f) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /const_format/tests/fmt_tests/str_writer_mut.rs: -------------------------------------------------------------------------------- 1 | use cfmt_a::fmt::{Error, StrWriterMut}; 2 | 3 | #[test] 4 | fn from_custom() -> Result<(), Error> { 5 | let mut len = 4; 6 | let mut buffer = [b' '; 256]; 7 | 8 | let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); 9 | assert_eq!(writer.as_bytes(), b" "); 10 | 11 | writer.write_str("hello")?; 12 | assert_eq!(writer.as_bytes(), b" hello"); 13 | 14 | assert_eq!(writer.len(), 9); 15 | assert!(writer.buffer().starts_with(b" hello")); 16 | 17 | assert_eq!(len, 9); 18 | assert!(buffer.starts_with(b" hello")); 19 | 20 | len = 6; 21 | let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); 22 | 23 | assert_eq!(writer.len(), 6); 24 | assert_eq!(writer.as_bytes(), b" he"); 25 | 26 | writer.write_str("roic")?; 27 | assert_eq!(writer.len(), 10); 28 | assert_eq!(writer.as_bytes(), b" heroic"); 29 | assert!(writer.buffer().starts_with(b" heroic")); 30 | 31 | assert_eq!(len, 10); 32 | assert!(buffer.starts_with(b" heroic")); 33 | 34 | Ok(()) 35 | } 36 | 37 | #[test] 38 | fn from_custom_cleared() -> Result<(), Error> { 39 | let mut len = 4; 40 | let mut buffer = [b' '; 256]; 41 | 42 | let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); 43 | assert_eq!(writer.as_str(), ""); 44 | 45 | writer.write_str("hello")?; 46 | assert_eq!(writer.len(), 5); 47 | assert_eq!(writer.as_str(), "hello"); 48 | assert!(writer.buffer().starts_with(b"hello")); 49 | 50 | assert_eq!(len, 5); 51 | assert!(buffer.starts_with(b"hello")); 52 | 53 | let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); 54 | 55 | assert!(writer.is_empty()); 56 | assert_eq!(writer.len(), 0); 57 | assert_eq!(writer.as_str(), ""); 58 | 59 | writer.write_str("he")?; 60 | assert!(!writer.is_empty()); 61 | assert_eq!(writer.len(), 2); 62 | assert_eq!(writer.as_str(), "he"); 63 | assert!(writer.buffer().starts_with(b"he")); 64 | 65 | writer.write_str("roic")?; 66 | assert!(!writer.is_empty()); 67 | assert_eq!(writer.len(), 6); 68 | assert_eq!(writer.as_str(), "heroic"); 69 | assert!(writer.buffer().starts_with(b"heroic")); 70 | 71 | assert_eq!(len, 6); 72 | assert!(buffer.starts_with(b"heroic")); 73 | 74 | Ok(()) 75 | } 76 | 77 | #[test] 78 | fn truncate_no_encoding() -> Result<(), Error> { 79 | let bytes = { 80 | let mut bytes = "foo".as_bytes().to_vec(); 81 | // These are unicode continuation bytes, 82 | // ensuring that the StrWriterMut can be truncated into a continuation byte 83 | bytes.push(0xBE); 84 | bytes.push(0xBF); 85 | bytes 86 | }; 87 | 88 | // This isn't valid unicode. 89 | assert!(std::str::from_utf8(&bytes).is_err()); 90 | 91 | let mut buffer = [0; 32]; 92 | buffer[..bytes.len()].copy_from_slice(&bytes); 93 | let mut len = bytes.len(); 94 | let mut writer = StrWriterMut::from_custom(&mut buffer, &mut len); 95 | 96 | assert_eq!(writer.as_bytes(), b"foo\xBE\xBF"); 97 | 98 | writer.write_str(" bar baz")?; 99 | 100 | let all = b"foo\xBE\xBF bar baz"; 101 | 102 | writer.truncate(usize::MAX / 2); 103 | assert_eq!(writer.as_bytes(), all); 104 | 105 | let fbb_len = writer.len(); 106 | 107 | writer.truncate(fbb_len + 1); 108 | assert_eq!(writer.as_bytes(), all); 109 | 110 | for truncate_to in (3..fbb_len).rev() { 111 | writer.truncate(truncate_to); 112 | assert_eq!(writer.as_bytes(), &all[..truncate_to]); 113 | } 114 | 115 | writer.write_str("ooooooo")?; 116 | assert_eq!(writer.as_bytes(), b"fooooooooo"); 117 | 118 | writer.truncate(4); 119 | assert_eq!(writer.as_bytes(), b"fooo"); 120 | 121 | Ok(()) 122 | } 123 | 124 | #[test] 125 | fn clear_test() { 126 | const CAP: usize = 512; 127 | let mut buffer = [0; CAP]; 128 | let mut len = 0; 129 | let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); 130 | 131 | writer.write_str("hello").unwrap(); 132 | assert_eq!(writer.as_str(), "hello"); 133 | 134 | writer.write_str("world").unwrap(); 135 | assert_eq!(writer.as_str(), "helloworld"); 136 | 137 | writer.clear(); 138 | assert_eq!(writer.as_str(), ""); 139 | assert_eq!(writer.len(), 0); 140 | 141 | assert_eq!(len, 0); 142 | 143 | let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); 144 | 145 | writer.write_str("foo").unwrap(); 146 | assert_eq!(writer.as_str(), "foo"); 147 | 148 | writer.write_str("bar").unwrap(); 149 | assert_eq!(writer.as_str(), "foobar"); 150 | } 151 | 152 | #[test] 153 | fn as_bytes() { 154 | const CAP: usize = 512; 155 | let mut buffer = [0; CAP]; 156 | let mut len = 0; 157 | let mut writer = StrWriterMut::from_custom_cleared(&mut buffer, &mut len); 158 | let mut string = String::new(); 159 | 160 | for i in 0..CAP { 161 | assert_eq!(writer.capacity(), CAP); 162 | assert_eq!(writer.remaining_capacity(), CAP - i); 163 | 164 | let ascii_char = ((i % 61) + 32) as u8; 165 | writer.write_ascii_repeated(ascii_char, 1).unwrap(); 166 | string.push(ascii_char as char); 167 | 168 | assert_eq!(writer.as_bytes(), string.as_bytes()); 169 | assert_eq!(writer.as_bytes_alt(), string.as_bytes()); 170 | assert_eq!(writer.as_str(), string.as_str()); 171 | 172 | assert_eq!(writer.remaining_capacity(), CAP - i - 1); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /const_format/tests/fmt_tests/std_impl_tests.rs: -------------------------------------------------------------------------------- 1 | use cfmt_a::{ 2 | coerce_to_fmt, 3 | fmt::{ComputeStrLength, Error, FormattingFlags, StrWriter}, 4 | try_, 5 | wrapper_types::PWrapper, 6 | }; 7 | 8 | use core::{ 9 | cmp::Ordering, 10 | marker::{PhantomData, PhantomPinned}, 11 | num::NonZeroU8, 12 | ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, 13 | ptr::NonNull, 14 | sync::atomic::Ordering as AtomicOrdering, 15 | }; 16 | 17 | const FLAGS: &[FormattingFlags] = &[ 18 | FormattingFlags::NEW, 19 | FormattingFlags::NEW.set_alternate(true).set_hexadecimal(), 20 | ]; 21 | 22 | macro_rules! test_fmt { 23 | ( 24 | $Ty:ty; 25 | $( ($value:expr, $expected_debug:expr, $expected_hex:expr $(,)? ) )+ 26 | ) => ({ 27 | const fn inner( 28 | this: &PWrapper<$Ty>, 29 | writer: &mut StrWriter, 30 | flags: FormattingFlags, 31 | ) -> Result { 32 | try_!(this.const_debug_fmt(&mut writer.make_formatter(flags))); 33 | 34 | let mut str_len = ComputeStrLength::new(); 35 | try_!(this.const_debug_fmt(&mut str_len.make_formatter(flags))); 36 | 37 | Ok(str_len.len()) 38 | } 39 | 40 | fn test_case(this: &PWrapper<$Ty>, expected: &str, expected_hex: &str) { 41 | let writer: &mut StrWriter = &mut StrWriter::new([0; 256]); 42 | 43 | for (&expected, &flag) in [expected,expected_hex].iter().zip(FLAGS.iter()) { 44 | writer.clear(); 45 | let len = inner(this, writer, flag).unwrap(); 46 | 47 | assert_eq!(writer.as_str(), expected); 48 | assert_eq!(writer.len(), len, "{}", writer.as_str()); 49 | } 50 | } 51 | 52 | $({ 53 | test_case( &coerce_to_fmt!(&$value) , $expected_debug, $expected_hex); 54 | })* 55 | }); 56 | } 57 | 58 | #[test] 59 | fn array_impls() { 60 | test_fmt! {&[u8]; 61 | ( 62 | [8u8, 13, 21, 34], 63 | "[8, 13, 21, 34]", 64 | "[\n 0x8,\n 0xD,\n 0x15,\n 0x22,\n]", 65 | ) 66 | } 67 | test_fmt! {&[&str]; 68 | ( 69 | ["foo\n", "bar\t"], 70 | "[\"foo\\n\", \"bar\\t\"]", 71 | "[\n \"foo\\n\",\n \"bar\\t\",\n]" 72 | ) 73 | } 74 | test_fmt! {&[char]; 75 | ( 76 | ['f', 'o', '\n', '\t', 'ñ', '个', '\u{100000}'], 77 | "['f', 'o', '\\n', '\\t', 'ñ', '个', '\u{100000}']", 78 | "[\n 'f',\n 'o',\n '\\n',\ 79 | \n '\\t',\n 'ñ',\n '个',\ 80 | \n '\u{100000}',\n\ 81 | ]" 82 | ) 83 | } 84 | } 85 | 86 | #[test] 87 | fn range_impls() { 88 | test_fmt! {Range; (11..64, "11..64", "0xB..0x40") } 89 | test_fmt! {RangeFrom; (11.., "11..", "0xB..") } 90 | test_fmt! {RangeFull; (.., "..", "..") } 91 | test_fmt! {RangeInclusive; (11..=64, "11..=64", "0xB..=0x40") } 92 | test_fmt! {RangeTo; (..64, "..64", "..0x40") } 93 | test_fmt! {RangeToInclusive; (..=64, "..=64", "..=0x40") } 94 | } 95 | 96 | #[test] 97 | fn options() { 98 | test_fmt! {Option<&str>; 99 | (None::<&str>, "None", "None") 100 | (Some("hello\n"), "Some(\"hello\\n\")", "Some(\n \"hello\\n\",\n)") 101 | } 102 | test_fmt! {Option; 103 | (None::, "None", "None") 104 | (Some(10u8), "Some(10)", "Some(\n 0xA,\n)") 105 | } 106 | test_fmt! {Option; 107 | (None::, "None", "None") 108 | (Some(false), "Some(false)", "Some(\n false,\n)") 109 | (Some(true), "Some(true)", "Some(\n true,\n)") 110 | } 111 | test_fmt! {Option; 112 | (None::, "None", "None") 113 | (Some('4'), "Some('4')", "Some(\n '4',\n)") 114 | (Some('\x00'), "Some('\\x00')", "Some(\n '\\x00',\n)") 115 | } 116 | test_fmt! {Option; 117 | (None::, "None", "None") 118 | (NonZeroU8::new(10), "Some(10)", "Some(\n 0xA,\n)") 119 | } 120 | test_fmt! {Option>; 121 | (None::>, "None", "None") 122 | (Some(NonNull::::dangling()), "Some()", "Some(\n ,\n)") 123 | } 124 | } 125 | 126 | #[test] 127 | fn pointers() { 128 | test_fmt! {*const u8; (core::ptr::null(), "", "") } 129 | test_fmt! {*mut u8; (core::ptr::null_mut(), "", "") } 130 | test_fmt! {NonNull; (NonNull::dangling(), "", "") } 131 | } 132 | 133 | #[test] 134 | fn marker() { 135 | test_fmt! {PhantomData<()>; (PhantomData, "PhantomData", "PhantomData") } 136 | test_fmt! {PhantomPinned; (PhantomPinned, "PhantomPinned", "PhantomPinned") } 137 | test_fmt! {(); ((), "()", "()") } 138 | } 139 | 140 | #[test] 141 | fn miscelaneous_enums() { 142 | test_fmt! { 143 | Ordering; 144 | (Ordering::Less, "Less", "Less") 145 | (Ordering::Equal, "Equal", "Equal") 146 | (Ordering::Greater, "Greater", "Greater") 147 | } 148 | test_fmt! { 149 | AtomicOrdering; 150 | (AtomicOrdering::Relaxed, "Relaxed", "Relaxed") 151 | (AtomicOrdering::Release, "Release", "Release") 152 | } 153 | } 154 | 155 | #[test] 156 | fn chars() { 157 | test_fmt! { 158 | char; 159 | ('\\', r#"'\\'"#, r#"'\\'"#) 160 | ('\'', r#"'\''"#, r#"'\''"#) 161 | ('\"', r#"'\"'"#, r#"'\"'"#) 162 | ('\n', r#"'\n'"#, r#"'\n'"#) 163 | ('\r', r#"'\r'"#, r#"'\r'"#) 164 | ('\t', r#"'\t'"#, r#"'\t'"#) 165 | ('o', r#"'o'"#, r#"'o'"#) 166 | ('ñ', r#"'ñ'"#, r#"'ñ'"#) 167 | ('个', r#"'个'"#, r#"'个'"#) 168 | // non-ascii characters are always written unescaped 169 | ('\u{100000}', "'\u{100000}'", "'\u{100000}'") 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /const_format/src/__ascii_case_conv/word_iterator.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Debug}; 2 | 3 | macro_rules! for_range_inc { 4 | ($current:ident in $start:expr, $end:expr => $($code:tt)*) => { 5 | let mut $current = $start; 6 | let end = $end; 7 | 8 | while $current <= end { 9 | $($code)* 10 | 11 | $current+=1; 12 | } 13 | }; 14 | } 15 | 16 | use core::ops::Range; 17 | 18 | #[derive(Copy, Clone)] 19 | struct ByteKind(u8); 20 | 21 | impl Debug for ByteKind { 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | f.write_str(match () { 24 | _ if self.0 == Self::Other.0 => "Other", 25 | _ if self.0 == Self::Number.0 => "Number", 26 | _ if self.0 == Self::LowerCase.0 => "LowerCase", 27 | _ if self.0 == Self::UpperCase.0 => "UpperCase", 28 | _ if self.0 == Self::NonAscii.0 => "NonAscii", 29 | _ => unreachable!(), 30 | }) 31 | } 32 | } 33 | 34 | #[allow(non_upper_case_globals)] 35 | impl ByteKind { 36 | const Other: Self = Self(0b0001); 37 | const Number: Self = Self(0b0010); 38 | const LowerCase: Self = Self(0b0100); 39 | const UpperCase: Self = Self(0b1000); 40 | const Alphabetic: Self = Self(Self::LowerCase.0 | Self::UpperCase.0); 41 | // Assumes that non-ascii chars are mostly alphabetic, 42 | // this should work out fine most of the time. 43 | const NonAscii: Self = Self(0b1100); 44 | } 45 | 46 | impl ByteKind { 47 | #[allow(dead_code)] 48 | #[inline(always)] 49 | pub const fn eq(self, other: Self) -> bool { 50 | (self.0 & other.0) != 0 51 | } 52 | 53 | #[inline(always)] 54 | pub const fn ne(self, other: Self) -> bool { 55 | (self.0 & other.0) == 0 56 | } 57 | 58 | #[inline(always)] 59 | pub const fn is_alphabetic(self) -> bool { 60 | self.0 == Self::LowerCase.0 || self.0 == Self::UpperCase.0 61 | } 62 | 63 | pub const fn is_end_of_word(mut self, prev: Self, other: Self) -> bool { 64 | if self.0 == Self::NonAscii.0 { 65 | self = prev; 66 | } 67 | 68 | if self.0 == Self::UpperCase.0 { 69 | other.ne(Self::Alphabetic) 70 | } else { 71 | self.ne(other) 72 | } 73 | } 74 | } 75 | 76 | #[derive(Debug, Copy, Clone)] 77 | pub(crate) struct WordIterator<'a> { 78 | bytes: &'a [u8], 79 | start: usize, 80 | } 81 | 82 | const BYTE_KIND: &[ByteKind; 256] = &{ 83 | let mut out = [ByteKind::NonAscii; 256]; 84 | 85 | // Make sure that this goes first 86 | for_range_inc! {i in 0, 127 => out[i as usize] = ByteKind::Other; } 87 | for_range_inc! {i in b'A', b'Z' => out[i as usize] = ByteKind::UpperCase; } 88 | for_range_inc! {i in b'a', b'z' => out[i as usize] = ByteKind::LowerCase; } 89 | for_range_inc! {i in b'0', b'9' => out[i as usize] = ByteKind::Number; } 90 | 91 | out 92 | }; 93 | 94 | impl<'a> WordIterator<'a> { 95 | pub(crate) const fn new(bytes: &'a [u8]) -> Self { 96 | Self { bytes, start: 0 } 97 | } 98 | 99 | const fn skip_same_kind(mut self, mut kind: ByteKind) -> (Self, ByteKind) { 100 | let orig_bytes_len = self.bytes.len(); 101 | 102 | let mut prev_kind = kind; 103 | while let [b, rem @ ..] = self.bytes { 104 | let next_kind = BYTE_KIND[*b as usize]; 105 | let cmp = kind.is_end_of_word(prev_kind, next_kind); 106 | if kind.is_alphabetic() { 107 | prev_kind = kind; 108 | } 109 | kind = next_kind; 110 | if cmp { 111 | break; 112 | } 113 | self.bytes = rem; 114 | } 115 | 116 | // Advance until a char boundary is found 117 | while let [b, rem @ ..] = self.bytes { 118 | if (*b as i8) >= -0x40 { 119 | break; 120 | } 121 | self.bytes = rem; 122 | } 123 | 124 | // Remember not to add return statements to the function 125 | self.start += orig_bytes_len - self.bytes.len(); 126 | 127 | (self, kind) 128 | } 129 | 130 | pub(crate) const fn next(self) -> Option<(Self, Range)> { 131 | let (this, fkind) = self.skip_same_kind(ByteKind::Other); 132 | if let [] = this.bytes { 133 | None 134 | } else { 135 | let (next, _) = this.skip_same_kind(fkind); 136 | let range = this.start..next.start; 137 | Some((next, range)) 138 | } 139 | } 140 | } 141 | 142 | #[cfg(test)] 143 | mod tests { 144 | use super::*; 145 | 146 | use arrayvec::ArrayVec; 147 | 148 | fn get_words(text: &str) -> ArrayVec<&str, 20> { 149 | let mut list = >::new(); 150 | let mut word_iter = WordIterator::new(text.as_bytes()); 151 | 152 | while let Some((niter, word_range)) = word_iter.next() { 153 | word_iter = niter; 154 | list.push(&text[word_range]); 155 | } 156 | 157 | list 158 | } 159 | 160 | #[test] 161 | fn test_word_iter() { 162 | assert_eq!( 163 | get_words("01934324ñmaniÑNnFooBar")[..], 164 | ["01934324", "ñmaniÑ", "Nn", "Foo", "Bar"], 165 | ); 166 | 167 | assert_eq!( 168 | get_words("01934 324 ñmani-嶲Nn____FOOOBar")[..], 169 | ["01934", "324", "ñmani", "嶲Nn", "FOOOBar"], 170 | ); 171 | 172 | assert_eq!(get_words(" 01934 1111 ")[..], ["01934", "1111"],); 173 | 174 | assert_eq!(get_words(" 嶲01934 ")[..], ["嶲", "01934"],); 175 | 176 | assert_eq!(get_words(" 嶲A01934 ")[..], ["嶲A", "01934"],); 177 | 178 | assert_eq!(get_words(" 嶲a01934 ")[..], ["嶲a", "01934"],); 179 | 180 | assert_eq!(get_words(" ñA01934 ")[..], ["ñA", "01934"],); 181 | 182 | assert_eq!(get_words(" ña01934 ")[..], ["ña", "01934"],); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /const_format/src/equality.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs, unused_variables)] 2 | 3 | use crate::wrapper_types::PWrapper; 4 | 5 | use core::{ 6 | cmp::Ordering, 7 | marker::{PhantomData, PhantomPinned}, 8 | num::{ 9 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 10 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, 11 | }, 12 | ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, 13 | sync::atomic::Ordering as AtomicOrdering, 14 | }; 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | macro_rules! slice_of_const_eq {($($elem:ty),* $(,)?) => ( 19 | $( 20 | impl PWrapper<&[$elem]> { 21 | /// This method is only available with the "assert" feature. 22 | pub const fn const_eq(&self, other: &[$elem]) -> bool { 23 | if self.0.len() != other.len() { 24 | return false; 25 | } 26 | 27 | __for_range!{i in 0..self.0.len() => 28 | if !PWrapper(self.0[i]).const_eq(&other[i]) { 29 | return false 30 | } 31 | } 32 | true 33 | } 34 | } 35 | )* 36 | )} 37 | 38 | slice_of_const_eq! { 39 | &str, 40 | } 41 | 42 | macro_rules! slice_of_equal_op_impl {($($elem:ty),* $(,)?) => ( 43 | $( 44 | impl PWrapper<&[$elem]> { 45 | /// This method is only available with the "assert" feature. 46 | pub const fn const_eq(&self, other: &[$elem]) -> bool { 47 | if self.0.len() != other.len() { 48 | return false; 49 | } 50 | 51 | __for_range!{i in 0..self.0.len() => 52 | if self.0[i] != other[i] { 53 | return false 54 | } 55 | } 56 | true 57 | } 58 | } 59 | )* 60 | )} 61 | 62 | slice_of_equal_op_impl! { 63 | bool, 64 | char, 65 | u8, i8, 66 | u16, i16, 67 | u32, i32, 68 | u64, i64, 69 | u128, i128, 70 | usize, isize, 71 | } 72 | 73 | //////////////////////////////////////////////////////////////////////////////// 74 | 75 | macro_rules! impl_eq_for_option_prim { 76 | ( 77 | (l=$l:ident, r=$r:ident) 78 | $( impl[$($impl_:tt)*] $type:ty = $comparison:expr; )* 79 | ) => ( 80 | $( 81 | impl<$($impl_)*> PWrapper> { 82 | /// This method is only available with the "assert" feature. 83 | pub const fn const_eq(&self, other:&Option<$type>) -> bool { 84 | match (self.0, other) { 85 | (Some($l), Some($r)) => $comparison, 86 | (None, None) => true, 87 | _ => false, 88 | } 89 | } 90 | } 91 | )* 92 | ) 93 | } 94 | 95 | impl_eq_for_option_prim! { 96 | (l=l, r=r) 97 | impl[] u8 = l == *r; 98 | impl[] i8 = l == *r; 99 | impl[] u16 = l == *r; 100 | impl[] i16 = l == *r; 101 | impl[] u32 = l == *r; 102 | impl[] i32 = l == *r; 103 | impl[] u64 = l == *r; 104 | impl[] i64 = l == *r; 105 | impl[] u128 = l == *r; 106 | impl[] i128 = l == *r; 107 | impl[] usize = l == *r; 108 | impl[] isize = l == *r; 109 | impl[] bool = l == *r; 110 | impl[] char = l == *r; 111 | impl[] &str = crate::slice_cmp::str_eq(l, r); 112 | } 113 | 114 | macro_rules! impl_eq_for_option { 115 | ( 116 | (l=$l:ident, r=$r:ident) 117 | $( impl[$($impl_:tt)*] $type:ty = $comparison:expr; )* 118 | ) => ( 119 | $( 120 | impl<$($impl_)*> PWrapper<$type> { 121 | /// This method is only available with the "assert" feature. 122 | pub const fn const_eq(&self, $r:&$type) -> bool { 123 | let $l = self.0; 124 | $comparison 125 | } 126 | } 127 | )* 128 | 129 | impl_eq_for_option_prim! { 130 | (l=$l, r=$r) 131 | $( impl[$($impl_)*] $type = $comparison; )* 132 | } 133 | ) 134 | } 135 | 136 | impl_eq_for_option! { 137 | (l=l, r=r) 138 | 139 | impl[] NonZeroU8 = l.get() == r.get(); 140 | impl[] NonZeroI8 = l.get() == r.get(); 141 | impl[] NonZeroU16 = l.get() == r.get(); 142 | impl[] NonZeroI16 = l.get() == r.get(); 143 | impl[] NonZeroU32 = l.get() == r.get(); 144 | impl[] NonZeroI32 = l.get() == r.get(); 145 | impl[] NonZeroU64 = l.get() == r.get(); 146 | impl[] NonZeroI64 = l.get() == r.get(); 147 | impl[] NonZeroU128 = l.get() == r.get(); 148 | impl[] NonZeroI128 = l.get() == r.get(); 149 | impl[] NonZeroUsize = l.get() == r.get(); 150 | impl[] NonZeroIsize = l.get() == r.get(); 151 | } 152 | 153 | macro_rules! impl_equality { 154 | ( 155 | (l=$l:ident, r=$r:ident) 156 | 157 | $( impl[$($impl_:tt)*] $type:ty = $comparison:expr ;)* 158 | ) => ( 159 | $( 160 | impl<$($impl_)*> PWrapper<$type> { 161 | /// This method is only available with the "assert" feature. 162 | #[inline(always)] 163 | pub const fn const_eq(&self, $r: &$type) -> bool { 164 | let $l = &self.0; 165 | $comparison 166 | } 167 | } 168 | )* 169 | ) 170 | } 171 | 172 | impl_equality! { 173 | (l=l, r=r) 174 | 175 | impl[T: ?Sized,] PhantomData = true; 176 | impl[] PhantomPinned = true; 177 | impl[] () = true; 178 | 179 | impl[] Ordering = *l as u8 == *r as u8; 180 | impl[] AtomicOrdering = *l as u8 == *r as u8; 181 | 182 | impl[] Range = l.start == r.start && l.end == r.end; 183 | impl[] RangeInclusive = *l.start() == *r.start() && *l.end() == *r.end(); 184 | impl[] RangeFrom = l.start == r.start; 185 | impl[] RangeFull = true; 186 | impl[] RangeTo = l.end == r.end; 187 | impl[] RangeToInclusive = l.end == r.end; 188 | } 189 | -------------------------------------------------------------------------------- /const_format/src/__ascii_case_conv.rs: -------------------------------------------------------------------------------- 1 | mod word_iterator; 2 | 3 | use word_iterator::WordIterator; 4 | 5 | /// The casing style of a string. 6 | /// 7 | /// You can pass this to [`map_ascii_case`] to determine the casing style of the 8 | /// returned `&'static str`. 9 | /// 10 | /// 11 | /// [`map_ascii_case`]: ./macro.map_ascii_case.html 12 | #[derive(Debug, Copy, Clone, PartialEq)] 13 | pub enum Case { 14 | /// Lowercase 15 | Lower, 16 | /// Uppercase 17 | Upper, 18 | /// Pascal case, eg: `FooBarBaz`. The first character is always uppercase. 19 | Pascal, 20 | /// Camel case, eg: `fooBarBaz`. The first character is always lowercase. 21 | Camel, 22 | /// Snake case, eg: `foo_bar_baz`. Also turns the string lowercase. 23 | Snake, 24 | /// Snake case, eg: `FOO_BAR_BAZ`. Also turns the string uppercase. 25 | UpperSnake, 26 | /// Kebab case, eg: `foo-bar-baz`. Also turns the string lowercase. 27 | Kebab, 28 | /// Kebab case, eg: `FOO-BAR-BAZ`. Also turns the string uppercase. 29 | UpperKebab, 30 | } 31 | 32 | macro_rules! if_next_word { 33 | ($word_iterator:ident, $word_range:ident => $then:block $(else $else:block)? ) => { 34 | #[allow(unused_mut)] 35 | if let Some((niter, mut $word_range)) = $word_iterator.next() { 36 | $word_iterator = niter; 37 | 38 | $then 39 | } $(else $else)? 40 | }; 41 | } 42 | 43 | macro_rules! while_next_word { 44 | ($word_iterator:ident, $word_range:ident => $then:block) => { 45 | #[allow(unused_mut)] 46 | while let Some((niter, mut $word_range)) = $word_iterator.next() { 47 | $word_iterator = niter; 48 | 49 | $then 50 | } 51 | }; 52 | } 53 | 54 | struct WordCountAndLength { 55 | /// The amount of words 56 | count: usize, 57 | /// The length of all words added up 58 | length: usize, 59 | } 60 | 61 | const fn words_count_and_length(bytes: &[u8]) -> WordCountAndLength { 62 | let mut count = 0; 63 | let mut length = 0; 64 | let mut word_iter = WordIterator::new(bytes); 65 | while_next_word! {word_iter, word_range => { 66 | count += 1; 67 | length += word_range.end - word_range.start; 68 | }} 69 | WordCountAndLength { count, length } 70 | } 71 | 72 | pub const fn size_after_conversion(case: Case, s: &str) -> usize { 73 | match case { 74 | Case::Upper | Case::Lower => s.len(), 75 | Case::Pascal | Case::Camel => { 76 | let wcl = words_count_and_length(s.as_bytes()); 77 | wcl.length 78 | } 79 | Case::Snake | Case::Kebab | Case::UpperSnake | Case::UpperKebab => { 80 | let wcl = words_count_and_length(s.as_bytes()); 81 | wcl.length + wcl.count.saturating_sub(1) 82 | } 83 | } 84 | } 85 | 86 | pub const fn convert_str(case: Case, s: &str) -> [u8; N] { 87 | let mut arr = [0; N]; 88 | let mut inp = s.as_bytes(); 89 | let mut o = 0; 90 | 91 | macro_rules! map_bytes { 92 | ($byte:ident => $e:expr) => { 93 | while let [$byte, rem @ ..] = inp { 94 | let $byte = *$byte; 95 | inp = rem; 96 | arr[o] = $e; 97 | o += 1; 98 | } 99 | }; 100 | } 101 | 102 | macro_rules! write_byte { 103 | ($byte:expr) => { 104 | arr[o] = $byte; 105 | o += 1; 106 | }; 107 | } 108 | 109 | macro_rules! write_range_from { 110 | ($range:expr, $from:expr, $byte:ident => $mapper:expr) => {{ 111 | let mut range = $range; 112 | while range.start < range.end { 113 | let $byte = $from[range.start]; 114 | arr[o] = $mapper; 115 | 116 | range.start += 1; 117 | o += 1; 118 | } 119 | }}; 120 | } 121 | 122 | macro_rules! write_snake_kebab_case { 123 | ($separator:expr, $byte_conversion:expr) => {{ 124 | let mut word_iter = WordIterator::new(inp); 125 | 126 | if_next_word! {word_iter, word_range => { 127 | write_range_from!(word_range, inp, byte => $byte_conversion(byte)); 128 | 129 | while_next_word!{word_iter, word_range => { 130 | write_byte!($separator); 131 | write_range_from!(word_range, inp, byte => $byte_conversion(byte)); 132 | }} 133 | }} 134 | }}; 135 | } 136 | 137 | macro_rules! write_pascal_camel_case { 138 | ($first_word_conv:expr) => {{ 139 | let mut word_iter = WordIterator::new(inp); 140 | 141 | if_next_word! {word_iter, word_range => { 142 | write_byte!($first_word_conv(inp[word_range.start])); 143 | word_range.start += 1; 144 | write_range_from!(word_range, inp, byte => lowercase_u8(byte)); 145 | 146 | while_next_word!{word_iter, word_range => { 147 | write_byte!(uppercase_u8(inp[word_range.start])); 148 | word_range.start += 1; 149 | write_range_from!(word_range, inp, byte => lowercase_u8(byte)); 150 | }} 151 | }} 152 | }}; 153 | } 154 | 155 | match case { 156 | Case::Upper => map_bytes!(b => uppercase_u8(b)), 157 | Case::Lower => map_bytes!(b => lowercase_u8(b)), 158 | Case::Snake => write_snake_kebab_case!(b'_', lowercase_u8), 159 | Case::UpperSnake => write_snake_kebab_case!(b'_', uppercase_u8), 160 | Case::Kebab => write_snake_kebab_case!(b'-', lowercase_u8), 161 | Case::UpperKebab => write_snake_kebab_case!(b'-', uppercase_u8), 162 | Case::Pascal => write_pascal_camel_case!(uppercase_u8), 163 | Case::Camel => write_pascal_camel_case!(lowercase_u8), 164 | } 165 | 166 | arr 167 | } 168 | 169 | const CASE_DIFF: u8 = b'a' - b'A'; 170 | 171 | const fn uppercase_u8(b: u8) -> u8 { 172 | if let b'a'..=b'z' = b { 173 | b - CASE_DIFF 174 | } else { 175 | b 176 | } 177 | } 178 | 179 | const fn lowercase_u8(b: u8) -> u8 { 180 | if let b'A'..=b'Z' = b { 181 | b + CASE_DIFF 182 | } else { 183 | b 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /const_format/src/__str_methods/str_indexing.rs: -------------------------------------------------------------------------------- 1 | pub struct StrIndexArgsConv { 2 | pub str: &'static str, 3 | pub arg: T, 4 | } 5 | 6 | #[allow(non_snake_case)] 7 | pub const fn StrIndexArgsConv(str: &'static str, arg: T) -> StrIndexArgsConv { 8 | StrIndexArgsConv { str, arg } 9 | } 10 | 11 | pub struct StrIndexArgs { 12 | pub str: &'static str, 13 | pub index_validity: IndexValidity, 14 | pub used_rstart: usize, 15 | pub used_rlen: usize, 16 | pub used_rend: usize, 17 | } 18 | 19 | #[derive(Copy, Clone)] 20 | #[cfg_attr(test, derive(Debug, PartialEq))] 21 | pub enum IndexValidity { 22 | Valid, 23 | StartOob(usize), 24 | StartInsideChar(usize), 25 | EndOob(usize), 26 | EndInsideChar(usize), 27 | } 28 | 29 | impl IndexValidity { 30 | pub const fn is_valid(self) -> bool { 31 | matches!(self, Self::Valid) 32 | } 33 | 34 | pub const fn assert_valid(self) { 35 | match self { 36 | Self::Valid => (), 37 | Self::StartOob(index) => [/*start index is out of bounds*/][index], 38 | Self::StartInsideChar(index) => [/*start index is not on a char boundary*/][index], 39 | Self::EndOob(index) => [/*end index is out of bounds*/][index], 40 | Self::EndInsideChar(index) => [/*end index is not on a char boundary*/][index], 41 | } 42 | } 43 | } 44 | 45 | macro_rules! pass_range_types { 46 | ($macro:ident) => { 47 | const _: () = { 48 | use core::ops; 49 | 50 | #[allow(unused_imports)] 51 | use crate::__hidden_utils::{is_char_boundary_no_len_check, max_usize, saturating_add}; 52 | 53 | $macro! { 54 | fn(self, usize) { 55 | let mut end = saturating_add(self.arg, 1); 56 | let bytes = self.str.as_bytes(); 57 | 58 | if end < self.str.len() { 59 | while !is_char_boundary_no_len_check(bytes, end) { 60 | end = saturating_add(end, 1); 61 | } 62 | } 63 | 64 | self.arg .. end 65 | } 66 | 67 | fn(self, ops::Range) { 68 | let ops::Range{start, end} = self.arg; 69 | start .. max_usize(start, end) 70 | } 71 | 72 | fn(self, ops::RangeTo) { 73 | 0..self.arg.end 74 | } 75 | 76 | fn(self, ops::RangeFrom) { 77 | self.arg.start..self.str.len() 78 | } 79 | 80 | fn(self, ops::RangeInclusive) { 81 | let start = *self.arg.start(); 82 | start .. max_usize(saturating_add(*self.arg.end(), 1), start) 83 | } 84 | 85 | fn(self, ops::RangeToInclusive) { 86 | 0 .. saturating_add(self.arg.end, 1) 87 | } 88 | 89 | fn(self, ops::RangeFull) { 90 | 0 .. self.str.len() 91 | } 92 | } 93 | }; 94 | }; 95 | } 96 | pub(super) use pass_range_types; 97 | 98 | macro_rules! define_conversions { 99 | ( 100 | $( fn($self:ident, $ty:ty) $block:block )* 101 | ) => { 102 | 103 | $( 104 | impl StrIndexArgsConv<$ty> { 105 | pub const fn conv($self) -> StrIndexArgs { 106 | use crate::__hidden_utils::is_char_boundary_no_len_check; 107 | 108 | let range = $block; 109 | 110 | let str_len = $self.str.len(); 111 | 112 | let mut used_rstart = 0; 113 | let mut used_rend = str_len; 114 | 115 | let mut index_validity = IndexValidity::Valid; 116 | let bytes = $self.str.as_bytes(); 117 | 118 | if range.end > str_len { 119 | index_validity = IndexValidity::EndOob(range.end); 120 | } else if is_char_boundary_no_len_check(bytes, range.end) { 121 | used_rend = range.end; 122 | } else { 123 | index_validity = IndexValidity::EndInsideChar(range.end); 124 | }; 125 | 126 | if range.start > str_len { 127 | index_validity = IndexValidity::StartOob(range.start); 128 | } else if is_char_boundary_no_len_check(bytes, range.start) { 129 | used_rstart = range.start; 130 | } else { 131 | index_validity = IndexValidity::StartInsideChar(range.start); 132 | }; 133 | 134 | StrIndexArgs { 135 | str: $self.str, 136 | index_validity, 137 | used_rstart, 138 | used_rend, 139 | used_rlen: used_rend - used_rstart, 140 | } 141 | } 142 | } 143 | )* 144 | }; 145 | } 146 | 147 | pass_range_types! {define_conversions} 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use super::*; 152 | 153 | #[test] 154 | fn index_validity_test() { 155 | macro_rules! miv { 156 | ($str:expr, $range:expr) => { 157 | StrIndexArgsConv($str, $range).conv().index_validity 158 | }; 159 | } 160 | 161 | assert_eq!(miv!("効率的", 3), IndexValidity::Valid); 162 | assert_eq!(miv!("効率的", 6), IndexValidity::Valid); 163 | assert_eq!(miv!("効率的", 3..6), IndexValidity::Valid); 164 | 165 | assert_eq!(miv!("効率的", 4..6), IndexValidity::StartInsideChar(4)); 166 | assert_eq!(miv!("効率的", 3..5), IndexValidity::EndInsideChar(5)); 167 | assert_eq!(miv!("効率的", 7..9), IndexValidity::StartInsideChar(7)); 168 | 169 | assert_eq!(miv!("効率的", 100..9), IndexValidity::StartOob(100)); 170 | assert_eq!(miv!("効率的", 3..10), IndexValidity::EndOob(10)); 171 | assert_eq!(miv!("効率的", 9), IndexValidity::EndOob(10)); 172 | assert_eq!(miv!("効率的", 10), IndexValidity::StartOob(10)); 173 | assert_eq!(miv!("効率的", 100..900), IndexValidity::StartOob(100)); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /const_format/tests/fmt_tests/display_formatting.rs: -------------------------------------------------------------------------------- 1 | use cfmt_a::{ 2 | fmt::{ComputeStrLength, Error, Formatter, FormattingFlags, StrWriter}, 3 | try_, 4 | wrapper_types::PWrapper, 5 | }; 6 | 7 | #[derive(Copy, Clone)] 8 | struct DisplayFoo { 9 | x: u32, 10 | y: &'static str, 11 | z: bool, 12 | debug: i8, 13 | } 14 | 15 | impl DisplayFoo { 16 | pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 17 | try_!(PWrapper(self.x).const_display_fmt(f)); 18 | try_!(PWrapper(" ").const_display_fmt(f)); 19 | try_!(PWrapper(self.y).const_display_fmt(f)); 20 | try_!(PWrapper(" ").const_display_fmt(f)); 21 | try_!(PWrapper(self.z).const_display_fmt(f)); 22 | try_!(PWrapper(" ").const_display_fmt(f)); 23 | try_!(PWrapper(self.debug).const_debug_fmt(f)); 24 | Ok(()) 25 | } 26 | } 27 | 28 | const fn inner( 29 | this: &DisplayFoo, 30 | writer: &mut StrWriter, 31 | flags: FormattingFlags, 32 | ) -> Result { 33 | try_!(this.const_display_fmt(&mut writer.make_formatter(flags))); 34 | 35 | let mut str_len = ComputeStrLength::new(); 36 | try_!(this.const_display_fmt(&mut str_len.make_formatter(flags))); 37 | 38 | Ok(str_len.len()) 39 | } 40 | 41 | #[test] 42 | fn test_display_foo() { 43 | fn test_case( 44 | this: &DisplayFoo, 45 | writer: &mut StrWriter, 46 | flags: FormattingFlags, 47 | expected: &str, 48 | ) { 49 | writer.clear(); 50 | let len = inner(this, writer, flags).unwrap(); 51 | 52 | assert_eq!(writer.as_str(), expected); 53 | assert_eq!(writer.len(), len, "{}", writer.as_str()); 54 | } 55 | 56 | let writer: &mut StrWriter = &mut StrWriter::new([0; 1024]); 57 | 58 | { 59 | let this = DisplayFoo { 60 | x: 3, 61 | y: "--\n5\n--", 62 | z: false, 63 | debug: 15, 64 | }; 65 | let flag = FormattingFlags::NEW.set_alternate(false); 66 | test_case(&this, writer, flag, "3 --\n5\n-- false 15"); 67 | test_case( 68 | &this, 69 | writer, 70 | flag.set_hexadecimal(), 71 | "3 --\n5\n-- false F", 72 | ); 73 | test_case( 74 | &this, 75 | writer, 76 | flag.set_binary(), 77 | "3 --\n5\n-- false 1111", 78 | ); 79 | 80 | let altflag = FormattingFlags::NEW.set_alternate(true); 81 | test_case(&this, writer, altflag, "3 --\n5\n-- false 15"); 82 | test_case( 83 | &this, 84 | writer, 85 | altflag.set_hexadecimal(), 86 | "3 --\n5\n-- false 0xF", 87 | ); 88 | test_case( 89 | &this, 90 | writer, 91 | altflag.set_binary(), 92 | "3 --\n5\n-- false 0b1111", 93 | ); 94 | } 95 | 96 | { 97 | let this = DisplayFoo { 98 | x: 3, 99 | y: "--\n5\n--", 100 | z: true, 101 | debug: -2, 102 | }; 103 | let flag = FormattingFlags::NEW.set_alternate(false); 104 | test_case(&this, writer, flag, "3 --\n5\n-- true -2"); 105 | test_case( 106 | &this, 107 | writer, 108 | flag.set_hexadecimal(), 109 | "3 --\n5\n-- true FE", 110 | ); 111 | test_case( 112 | &this, 113 | writer, 114 | flag.set_binary(), 115 | "3 --\n5\n-- true 11111110", 116 | ); 117 | 118 | let altflag = FormattingFlags::NEW.set_alternate(true); 119 | test_case(&this, writer, altflag, "3 --\n5\n-- true -2"); 120 | test_case( 121 | &this, 122 | writer, 123 | altflag.set_hexadecimal(), 124 | "3 --\n5\n-- true 0xFE", 125 | ); 126 | test_case( 127 | &this, 128 | writer, 129 | altflag.set_binary(), 130 | "3 --\n5\n-- true 0b11111110", 131 | ); 132 | } 133 | } 134 | 135 | /*//////////////////////////////////////////////////////////////////////////////// 136 | Testing Display formatting for non-primitive integer or `&str` types, 137 | because those types are already tested in other tests. 138 | *///////////////////////////////////////////////////////////////////////////////// 139 | 140 | use std::num::{NonZeroI8, NonZeroIsize, NonZeroU8, NonZeroUsize}; 141 | 142 | macro_rules! unwrap_opt { 143 | ($opt:expr) => { 144 | match $opt { 145 | Some(x) => x, 146 | None => loop {}, 147 | } 148 | }; 149 | } 150 | 151 | #[test] 152 | fn display_fmt_other_types() { 153 | const fn inner(fmt: &mut Formatter<'_>) -> cfmt_a::Result { 154 | cfmt_a::writec!( 155 | fmt, 156 | concat!("{},{};", "{},{};{},{};{},{};{},{};{},{},{},{};",), 157 | false, 158 | true, 159 | unwrap_opt!(NonZeroI8::new(-13)), 160 | unwrap_opt!(NonZeroI8::new(21)), 161 | unwrap_opt!(NonZeroIsize::new(-13)), 162 | unwrap_opt!(NonZeroIsize::new(21)), 163 | unwrap_opt!(NonZeroU8::new(3)), 164 | unwrap_opt!(NonZeroU8::new(13)), 165 | unwrap_opt!(NonZeroUsize::new(3)), 166 | unwrap_opt!(NonZeroUsize::new(13)), 167 | 'o', 168 | 'ñ', 169 | '个', 170 | '\u{100000}', 171 | ) 172 | } 173 | 174 | let flags = [ 175 | FormattingFlags::NEW.set_alternate(false).set_decimal(), 176 | FormattingFlags::NEW.set_alternate(false).set_hexadecimal(), 177 | FormattingFlags::NEW.set_alternate(false).set_binary(), 178 | FormattingFlags::NEW.set_alternate(true).set_decimal(), 179 | FormattingFlags::NEW.set_alternate(true).set_hexadecimal(), 180 | FormattingFlags::NEW.set_alternate(true).set_binary(), 181 | ]; 182 | 183 | let writer: &mut StrWriter = &mut StrWriter::new([0; 1024]); 184 | for flag in flags.iter().copied() { 185 | writer.clear(); 186 | 187 | inner(&mut writer.make_formatter(flag)).unwrap(); 188 | 189 | assert_eq!( 190 | writer.as_str(), 191 | "false,true;-13,21;-13,21;3,13;3,13;o,ñ,个,\u{100000};", 192 | ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /const_format/src/pargument.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::wrong_self_convention)] 2 | 3 | use crate::{ 4 | char_encoding::FmtChar, 5 | formatting::{Formatting, FormattingFlags}, 6 | wrapper_types::PWrapper, 7 | }; 8 | 9 | #[doc(hidden)] 10 | /// The uniform representation for every argument of the concatcp macro. 11 | pub struct PArgument { 12 | pub elem: PVariant, 13 | pub fmt_len: usize, 14 | pub fmt: Formatting, 15 | pub fmt_flags: FormattingFlags, 16 | } 17 | 18 | impl PArgument { 19 | /// Calculates the length of the string after adding up all the PArguments 20 | pub const fn calc_len(mut args: &[PArgument]) -> usize { 21 | let mut sum = 0; 22 | 23 | while let [curr, rem @ ..] = args { 24 | args = rem; 25 | sum += curr.fmt_len; 26 | } 27 | 28 | sum 29 | } 30 | } 31 | 32 | #[doc(hidden)] 33 | pub enum PVariant { 34 | Str(&'static str), 35 | Int(Integer), 36 | Char(FmtChar), 37 | } 38 | 39 | #[derive(Debug, Copy, Clone)] 40 | pub struct Integer { 41 | pub is_negative: bool, 42 | pub unsigned: u128, 43 | pub mask: &'static u128, // A mask which disables the bits that weren't in the original number 44 | } 45 | 46 | #[doc(hidden)] 47 | pub struct PConvWrapper(pub T); 48 | 49 | macro_rules! pconvwrapper_impls { 50 | ( $( ($Signed:ty, $Unsigned:ty) )* ) => ( 51 | pconvwrapper_impls!{ 52 | @inner to_pargument_display, compute_display_len, Formatting::Display; 53 | $(($Signed, $Unsigned))* 54 | } 55 | pconvwrapper_impls!{ 56 | @inner to_pargument_debug, compute_debug_len, Formatting::Debug; 57 | $(($Signed, $Unsigned))* 58 | } 59 | 60 | $( 61 | #[doc(hidden)] 62 | impl PConvWrapper<$Signed>{ 63 | pub const fn to_integer(self)->Integer{ 64 | Integer{ 65 | is_negative: self.0 < 0, 66 | unsigned: PWrapper(self.0).unsigned_abs() as u128, 67 | mask: &(((!(0 as $Signed)) as $Unsigned) as u128), 68 | } 69 | } 70 | } 71 | 72 | #[doc(hidden)] 73 | impl PConvWrapper<$Unsigned>{ 74 | pub const fn to_integer(self)->Integer{ 75 | Integer{ 76 | is_negative: false, 77 | unsigned: self.0 as u128, 78 | mask: &((!(0 as $Unsigned)) as u128), 79 | } 80 | } 81 | } 82 | )* 83 | ); 84 | (@inner 85 | $method:ident, 86 | $called:ident, 87 | $formatting:expr; 88 | $( ($Signed:ty, $Unsigned:ty) )* 89 | ) => ( 90 | $( 91 | #[doc(hidden)] 92 | impl PConvWrapper<$Signed> { 93 | pub const fn $method(self, fmt_flags: FormattingFlags)->PArgument{ 94 | PArgument { 95 | fmt_len: $crate::pmr::PWrapper(self.0).$called(fmt_flags), 96 | fmt: $formatting, 97 | fmt_flags, 98 | elem: PVariant::Int(self.to_integer()), 99 | } 100 | } 101 | } 102 | 103 | #[doc(hidden)] 104 | impl PConvWrapper<$Unsigned> { 105 | pub const fn $method(self, fmt_flags: FormattingFlags)->PArgument{ 106 | PArgument { 107 | fmt_len: $crate::pmr::PWrapper(self.0).$called(fmt_flags), 108 | fmt: $formatting, 109 | fmt_flags, 110 | elem: PVariant::Int(self.to_integer()), 111 | } 112 | } 113 | } 114 | )* 115 | ); 116 | } 117 | 118 | pconvwrapper_impls! { 119 | (i8, u8) 120 | (i16, u16) 121 | (i32, u32) 122 | (i64, u64) 123 | (i128, u128) 124 | (isize, usize) 125 | } 126 | 127 | #[doc(hidden)] 128 | impl PConvWrapper { 129 | #[inline] 130 | pub const fn to_pargument_display(self, _: FormattingFlags) -> PArgument { 131 | self.0 132 | } 133 | #[inline] 134 | pub const fn to_pargument_debug(self, _: FormattingFlags) -> PArgument { 135 | self.0 136 | } 137 | } 138 | 139 | #[doc(hidden)] 140 | impl PConvWrapper { 141 | #[inline] 142 | pub const fn to_pargument_display(self, _: FormattingFlags) -> PArgument { 143 | PConvWrapper(if self.0 { "true" } else { "false" }) 144 | .to_pargument_display(FormattingFlags::DEFAULT) 145 | } 146 | #[inline] 147 | pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { 148 | self.to_pargument_display(fmt_flags) 149 | } 150 | } 151 | 152 | #[doc(hidden)] 153 | impl PConvWrapper { 154 | #[inline] 155 | pub const fn to_pargument_display(self, fmt_flags: FormattingFlags) -> PArgument { 156 | let elem = crate::char_encoding::char_to_display(self.0); 157 | PArgument { 158 | fmt_len: elem.len(), 159 | fmt_flags, 160 | fmt: Formatting::Display, 161 | elem: PVariant::Char(elem), 162 | } 163 | } 164 | #[inline] 165 | pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { 166 | let elem = crate::char_encoding::char_to_debug(self.0); 167 | PArgument { 168 | fmt_len: elem.len(), 169 | fmt_flags, 170 | fmt: Formatting::Debug, 171 | elem: PVariant::Char(elem), 172 | } 173 | } 174 | } 175 | 176 | #[doc(hidden)] 177 | impl PConvWrapper<&'static str> { 178 | #[inline] 179 | pub const fn to_pargument_display(self, fmt_flags: FormattingFlags) -> PArgument { 180 | PArgument { 181 | fmt_len: PWrapper(self.0).compute_display_len(fmt_flags), 182 | fmt_flags, 183 | fmt: Formatting::Display, 184 | elem: PVariant::Str(self.0), 185 | } 186 | } 187 | #[inline] 188 | pub const fn to_pargument_debug(self, fmt_flags: FormattingFlags) -> PArgument { 189 | PArgument { 190 | fmt_len: PWrapper(self.0).compute_debug_len(fmt_flags), 191 | fmt_flags, 192 | fmt: Formatting::Debug, 193 | elem: PVariant::Str(self.0), 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /const_format/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Miscelaneous functions. 2 | 3 | use core::ops::Range; 4 | 5 | /// Newtype wrapper to get around limitations in `const fn`s 6 | pub(crate) struct Constructor(#[allow(dead_code)] fn() -> T); 7 | 8 | pub use crate::slice_cmp::{str_eq, u8_slice_eq}; 9 | 10 | #[doc(hidden)] 11 | #[inline] 12 | pub const fn saturate_range(s: &[u8], range: &Range) -> Range { 13 | let len = s.len(); 14 | let end = min_usize(range.end, len); 15 | min_usize(range.start, end)..end 16 | } 17 | 18 | #[doc(hidden)] 19 | #[cfg(feature = "rust_1_64")] 20 | #[repr(C)] 21 | pub union Dereference<'a, T: ?Sized> { 22 | pub ptr: *const T, 23 | pub reff: &'a T, 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | macro_rules! slice_up_to_len_alt_docs { 29 | ($item:item) => { 30 | /// A const equivalent of `&slice[..len]`. 31 | /// 32 | /// If `slice.len() < len`, this simply returns `slice` back. 33 | /// 34 | /// # Runtime 35 | /// 36 | /// If the "rust_1_64" feature is disabled, 37 | /// thich takes linear time to remove the trailing elements, 38 | /// proportional to `slice.len() - len`. 39 | /// 40 | /// If the "rust_1_64" feature is enabled, it takes constant time to run. 41 | /// 42 | /// # Example 43 | /// 44 | /// ```rust 45 | /// use const_format::utils::slice_up_to_len_alt; 46 | /// 47 | /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89]; 48 | /// 49 | /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2); 50 | /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4); 51 | /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX); 52 | /// 53 | /// assert_eq!(TWO, &[3, 5]); 54 | /// assert_eq!(FOUR, &[3, 5, 8, 13]); 55 | /// assert_eq!(FIBB, ALL); 56 | /// 57 | /// ``` 58 | $item 59 | }; 60 | } 61 | 62 | slice_up_to_len_alt_docs! { 63 | #[cfg(feature = "rust_1_64")] 64 | #[inline(always)] 65 | pub const fn slice_up_to_len_alt(slice: &[T], len: usize) -> &[T] { 66 | slice_up_to_len(slice, len) 67 | } 68 | } 69 | slice_up_to_len_alt_docs! { 70 | #[cfg(not(feature = "rust_1_64"))] 71 | pub const fn slice_up_to_len_alt(slice: &[T], len: usize) -> &[T] { 72 | let mut rem = slice.len().saturating_add(1).saturating_sub(len); 73 | let mut ret = slice; 74 | 75 | if rem == 0 { 76 | return slice; 77 | } 78 | 79 | macro_rules! slice_up_to_len_alt_impl{ 80 | ( 81 | $( ($len:expr, [$($ignored:tt)*]) )* 82 | )=>{ 83 | $( 84 | while rem > $len { 85 | if let [next @ .., $($ignored)* ] = ret { 86 | ret = next; 87 | rem -= $len; 88 | } 89 | } 90 | )* 91 | } 92 | } 93 | slice_up_to_len_alt_impl!{ 94 | (36, [_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,]) 95 | (6, [_, _, _, _, _, _]) 96 | (1, [_]) 97 | } 98 | ret 99 | } 100 | } 101 | 102 | //////////////////////////////////////////////////////////////////////////////// 103 | 104 | macro_rules! slice_up_to_len_docs { 105 | ($item:item) => { 106 | /// A conditionally-const equivalent of `&slice[..len]`. 107 | /// 108 | /// If `slice.len() < len`, this simply returns `slice` back. 109 | /// 110 | /// # Constness 111 | /// 112 | /// This function takes constant time, 113 | /// and in order to be `const fn` it requires the "rust_1_64" 114 | /// feature to be enabled. 115 | /// 116 | /// # Example 117 | /// 118 | /// ```rust 119 | /// use const_format::utils::slice_up_to_len_alt; 120 | /// 121 | /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89]; 122 | /// 123 | /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2); 124 | /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4); 125 | /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX); 126 | /// 127 | /// assert_eq!(TWO, &[3, 5]); 128 | /// assert_eq!(FOUR, &[3, 5, 8, 13]); 129 | /// assert_eq!(FIBB, ALL); 130 | /// 131 | /// ``` 132 | $item 133 | }; 134 | } 135 | 136 | slice_up_to_len_docs! { 137 | #[cfg(feature = "rust_1_64")] 138 | #[inline] 139 | pub const fn slice_up_to_len(slice: &[T], len: usize) -> &[T] { 140 | if len > slice.len() { 141 | return slice; 142 | } 143 | 144 | // Doing this to get a slice up to length at compile-time 145 | unsafe { 146 | let raw_slice = core::ptr::slice_from_raw_parts(slice.as_ptr(), len); 147 | Dereference { ptr: raw_slice }.reff 148 | } 149 | } 150 | } 151 | 152 | slice_up_to_len_docs! { 153 | #[cfg(not(feature = "rust_1_64"))] 154 | #[inline] 155 | pub fn slice_up_to_len(slice: &[T], len: usize) -> &[T] { 156 | if len > slice.len() { 157 | return slice; 158 | } 159 | 160 | &slice[..len] 161 | } 162 | } 163 | 164 | //////////////////////////////////////////////////////////////////////////////// 165 | 166 | pub(crate) const fn min_usize(l: usize, r: usize) -> usize { 167 | if l < r { 168 | l 169 | } else { 170 | r 171 | } 172 | } 173 | 174 | #[cfg(test)] 175 | mod tests { 176 | use super::*; 177 | 178 | #[test] 179 | fn test_slice_up_to_len_alt() { 180 | let mut list = [0u16; 256]; 181 | 182 | (100..).zip(list.iter_mut()).for_each(|(i, m)| *m = i); 183 | 184 | for i in 0..list.len() { 185 | assert_eq!(slice_up_to_len_alt(&list, i), &list[..i]); 186 | } 187 | } 188 | 189 | #[test] 190 | fn slice_in_bounds() { 191 | assert_eq!(slice_up_to_len(&[3, 5], 0), []); 192 | assert_eq!(slice_up_to_len(&[3, 5], 1), [3]); 193 | assert_eq!(slice_up_to_len(&[3, 5], 2), [3, 5]); 194 | assert_eq!(slice_up_to_len(&[3, 5], 3), [3, 5]); 195 | assert_eq!(slice_up_to_len(&[3, 5], 4), [3, 5]); 196 | } 197 | } 198 | --------------------------------------------------------------------------------