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}