1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TypeSpecifier {
12 BinaryNumber,
14
15 CharacterAsciiValue,
17
18 DecimalNumber,
20
21 IntegerNumber,
23
24 ScientificNotation,
26
27 UnsignedDecimalNumber,
29
30 FloatingPointNumber,
32
33 FloatingPointNumberWithSignificantDigits,
35
36 OctalNumber,
38
39 String,
41
42 TrueOrFalse,
44
45 TypeOfArgument,
47
48 PrimitiveValue,
50
51 HexadecimalNumberLowercase,
53
54 HexadecimalNumberUppercase,
56
57 Json,
59}
60
61impl TypeSpecifier {
62 const fn is_numeric(self) -> bool {
65 matches!(
66 self,
67 Self::BinaryNumber
68 | Self::DecimalNumber
69 | Self::IntegerNumber
70 | Self::ScientificNotation
71 | Self::UnsignedDecimalNumber
72 | Self::FloatingPointNumber
73 | Self::FloatingPointNumberWithSignificantDigits
74 | Self::OctalNumber
75 | Self::HexadecimalNumberLowercase
76 | Self::HexadecimalNumberUppercase
77 )
78 }
79}
80
81impl std::fmt::Display for TypeSpecifier {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 let specifier = match self {
84 Self::BinaryNumber => 'b',
85 Self::CharacterAsciiValue => 'c',
86 Self::DecimalNumber => 'd',
87 Self::IntegerNumber => 'i',
88 Self::ScientificNotation => 'e',
89 Self::UnsignedDecimalNumber => 'u',
90 Self::FloatingPointNumber => 'f',
91 Self::FloatingPointNumberWithSignificantDigits => 'g',
92 Self::OctalNumber => 'o',
93 Self::String => 's',
94 Self::TrueOrFalse => 't',
95 Self::TypeOfArgument => 'T',
96 Self::PrimitiveValue => 'v',
97 Self::HexadecimalNumberLowercase => 'x',
98 Self::HexadecimalNumberUppercase => 'X',
99 Self::Json => 'j',
100 };
101 write!(f, "{specifier}")
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub enum ArgumentReference {
107 Indexed(usize),
108 Named(String),
109}
110
111impl std::fmt::Display for ArgumentReference {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 ArgumentReference::Indexed(index) => write!(f, "{index}$"),
115 ArgumentReference::Named(name) => write!(f, "({name})"),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum PaddingSpecifier {
122 Zero,
123 Char(char),
124}
125
126impl std::fmt::Display for PaddingSpecifier {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 match self {
129 PaddingSpecifier::Zero => write!(f, "0"),
130 PaddingSpecifier::Char(c) => write!(f, "'{c}"),
131 }
132 }
133}
134
135impl PaddingSpecifier {
136 pub fn char(self) -> char {
137 match self {
138 PaddingSpecifier::Zero => '0',
139 PaddingSpecifier::Char(c) => c,
140 }
141 }
142
143 pub const fn is_zero(self) -> bool {
144 match self {
145 PaddingSpecifier::Zero => true,
146 PaddingSpecifier::Char(_) => false,
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct Placeholder {
153 pub type_specifier: TypeSpecifier,
154 pub requested_argument: Option<ArgumentReference>,
155 pub plus_sign: bool,
156 pub padding_specifier: Option<PaddingSpecifier>,
157 pub left_align: bool,
158 pub width: Option<usize>,
159 pub precision: Option<usize>,
160}
161
162impl Placeholder {
163 pub fn padding_specifier_is_zero(&self) -> bool {
164 self.padding_specifier
165 .is_some_and(PaddingSpecifier::is_zero)
166 }
167
168 pub fn numeric_width(&self) -> Option<usize> {
170 self.width
171 .filter(|_| self.padding_specifier_is_zero() && self.type_specifier.is_numeric())
172 }
173}
174
175impl std::fmt::Display for Placeholder {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 write!(f, "%")?;
178 if let Some(argument) = &self.requested_argument {
179 write!(f, "{argument}")?;
180 }
181
182 if self.plus_sign {
183 write!(f, "+")?;
184 }
185
186 if let Some(padding_specifier) = &self.padding_specifier {
187 write!(f, "{padding_specifier}")?;
188 }
189
190 if self.left_align {
191 write!(f, "-")?;
192 }
193
194 if let Some(width) = self.width {
195 write!(f, "{width}")?;
196 }
197
198 if let Some(precision) = self.precision {
199 write!(f, ".{precision}")?;
200 }
201
202 write!(f, "{}", self.type_specifier)
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
207pub struct Message {
208 parts: Vec<Part>,
209}
210
211impl std::fmt::Display for Message {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 for part in &self.parts {
214 write!(f, "{part}")?;
215 }
216 Ok(())
217 }
218}
219
220impl FromIterator<Part> for Message {
221 fn from_iter<T: IntoIterator<Item = Part>>(iter: T) -> Self {
222 Self {
223 parts: iter.into_iter().collect(),
224 }
225 }
226}
227
228impl Message {
229 pub(crate) fn parts(&self) -> std::slice::Iter<'_, Part> {
230 self.parts.iter()
231 }
232
233 #[must_use]
235 pub fn from_literal(literal: String) -> Message {
236 Message {
237 parts: vec![Part::Text(literal)],
238 }
239 }
240}
241
242impl Serialize for Message {
243 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
244 let string = self.to_string();
245 serializer.serialize_str(&string)
246 }
247}
248
249impl<'de> Deserialize<'de> for Message {
250 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
251 let string = String::deserialize(deserializer)?;
252 string.parse().map_err(serde::de::Error::custom)
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
257pub(crate) enum Part {
258 Percent,
259 Text(String),
260 Placeholder(Placeholder),
261}
262
263impl From<Placeholder> for Part {
264 fn from(placeholder: Placeholder) -> Self {
265 Self::Placeholder(placeholder)
266 }
267}
268
269impl From<String> for Part {
270 fn from(text: String) -> Self {
271 Self::Text(text)
272 }
273}
274
275impl std::fmt::Display for Part {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 match self {
278 Part::Percent => write!(f, "%%"),
279 Part::Text(text) => write!(f, "{text}"),
280 Part::Placeholder(placeholder) => write!(f, "{placeholder}"),
281 }
282 }
283}