1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 use crate::*;
16
17 #[test]
std_fmt_examples_parse_correctly()18 fn std_fmt_examples_parse_correctly() {
19 // Examples from std::fmt.
20 assert_eq!(
21 FormatString::parse_core_fmt("Hello {:?}!"),
22 Ok(FormatString {
23 fragments: vec![
24 FormatFragment::Literal("Hello ".to_string()),
25 FormatFragment::Conversion(ConversionSpec {
26 argument: Argument::None,
27 fill: ' ',
28 alignment: Alignment::None,
29 flags: HashSet::new(),
30 min_field_width: MinFieldWidth::None,
31 precision: Precision::None,
32 length: None,
33 primitive: Primitive::Untyped,
34 style: Style::Debug,
35 }),
36 FormatFragment::Literal("!".to_string()),
37 ]
38 })
39 );
40
41 assert_eq!(
42 FormatString::parse_core_fmt("{:04}"),
43 Ok(FormatString {
44 fragments: vec![FormatFragment::Conversion(ConversionSpec {
45 argument: Argument::None,
46 fill: ' ',
47 alignment: Alignment::None,
48 flags: vec![Flag::LeadingZeros].into_iter().collect(),
49 min_field_width: MinFieldWidth::Fixed(4),
50 precision: Precision::None,
51 length: None,
52 primitive: Primitive::Untyped,
53 style: Style::None,
54 })]
55 })
56 );
57
58 assert_eq!(
59 FormatString::parse_core_fmt("{:#?}"),
60 Ok(FormatString {
61 fragments: vec![FormatFragment::Conversion(ConversionSpec {
62 argument: Argument::None,
63 fill: ' ',
64 alignment: Alignment::None,
65 flags: vec![Flag::AlternateSyntax].into_iter().collect(),
66 min_field_width: MinFieldWidth::None,
67 precision: Precision::None,
68 length: None,
69 primitive: Primitive::Untyped,
70 style: Style::Debug,
71 })]
72 })
73 );
74
75 assert_eq!(
76 FormatString::parse_core_fmt("{0}"),
77 Ok(FormatString {
78 fragments: vec![FormatFragment::Conversion(ConversionSpec {
79 argument: Argument::Positional(0),
80 fill: ' ',
81 alignment: Alignment::None,
82 flags: HashSet::new(),
83 min_field_width: MinFieldWidth::None,
84 precision: Precision::None,
85 length: None,
86 primitive: Primitive::Untyped,
87 style: Style::None,
88 })]
89 })
90 );
91
92 assert_eq!(
93 FormatString::parse_core_fmt("{1}"),
94 Ok(FormatString {
95 fragments: vec![FormatFragment::Conversion(ConversionSpec {
96 argument: Argument::Positional(1),
97 fill: ' ',
98 alignment: Alignment::None,
99 flags: HashSet::new(),
100 min_field_width: MinFieldWidth::None,
101 precision: Precision::None,
102 length: None,
103 primitive: Primitive::Untyped,
104 style: Style::None,
105 })]
106 })
107 );
108
109 assert_eq!(
110 FormatString::parse_core_fmt("{argument}"),
111 Ok(FormatString {
112 fragments: vec![FormatFragment::Conversion(ConversionSpec {
113 argument: Argument::Named("argument".to_string()),
114 fill: ' ',
115 alignment: Alignment::None,
116 flags: HashSet::new(),
117 min_field_width: MinFieldWidth::None,
118 precision: Precision::None,
119 length: None,
120 primitive: Primitive::Untyped,
121 style: Style::None,
122 })]
123 })
124 );
125
126 assert_eq!(
127 FormatString::parse_core_fmt("{:<5}"),
128 Ok(FormatString {
129 fragments: vec![FormatFragment::Conversion(ConversionSpec {
130 argument: Argument::None,
131 fill: ' ',
132 alignment: Alignment::Left,
133 flags: HashSet::new(),
134 min_field_width: MinFieldWidth::Fixed(5),
135 precision: Precision::None,
136 length: None,
137 primitive: Primitive::Untyped,
138 style: Style::None,
139 })]
140 })
141 );
142
143 assert_eq!(
144 FormatString::parse_core_fmt("{:-<5}"),
145 Ok(FormatString {
146 fragments: vec![FormatFragment::Conversion(ConversionSpec {
147 argument: Argument::None,
148 fill: '-',
149 alignment: Alignment::Left,
150 flags: HashSet::new(),
151 min_field_width: MinFieldWidth::Fixed(5),
152 precision: Precision::None,
153 length: None,
154 primitive: Primitive::Untyped,
155 style: Style::None,
156 })]
157 })
158 );
159
160 assert_eq!(
161 FormatString::parse_core_fmt("{:^5}"),
162 Ok(FormatString {
163 fragments: vec![FormatFragment::Conversion(ConversionSpec {
164 argument: Argument::None,
165 fill: ' ',
166 alignment: Alignment::Center,
167 flags: HashSet::new(),
168 min_field_width: MinFieldWidth::Fixed(5),
169 precision: Precision::None,
170 length: None,
171 primitive: Primitive::Untyped,
172 style: Style::None,
173 })]
174 })
175 );
176
177 assert_eq!(
178 FormatString::parse_core_fmt("{:>5}"),
179 Ok(FormatString {
180 fragments: vec![FormatFragment::Conversion(ConversionSpec {
181 argument: Argument::None,
182 fill: ' ',
183 alignment: Alignment::Right,
184 flags: HashSet::new(),
185 min_field_width: MinFieldWidth::Fixed(5),
186 precision: Precision::None,
187 length: None,
188 primitive: Primitive::Untyped,
189 style: Style::None,
190 })]
191 })
192 );
193
194 assert_eq!(
195 FormatString::parse_core_fmt("{:+}"),
196 Ok(FormatString {
197 fragments: vec![FormatFragment::Conversion(ConversionSpec {
198 argument: Argument::None,
199 fill: ' ',
200 alignment: Alignment::None,
201 flags: vec![Flag::ForceSign].into_iter().collect(),
202 min_field_width: MinFieldWidth::None,
203 precision: Precision::None,
204 length: None,
205 primitive: Primitive::Untyped,
206 style: Style::None,
207 })]
208 })
209 );
210
211 assert_eq!(
212 FormatString::parse_core_fmt("{:#x}"),
213 Ok(FormatString {
214 fragments: vec![FormatFragment::Conversion(ConversionSpec {
215 argument: Argument::None,
216 fill: ' ',
217 alignment: Alignment::None,
218 flags: vec![Flag::AlternateSyntax].into_iter().collect(),
219 min_field_width: MinFieldWidth::None,
220 precision: Precision::None,
221 length: None,
222 primitive: Primitive::Untyped,
223 style: Style::Hex,
224 })]
225 })
226 );
227
228 assert_eq!(
229 FormatString::parse_core_fmt("{:#010x}"),
230 Ok(FormatString {
231 fragments: vec![FormatFragment::Conversion(ConversionSpec {
232 argument: Argument::None,
233 fill: ' ',
234 alignment: Alignment::None,
235 flags: vec![Flag::AlternateSyntax, Flag::LeadingZeros]
236 .into_iter()
237 .collect(),
238 min_field_width: MinFieldWidth::Fixed(10),
239 precision: Precision::None,
240 length: None,
241 primitive: Primitive::Untyped,
242 style: Style::Hex,
243 })]
244 })
245 );
246
247 assert_eq!(
248 FormatString::parse_core_fmt("{1:.5}"),
249 Ok(FormatString {
250 fragments: vec![FormatFragment::Conversion(ConversionSpec {
251 argument: Argument::Positional(1),
252 fill: ' ',
253 alignment: Alignment::None,
254 flags: HashSet::new(),
255 min_field_width: MinFieldWidth::None,
256 precision: Precision::Fixed(5),
257 length: None,
258 primitive: Primitive::Untyped,
259 style: Style::None,
260 })]
261 })
262 );
263
264 assert_eq!(
265 FormatString::parse_core_fmt("{:.*}"),
266 Ok(FormatString {
267 fragments: vec![FormatFragment::Conversion(ConversionSpec {
268 argument: Argument::None,
269 fill: ' ',
270 alignment: Alignment::None,
271 flags: HashSet::new(),
272 min_field_width: MinFieldWidth::None,
273 precision: Precision::Variable,
274 length: None,
275 primitive: Primitive::Untyped,
276 style: Style::None,
277 })]
278 })
279 );
280
281 assert_eq!(
282 FormatString::parse_core_fmt("{2:.*}"),
283 Ok(FormatString {
284 fragments: vec![FormatFragment::Conversion(ConversionSpec {
285 argument: Argument::Positional(2),
286 fill: ' ',
287 alignment: Alignment::None,
288 flags: HashSet::new(),
289 min_field_width: MinFieldWidth::None,
290 precision: Precision::Variable,
291 length: None,
292 primitive: Primitive::Untyped,
293 style: Style::None,
294 })]
295 })
296 );
297
298 assert_eq!(
299 FormatString::parse_core_fmt("{name:.*}"),
300 Ok(FormatString {
301 fragments: vec![FormatFragment::Conversion(ConversionSpec {
302 argument: Argument::Named("name".to_string()),
303 fill: ' ',
304 alignment: Alignment::None,
305 flags: HashSet::new(),
306 min_field_width: MinFieldWidth::None,
307 precision: Precision::Variable,
308 length: None,
309 primitive: Primitive::Untyped,
310 style: Style::None,
311 })]
312 })
313 );
314
315 assert_eq!(
316 FormatString::parse_core_fmt("{name:>8.*}"),
317 Ok(FormatString {
318 fragments: vec![FormatFragment::Conversion(ConversionSpec {
319 argument: Argument::Named("name".to_string()),
320 fill: ' ',
321 alignment: Alignment::Right,
322 flags: HashSet::new(),
323 min_field_width: MinFieldWidth::Fixed(8),
324 precision: Precision::Variable,
325 length: None,
326 primitive: Primitive::Untyped,
327 style: Style::None,
328 })]
329 })
330 );
331 }
332
333 #[test]
conversions_allow_trailing_whitespace()334 fn conversions_allow_trailing_whitespace() {
335 assert_eq!(
336 FormatString::parse_core_fmt("{name:>8.* \t\r\n}"),
337 Ok(FormatString {
338 fragments: vec![FormatFragment::Conversion(ConversionSpec {
339 argument: Argument::Named("name".to_string()),
340 fill: ' ',
341 alignment: Alignment::Right,
342 flags: HashSet::new(),
343 min_field_width: MinFieldWidth::Fixed(8),
344 precision: Precision::Variable,
345 length: None,
346 primitive: Primitive::Untyped,
347 style: Style::None,
348 })]
349 })
350 );
351 }
352
353 #[test]
escaped_curly_braces_parse_correctly()354 fn escaped_curly_braces_parse_correctly() {
355 assert_eq!(
356 FormatString::parse_core_fmt("Hello {{{:?}}}!"),
357 Ok(FormatString {
358 fragments: vec![
359 FormatFragment::Literal("Hello {".to_string()),
360 FormatFragment::Conversion(ConversionSpec {
361 argument: Argument::None,
362 fill: ' ',
363 alignment: Alignment::None,
364 flags: HashSet::new(),
365 min_field_width: MinFieldWidth::None,
366 precision: Precision::None,
367 length: None,
368 primitive: Primitive::Untyped,
369 style: Style::Debug,
370 }),
371 FormatFragment::Literal("}!".to_string()),
372 ]
373 })
374 );
375 }
376