local_fmt/utils.rs
1/// A wrapper for a byte array buffer.
2/// The total field is the number of bytes used in the buffer.
3/// The buffer field is the byte array.
4/// The buffer field is split at the total field to get the used bytes.
5pub struct UtilBufWrapper<const N: usize> {
6 pub buffer: [u8; N],
7 pub total: usize,
8}
9
10impl<const N: usize> UtilBufWrapper<N> {
11 pub const fn new(buffer: [u8; N], total: usize) -> Self {
12 Self { buffer, total }
13 }
14
15 /// Returns the buffer split at the total field.
16 /// The used bytes are returned.
17 pub const fn buffer(&self) -> &[u8] {
18 self.buffer.split_at(self.total).0
19 }
20
21 /// Returns the buffer as a str.
22 pub const fn as_str(&self) -> &str {
23 unsafe { std::str::from_utf8_unchecked(self.buffer()) }
24 }
25}
26
27/// Converts a u128 to a str as a byte array.
28/// The buffer is 39 bytes long.
29/// The maximum number of digits in a u128 is 39.
30///
31/// # Example
32/// ```
33/// use local_fmt::utils::const_u128_to_str;
34///
35/// const BUFFER: &[u8] = const_u128_to_str(1234567890).buffer();
36/// assert_eq!(BUFFER, b"1234567890");
37/// ```
38pub const fn const_u128_to_str(n: u128) -> UtilBufWrapper<39> {
39 if n == 0 {
40 let buf: [u8; 39] = [
41 b'0', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 ];
44 return UtilBufWrapper::new(buf, 1);
45 }
46 let mut buffer = [0u8; 39];
47 let mut i = 0;
48 let mut n = n;
49 while n > 0 {
50 buffer[i] = b'0' + (n % 10) as u8;
51 n /= 10;
52 i += 1;
53 }
54 let mut result = [0u8; 39];
55 let mut j = 0;
56 while j < i {
57 result[j] = buffer[i - j - 1];
58 j += 1;
59 }
60 UtilBufWrapper::new(result, i)
61}
62
63/// Converts an i128 to a str as a byte array.
64/// The buffer is 40 bytes long.
65/// The maximum number of digits in an i128 is 39.
66/// The first byte is a '-' if the number is negative.
67///
68/// # Example
69/// ```
70/// use local_fmt::utils::const_i128_to_str;
71///
72/// const BUFFER: &[u8] = const_i128_to_str(-1234567890).buffer();
73/// assert_eq!(BUFFER, b"-1234567890");
74/// ```
75pub const fn const_i128_to_str(n: i128) -> UtilBufWrapper<40> {
76 let UtilBufWrapper { buffer: buf, total } = const_u128_to_str(n.unsigned_abs());
77 let mut buffer = [0u8; 40];
78 let mut i = 0;
79 if n < 0 {
80 buffer[i] = b'-';
81 i += 1;
82 }
83 let mut j = 0;
84 while j < total {
85 buffer[i] = buf[j];
86 i += 1;
87 j += 1;
88 }
89
90 UtilBufWrapper::new(buffer, i)
91}
92
93/// A macro for creating a panic message with placeholders for arguments.
94/// The message is formatted with the arguments and then a panic is raised.
95///
96/// # Example
97/// ```rust
98/// use local_fmt::{panic_builder, gen_static_message};
99///
100/// const _: () = {
101/// let message = gen_static_message!("Error: {0} {1} {2}");
102/// // panic_builder!(message, ["This"], ["is"], ["a test"]);
103/// };
104/// ```
105/// ```text
106/// error[E0080]: evaluation of constant value failed
107/// --> local-fmt/src/utils.rs:103:5
108/// |
109/// 9 | panic_builder!(message, ["This"], ["is"], ["a test"]);
110/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Error: This is a test', local-fmt/src/utils.rs:9:5
111/// |
112/// ```
113///
114#[macro_export]
115macro_rules! panic_builder {
116 ($message:ident, $([$($arg:tt)+]),* $(,)?) => {
117 {
118 let buffer = $crate::fmt_builder!($message,
119 $(
120 [$($arg)+],
121 )*
122 );
123 panic!("{}", buffer.as_str());
124 }
125 };
126}
127
128/// Formats a message with placeholders for arguments.
129///
130/// # Example
131/// ```
132/// use local_fmt::{fmt_builder, UtilBufWrapper, StaticMessage, gen_static_message};
133///
134/// const MESSAGE: StaticMessage<1> = gen_static_message!("Hello, {0}!");
135/// const TEXT: &'static str = fmt_builder!(MESSAGE, ["World"]).as_str();
136///
137/// assert_eq!(TEXT, "Hello, World!");
138#[macro_export]
139macro_rules! fmt_builder {
140 (@ $arg:literal) => {
141 $arg.as_bytes()
142 };
143 (@ $arg:ident) => {
144 $arg.as_bytes()
145 };
146 (@ b; $arg:ident) => {
147 $arg
148 };
149 (@ u; $arg:expr ) => {
150 $crate::const_u128_to_str($arg as u128).buffer()
151 };
152 (@ i; $arg:expr) => {
153 $crate::const_i128_to_str($arg as i128).buffer()
154 };
155 ($message:ident, $([$($args:tt)+]),* $(,)?) => {
156 unsafe {
157 $message.const_format::<1024>(
158 &[
159 $(
160 $crate::fmt_builder!(@ $($args)+),
161 )*
162 ]
163 )
164 }
165 };
166}