local_fmt/message/
refer.rs

1use std::fmt::Display;
2
3use crate::{const_i128_to_str, const_u128_to_str, UtilBufWrapper};
4
5use super::CreateMessageError;
6
7#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
8pub enum RefMessageFormat<'a> {
9    RefText(&'a str),
10    UNumber(u128),
11    INumber(i128),
12    Placeholder(usize),
13}
14
15impl Display for RefMessageFormat<'_> {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            RefMessageFormat::RefText(text) => write!(f, "{}", text),
19            RefMessageFormat::UNumber(n) => write!(f, "{}", n),
20            RefMessageFormat::INumber(n) => write!(f, "{}", n),
21            RefMessageFormat::Placeholder(n) => write!(f, "{{{}}}", n),
22        }
23    }
24}
25
26pub type StaticMessage<const N: usize> = RefMessage<'static, N>;
27
28#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
29pub struct RefMessage<'a, const N: usize> {
30    formats: &'a [RefMessageFormat<'a>],
31}
32
33impl<'a, const N: usize> RefMessage<'a, N> {
34    /// # Safety
35    /// The caller must ensure that the `formats` slice is valid.
36    pub const unsafe fn new_unchecked(formats: &'a [RefMessageFormat<'a>]) -> Self {
37        Self { formats }
38    }
39
40    pub const fn new(formats: &'a [RefMessageFormat<'a>]) -> Result<Self, CreateMessageError> {
41        let mut numbers = [false; N];
42
43        let mut current = 0;
44
45        while formats.len() > current {
46            if let RefMessageFormat::Placeholder(n) = formats[current] {
47                if n >= N {
48                    return Err(CreateMessageError::InvalidNumber { number: n, n: N });
49                }
50                numbers[n] = true;
51            }
52            current += 1;
53        }
54
55        let mut current = 0;
56
57        while numbers.len() > current {
58            if !numbers[current] {
59                return Err(CreateMessageError::WithoutNumber {
60                    number: current,
61                    n: N,
62                });
63            }
64            current += 1;
65        }
66
67        Ok(Self { formats })
68    }
69
70    #[track_caller]
71    pub const fn new_panic(formats: &'a [RefMessageFormat<'a>]) -> Self {
72        match Self::new(formats) {
73            Ok(message) => message,
74            Err(error) => error.panic(),
75        }
76    }
77
78    pub fn format(&self, args: &[&str; N]) -> String {
79        let mut result = String::new();
80
81        for format in self.formats {
82            match format {
83                RefMessageFormat::RefText(text) => result.push_str(text),
84                RefMessageFormat::UNumber(n) => result.push_str(&n.to_string()),
85                RefMessageFormat::INumber(n) => result.push_str(&n.to_string()),
86                RefMessageFormat::Placeholder(n) => result.push_str(args[*n]),
87            }
88        }
89
90        result
91    }
92
93    pub const fn len(&self) -> usize {
94        self.formats.len()
95    }
96
97    pub const fn is_empty(&self) -> bool {
98        self.formats.is_empty()
99    }
100
101    pub const fn formats(&self) -> &'a [RefMessageFormat<'a>] {
102        self.formats
103    }
104}
105
106impl<const N: usize> StaticMessage<N> {
107    /// Formats the message with the given arguments.
108    ///
109    /// # Safety
110    /// Ensure that the total of all characters does not exceed SIZE
111    ///
112    /// # Example
113    /// ```
114    /// use local_fmt::{RefMessageFormat, StaticMessage, utils::UtilBufWrapper};
115    ///
116    /// const MESSAGE: StaticMessage<2> = StaticMessage::new_panic(&[
117    ///    RefMessageFormat::RefText("Hello! "),
118    ///    RefMessageFormat::Placeholder(0),
119    ///    RefMessageFormat::RefText(" World! "),
120    ///    RefMessageFormat::Placeholder(1),
121    ///    RefMessageFormat::RefText("!"),
122    /// ]);
123    ///
124    /// const TEXT: &str = {
125    ///     const BUF: UtilBufWrapper<32> = unsafe {
126    ///         MESSAGE.const_format::<32>(&[b"Beautiful", b"Rust!"])
127    ///     };
128    ///     BUF.as_str()
129    /// };
130    ///
131    /// assert_eq!(TEXT, "Hello! Beautiful World! Rust!!");
132    /// ```
133    pub const unsafe fn const_format<const SIZE: usize>(
134        &self,
135        args: &[&[u8]; N],
136    ) -> UtilBufWrapper<SIZE> {
137        let mut buf = [0u8; SIZE];
138        let mut total = 0;
139
140        let mut i = 0;
141        while i < self.formats.len() {
142            macro_rules! process {
143                ($bytes:expr) => {
144                    match $bytes {
145                        bytes => {
146                            let len = bytes.len();
147                            let mut j = 0;
148                            while j < len {
149                                buf[total] = bytes[j];
150                                total += 1;
151                                j += 1;
152                            }
153                            i += 1;
154                        }
155                    }
156                };
157            }
158            match &self.formats[i] {
159                RefMessageFormat::RefText(text) => process!(text.as_bytes()),
160                RefMessageFormat::UNumber(n) => process!(const_u128_to_str(*n).buffer()),
161                RefMessageFormat::INumber(n) => process!(const_i128_to_str(*n).buffer()),
162                RefMessageFormat::Placeholder(n) => process!(args[*n]),
163            }
164        }
165
166        UtilBufWrapper::new(buf, total)
167    }
168}
169
170impl<const N: usize> Display for RefMessage<'_, N> {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        for format in self.formats {
173            write!(f, "{}", format)?;
174        }
175
176        Ok(())
177    }
178}