xref: /aosp_15_r20/external/pigweed/pw_format/rust/pw_format/tests/printf.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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]
test_parse()18 fn test_parse() {
19     assert_eq!(
20         FormatString::parse_printf("long double %+ 4.2Lf is %-03hd%%."),
21         Ok(FormatString {
22             fragments: vec![
23                 FormatFragment::Literal("long double ".to_string()),
24                 FormatFragment::Conversion(ConversionSpec {
25                     argument: Argument::None,
26                     fill: ' ',
27                     alignment: Alignment::None,
28                     flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
29                     min_field_width: MinFieldWidth::Fixed(4),
30                     precision: Precision::Fixed(2),
31                     length: Some(Length::LongDouble),
32                     primitive: Primitive::Float,
33                     style: Style::None,
34                 }),
35                 FormatFragment::Literal(" is ".to_string()),
36                 FormatFragment::Conversion(ConversionSpec {
37                     argument: Argument::None,
38                     fill: ' ',
39                     alignment: Alignment::Left,
40                     flags: [Flag::LeftJustify, Flag::LeadingZeros]
41                         .into_iter()
42                         .collect(),
43                     min_field_width: MinFieldWidth::Fixed(3),
44                     precision: Precision::None,
45                     length: Some(Length::Short),
46                     primitive: Primitive::Integer,
47                     style: Style::None,
48                 }),
49                 FormatFragment::Literal("%.".to_string()),
50             ]
51         })
52     );
53 }
54 
55 //
56 // The following test cases are from //pw_tokenizer/py/decode_test.py
57 //
58 
59 #[test]
test_percent()60 fn test_percent() {
61     assert_eq!(
62         FormatString::parse_printf("%%"),
63         Ok(FormatString {
64             fragments: vec![FormatFragment::Literal("%".to_string())],
65         }),
66     );
67 }
68 
69 #[test]
test_percent_with_leading_plus_fails()70 fn test_percent_with_leading_plus_fails() {
71     assert!(FormatString::parse_printf("%+%").is_err());
72 }
73 
74 #[test]
test_percent_with_leading_negative_fails()75 fn test_percent_with_leading_negative_fails() {
76     assert!(FormatString::parse_printf("%-%").is_err());
77 }
78 
79 #[test]
test_percent_with_leading_space_fails()80 fn test_percent_with_leading_space_fails() {
81     assert!(FormatString::parse_printf("% %").is_err());
82 }
83 
84 #[test]
test_percent_with_leading_hash_fails()85 fn test_percent_with_leading_hash_fails() {
86     assert!(FormatString::parse_printf("%#%").is_err());
87 }
88 
89 #[test]
test_percent_with_leading_zero_fails()90 fn test_percent_with_leading_zero_fails() {
91     assert!(FormatString::parse_printf("%0%").is_err());
92 }
93 
94 #[test]
test_percent_with_length_fails()95 fn test_percent_with_length_fails() {
96     assert!(FormatString::parse_printf("%hh%").is_err());
97     assert!(FormatString::parse_printf("%h%").is_err());
98     assert!(FormatString::parse_printf("%l%").is_err());
99     assert!(FormatString::parse_printf("%L%").is_err());
100     assert!(FormatString::parse_printf("%j%").is_err());
101     assert!(FormatString::parse_printf("%z%").is_err());
102     assert!(FormatString::parse_printf("%t%").is_err());
103 }
104 
105 #[test]
test_percent_with_width_fails()106 fn test_percent_with_width_fails() {
107     assert!(FormatString::parse_printf("%9%").is_err());
108 }
109 
110 #[test]
test_percent_with_multidigit_width_fails()111 fn test_percent_with_multidigit_width_fails() {
112     assert!(FormatString::parse_printf("%10%").is_err());
113 }
114 
115 #[test]
test_percent_with_star_width_fails()116 fn test_percent_with_star_width_fails() {
117     assert!(FormatString::parse_printf("%*%").is_err());
118 }
119 
120 #[test]
test_percent_with_precision_fails()121 fn test_percent_with_precision_fails() {
122     assert!(FormatString::parse_printf("%.5%").is_err());
123 }
124 
125 #[test]
test_percent_with_multidigit_precision_fails()126 fn test_percent_with_multidigit_precision_fails() {
127     assert!(FormatString::parse_printf("%.10%").is_err());
128 }
129 
130 #[test]
test_percent_with_star_precision_fails()131 fn test_percent_with_star_precision_fails() {
132     assert!(FormatString::parse_printf("%*%").is_err());
133 }
134 
135 const INTEGERS: &[(&str, Primitive, Style)] = &[
136     ("d", Primitive::Integer, Style::None),
137     ("i", Primitive::Integer, Style::None),
138     ("o", Primitive::Unsigned, Style::Octal),
139     ("u", Primitive::Unsigned, Style::None),
140     ("x", Primitive::Unsigned, Style::Hex),
141     ("X", Primitive::Unsigned, Style::UpperHex),
142     // While not strictly an integer pointers take the same args as integers.
143     ("p", Primitive::Pointer, Style::Pointer),
144 ];
145 
146 #[test]
test_integer()147 fn test_integer() {
148     for (format_char, primitive, style) in INTEGERS {
149         assert_eq!(
150             FormatString::parse_printf(&format!("%{format_char}")),
151             Ok(FormatString {
152                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
153                     argument: Argument::None,
154                     fill: ' ',
155                     alignment: Alignment::None,
156                     flags: HashSet::new(),
157                     min_field_width: MinFieldWidth::None,
158                     precision: Precision::None,
159                     length: None,
160                     primitive: *primitive,
161                     style: *style,
162                 })]
163             })
164         );
165     }
166 }
167 
168 #[test]
test_integer_with_minus()169 fn test_integer_with_minus() {
170     for (format_char, primitive, style) in INTEGERS {
171         assert_eq!(
172             FormatString::parse_printf(&format!("%-5{format_char}")),
173             Ok(FormatString {
174                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
175                     argument: Argument::None,
176                     fill: ' ',
177                     alignment: Alignment::Left,
178                     flags: [Flag::LeftJustify].into_iter().collect(),
179                     min_field_width: MinFieldWidth::Fixed(5),
180                     precision: Precision::None,
181                     length: None,
182                     primitive: *primitive,
183                     style: *style,
184                 })]
185             })
186         );
187     }
188 }
189 
190 #[test]
test_integer_with_plus()191 fn test_integer_with_plus() {
192     for (format_char, primitive, style) in INTEGERS {
193         assert_eq!(
194             FormatString::parse_printf(&format!("%+{format_char}")),
195             Ok(FormatString {
196                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
197                     argument: Argument::None,
198                     fill: ' ',
199                     alignment: Alignment::None,
200                     flags: [Flag::ForceSign].into_iter().collect(),
201                     min_field_width: MinFieldWidth::None,
202                     precision: Precision::None,
203                     length: None,
204                     primitive: *primitive,
205                     style: *style,
206                 })]
207             })
208         );
209     }
210 }
211 
212 #[test]
test_integer_with_blank_space()213 fn test_integer_with_blank_space() {
214     for (format_char, primitive, style) in INTEGERS {
215         assert_eq!(
216             FormatString::parse_printf(&format!("% {format_char}")),
217             Ok(FormatString {
218                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
219                     argument: Argument::None,
220                     fill: ' ',
221                     alignment: Alignment::None,
222                     flags: [Flag::SpaceSign].into_iter().collect(),
223                     min_field_width: MinFieldWidth::None,
224                     precision: Precision::None,
225                     length: None,
226                     primitive: *primitive,
227                     style: *style,
228                 })]
229             })
230         );
231     }
232 }
233 
234 #[test]
test_integer_with_plus_and_blank_space_ignores_blank_space()235 fn test_integer_with_plus_and_blank_space_ignores_blank_space() {
236     for (format_char, primitive, style) in INTEGERS {
237         assert_eq!(
238             FormatString::parse_printf(&format!("%+ {format_char}")),
239             Ok(FormatString {
240                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
241                     argument: Argument::None,
242                     fill: ' ',
243                     alignment: Alignment::None,
244                     flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
245                     min_field_width: MinFieldWidth::None,
246                     precision: Precision::None,
247                     length: None,
248                     primitive: *primitive,
249                     style: *style,
250                 })]
251             })
252         );
253 
254         assert_eq!(
255             FormatString::parse_printf(&format!("% +{format_char}")),
256             Ok(FormatString {
257                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
258                     argument: Argument::None,
259                     fill: ' ',
260                     alignment: Alignment::None,
261                     flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
262                     min_field_width: MinFieldWidth::None,
263                     precision: Precision::None,
264                     length: None,
265                     primitive: *primitive,
266                     style: *style,
267                 })]
268             })
269         );
270     }
271 }
272 
273 #[test]
test_integer_with_hash()274 fn test_integer_with_hash() {
275     for (format_char, primitive, style) in INTEGERS {
276         assert_eq!(
277             FormatString::parse_printf(&format!("%#{format_char}")),
278             Ok(FormatString {
279                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
280                     argument: Argument::None,
281                     fill: ' ',
282                     alignment: Alignment::None,
283                     flags: [Flag::AlternateSyntax].into_iter().collect(),
284                     min_field_width: MinFieldWidth::None,
285                     precision: Precision::None,
286                     length: None,
287                     primitive: *primitive,
288                     style: *style,
289                 })]
290             })
291         );
292     }
293 }
294 
295 #[test]
test_integer_with_zero()296 fn test_integer_with_zero() {
297     for (format_char, primitive, style) in INTEGERS {
298         assert_eq!(
299             FormatString::parse_printf(&format!("%0{format_char}")),
300             Ok(FormatString {
301                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
302                     argument: Argument::None,
303                     fill: ' ',
304                     alignment: Alignment::None,
305                     flags: [Flag::LeadingZeros].into_iter().collect(),
306                     min_field_width: MinFieldWidth::None,
307                     precision: Precision::None,
308                     length: None,
309                     primitive: *primitive,
310                     style: *style,
311                 })]
312             })
313         );
314     }
315 }
316 
317 #[test]
test_integer_with_length()318 fn test_integer_with_length() {
319     for (format_char, primitive, style) in INTEGERS {
320         assert_eq!(
321             FormatString::parse_printf(&format!("%hh{format_char}")),
322             Ok(FormatString {
323                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
324                     argument: Argument::None,
325                     fill: ' ',
326                     alignment: Alignment::None,
327                     flags: HashSet::new(),
328                     min_field_width: MinFieldWidth::None,
329                     precision: Precision::None,
330                     length: Some(Length::Char),
331                     primitive: *primitive,
332                     style: *style,
333                 })]
334             })
335         );
336 
337         assert_eq!(
338             FormatString::parse_printf(&format!("%h{format_char}")),
339             Ok(FormatString {
340                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
341                     argument: Argument::None,
342                     fill: ' ',
343                     alignment: Alignment::None,
344                     flags: HashSet::new(),
345                     min_field_width: MinFieldWidth::None,
346                     precision: Precision::None,
347                     length: Some(Length::Short),
348                     primitive: *primitive,
349                     style: *style,
350                 })]
351             })
352         );
353 
354         assert_eq!(
355             FormatString::parse_printf(&format!("%l{format_char}")),
356             Ok(FormatString {
357                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
358                     argument: Argument::None,
359                     fill: ' ',
360                     alignment: Alignment::None,
361                     flags: HashSet::new(),
362                     min_field_width: MinFieldWidth::None,
363                     precision: Precision::None,
364                     length: Some(Length::Long),
365                     primitive: *primitive,
366                     style: *style,
367                 })]
368             })
369         );
370 
371         assert_eq!(
372             FormatString::parse_printf(&format!("%ll{format_char}")),
373             Ok(FormatString {
374                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
375                     argument: Argument::None,
376                     fill: ' ',
377                     alignment: Alignment::None,
378                     flags: HashSet::new(),
379                     min_field_width: MinFieldWidth::None,
380                     precision: Precision::None,
381                     length: Some(Length::LongLong),
382                     primitive: *primitive,
383                     style: *style,
384                 })]
385             })
386         );
387 
388         assert_eq!(
389             FormatString::parse_printf(&format!("%j{format_char}")),
390             Ok(FormatString {
391                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
392                     argument: Argument::None,
393                     fill: ' ',
394                     alignment: Alignment::None,
395                     flags: HashSet::new(),
396                     min_field_width: MinFieldWidth::None,
397                     precision: Precision::None,
398                     length: Some(Length::IntMax),
399                     primitive: *primitive,
400                     style: *style,
401                 })]
402             })
403         );
404 
405         assert_eq!(
406             FormatString::parse_printf(&format!("%z{format_char}")),
407             Ok(FormatString {
408                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
409                     argument: Argument::None,
410                     fill: ' ',
411                     alignment: Alignment::None,
412                     flags: HashSet::new(),
413                     min_field_width: MinFieldWidth::None,
414                     precision: Precision::None,
415                     length: Some(Length::Size),
416                     primitive: *primitive,
417                     style: *style,
418                 })]
419             })
420         );
421 
422         assert_eq!(
423             FormatString::parse_printf(&format!("%t{format_char}")),
424             Ok(FormatString {
425                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
426                     argument: Argument::None,
427                     fill: ' ',
428                     alignment: Alignment::None,
429                     flags: HashSet::new(),
430                     min_field_width: MinFieldWidth::None,
431                     precision: Precision::None,
432                     length: Some(Length::PointerDiff),
433                     primitive: *primitive,
434                     style: *style,
435                 })]
436             })
437         );
438     }
439 }
440 
441 const FLOATS: &[(&str, Primitive, Style)] = &[
442     ("f", Primitive::Float, Style::None),
443     ("e", Primitive::Float, Style::Exponential),
444     ("E", Primitive::Float, Style::UpperExponential),
445 ];
446 
447 #[test]
test_float()448 fn test_float() {
449     for (format_char, primitive, style) in FLOATS {
450         assert_eq!(
451             FormatString::parse_printf(&format!("%{format_char}")),
452             Ok(FormatString {
453                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
454                     argument: Argument::None,
455                     fill: ' ',
456                     alignment: Alignment::None,
457                     flags: HashSet::new(),
458                     min_field_width: MinFieldWidth::None,
459                     precision: Precision::None,
460                     length: None,
461                     primitive: *primitive,
462                     style: *style,
463                 })]
464             })
465         );
466     }
467 }
468 
469 #[test]
test_float_with_minus()470 fn test_float_with_minus() {
471     for (format_char, primitive, style) in FLOATS {
472         assert_eq!(
473             FormatString::parse_printf(&format!("%-10{format_char}")),
474             Ok(FormatString {
475                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
476                     argument: Argument::None,
477                     fill: ' ',
478                     alignment: Alignment::Left,
479                     flags: [Flag::LeftJustify].into_iter().collect(),
480                     min_field_width: MinFieldWidth::Fixed(10),
481                     precision: Precision::None,
482                     length: None,
483                     primitive: *primitive,
484                     style: *style,
485                 })]
486             })
487         );
488     }
489 }
490 
491 #[test]
test_float_with_plus()492 fn test_float_with_plus() {
493     for (format_char, primitive, style) in FLOATS {
494         assert_eq!(
495             FormatString::parse_printf(&format!("%+{format_char}")),
496             Ok(FormatString {
497                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
498                     argument: Argument::None,
499                     fill: ' ',
500                     alignment: Alignment::None,
501                     flags: [Flag::ForceSign].into_iter().collect(),
502                     min_field_width: MinFieldWidth::None,
503                     precision: Precision::None,
504                     length: None,
505                     primitive: *primitive,
506                     style: *style,
507                 })]
508             })
509         );
510     }
511 }
512 
513 #[test]
test_float_with_blank_space()514 fn test_float_with_blank_space() {
515     for (format_char, primitive, style) in FLOATS {
516         assert_eq!(
517             FormatString::parse_printf(&format!("% {format_char}")),
518             Ok(FormatString {
519                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
520                     argument: Argument::None,
521                     fill: ' ',
522                     alignment: Alignment::None,
523                     flags: [Flag::SpaceSign].into_iter().collect(),
524                     min_field_width: MinFieldWidth::None,
525                     precision: Precision::None,
526                     length: None,
527                     primitive: *primitive,
528                     style: *style,
529                 })]
530             })
531         );
532     }
533 }
534 
535 #[test]
test_float_with_plus_and_blank_space_ignores_blank_space()536 fn test_float_with_plus_and_blank_space_ignores_blank_space() {
537     for (format_char, primitive, style) in FLOATS {
538         assert_eq!(
539             FormatString::parse_printf(&format!("%+ {format_char}")),
540             Ok(FormatString {
541                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
542                     argument: Argument::None,
543                     fill: ' ',
544                     alignment: Alignment::None,
545                     flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
546                     min_field_width: MinFieldWidth::None,
547                     precision: Precision::None,
548                     length: None,
549                     primitive: *primitive,
550                     style: *style,
551                 })]
552             })
553         );
554 
555         assert_eq!(
556             FormatString::parse_printf(&format!("% +{format_char}")),
557             Ok(FormatString {
558                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
559                     argument: Argument::None,
560                     fill: ' ',
561                     alignment: Alignment::None,
562                     flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
563                     min_field_width: MinFieldWidth::None,
564                     precision: Precision::None,
565                     length: None,
566                     primitive: *primitive,
567                     style: *style,
568                 })]
569             })
570         );
571     }
572 }
573 
574 #[test]
test_float_with_hash()575 fn test_float_with_hash() {
576     for (format_char, primitive, style) in FLOATS {
577         assert_eq!(
578             FormatString::parse_printf(&format!("%.0{format_char}")),
579             Ok(FormatString {
580                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
581                     argument: Argument::None,
582                     fill: ' ',
583                     alignment: Alignment::None,
584                     flags: [].into_iter().collect(),
585                     min_field_width: MinFieldWidth::None,
586                     precision: Precision::Fixed(0),
587                     length: None,
588                     primitive: *primitive,
589                     style: *style,
590                 })]
591             })
592         );
593 
594         assert_eq!(
595             FormatString::parse_printf(&format!("%#.0{format_char}")),
596             Ok(FormatString {
597                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
598                     argument: Argument::None,
599                     fill: ' ',
600                     alignment: Alignment::None,
601                     flags: [Flag::AlternateSyntax].into_iter().collect(),
602                     min_field_width: MinFieldWidth::None,
603                     precision: Precision::Fixed(0),
604                     length: None,
605                     primitive: *primitive,
606                     style: *style,
607                 })]
608             })
609         );
610     }
611 }
612 
613 #[test]
test_float_with_zero()614 fn test_float_with_zero() {
615     for (format_char, primitive, style) in FLOATS {
616         assert_eq!(
617             FormatString::parse_printf(&format!("%010{format_char}")),
618             Ok(FormatString {
619                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
620                     argument: Argument::None,
621                     fill: ' ',
622                     alignment: Alignment::None,
623                     flags: [Flag::LeadingZeros].into_iter().collect(),
624                     min_field_width: MinFieldWidth::Fixed(10),
625                     precision: Precision::None,
626                     length: None,
627                     primitive: *primitive,
628                     style: *style,
629                 })]
630             })
631         );
632     }
633 }
634 
635 #[test]
test_float_with_length()636 fn test_float_with_length() {
637     for (format_char, primitive, style) in FLOATS {
638         assert_eq!(
639             FormatString::parse_printf(&format!("%hh{format_char}")),
640             Ok(FormatString {
641                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
642                     argument: Argument::None,
643                     fill: ' ',
644                     alignment: Alignment::None,
645                     flags: HashSet::new(),
646                     min_field_width: MinFieldWidth::None,
647                     precision: Precision::None,
648                     length: Some(Length::Char),
649                     primitive: *primitive,
650                     style: *style,
651                 })]
652             })
653         );
654 
655         assert_eq!(
656             FormatString::parse_printf(&format!("%h{format_char}")),
657             Ok(FormatString {
658                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
659                     argument: Argument::None,
660                     fill: ' ',
661                     alignment: Alignment::None,
662                     flags: HashSet::new(),
663                     min_field_width: MinFieldWidth::None,
664                     precision: Precision::None,
665                     length: Some(Length::Short),
666                     primitive: *primitive,
667                     style: *style,
668                 })]
669             })
670         );
671 
672         assert_eq!(
673             FormatString::parse_printf(&format!("%l{format_char}")),
674             Ok(FormatString {
675                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
676                     argument: Argument::None,
677                     fill: ' ',
678                     alignment: Alignment::None,
679                     flags: HashSet::new(),
680                     min_field_width: MinFieldWidth::None,
681                     precision: Precision::None,
682                     length: Some(Length::Long),
683                     primitive: *primitive,
684                     style: *style,
685                 })]
686             })
687         );
688 
689         assert_eq!(
690             FormatString::parse_printf(&format!("%ll{format_char}")),
691             Ok(FormatString {
692                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
693                     argument: Argument::None,
694                     fill: ' ',
695                     alignment: Alignment::None,
696                     flags: HashSet::new(),
697                     min_field_width: MinFieldWidth::None,
698                     precision: Precision::None,
699                     length: Some(Length::LongLong),
700                     primitive: *primitive,
701                     style: *style,
702                 })]
703             })
704         );
705 
706         assert_eq!(
707             FormatString::parse_printf(&format!("%j{format_char}")),
708             Ok(FormatString {
709                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
710                     argument: Argument::None,
711                     fill: ' ',
712                     alignment: Alignment::None,
713                     flags: HashSet::new(),
714                     min_field_width: MinFieldWidth::None,
715                     precision: Precision::None,
716                     length: Some(Length::IntMax),
717                     primitive: *primitive,
718                     style: *style,
719                 })]
720             })
721         );
722 
723         assert_eq!(
724             FormatString::parse_printf(&format!("%z{format_char}")),
725             Ok(FormatString {
726                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
727                     argument: Argument::None,
728                     fill: ' ',
729                     alignment: Alignment::None,
730                     flags: HashSet::new(),
731                     min_field_width: MinFieldWidth::None,
732                     precision: Precision::None,
733                     length: Some(Length::Size),
734                     primitive: *primitive,
735                     style: *style,
736                 })]
737             })
738         );
739 
740         assert_eq!(
741             FormatString::parse_printf(&format!("%t{format_char}")),
742             Ok(FormatString {
743                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
744                     argument: Argument::None,
745                     fill: ' ',
746                     alignment: Alignment::None,
747                     flags: HashSet::new(),
748                     min_field_width: MinFieldWidth::None,
749                     precision: Precision::None,
750                     length: Some(Length::PointerDiff),
751                     primitive: *primitive,
752                     style: *style,
753                 })]
754             })
755         );
756 
757         assert_eq!(
758             FormatString::parse_printf(&format!("%L{format_char}")),
759             Ok(FormatString {
760                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
761                     argument: Argument::None,
762                     fill: ' ',
763                     alignment: Alignment::None,
764                     flags: HashSet::new(),
765                     min_field_width: MinFieldWidth::None,
766                     precision: Precision::None,
767                     length: Some(Length::LongDouble),
768                     primitive: *primitive,
769                     style: *style,
770                 })]
771             })
772         );
773     }
774 }
775 
776 #[test]
test_float_with_width()777 fn test_float_with_width() {
778     for (format_char, primitive, style) in FLOATS {
779         assert_eq!(
780             FormatString::parse_printf(&format!("%9{format_char}")),
781             Ok(FormatString {
782                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
783                     argument: Argument::None,
784                     fill: ' ',
785                     alignment: Alignment::None,
786                     flags: [].into_iter().collect(),
787                     min_field_width: MinFieldWidth::Fixed(9),
788                     precision: Precision::None,
789                     length: None,
790                     primitive: *primitive,
791                     style: *style,
792                 })]
793             })
794         );
795     }
796 }
797 
798 #[test]
test_float_with_multidigit_width()799 fn test_float_with_multidigit_width() {
800     for (format_char, primitive, style) in FLOATS {
801         assert_eq!(
802             FormatString::parse_printf(&format!("%10{format_char}")),
803             Ok(FormatString {
804                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
805                     argument: Argument::None,
806                     fill: ' ',
807                     alignment: Alignment::None,
808                     flags: [].into_iter().collect(),
809                     min_field_width: MinFieldWidth::Fixed(10),
810                     precision: Precision::None,
811                     length: None,
812                     primitive: *primitive,
813                     style: *style,
814                 })]
815             })
816         );
817     }
818 }
819 
820 #[test]
test_float_with_star_width()821 fn test_float_with_star_width() {
822     for (format_char, primitive, style) in FLOATS {
823         assert_eq!(
824             FormatString::parse_printf(&format!("%*{format_char}")),
825             Ok(FormatString {
826                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
827                     argument: Argument::None,
828                     fill: ' ',
829                     alignment: Alignment::None,
830                     flags: [].into_iter().collect(),
831                     min_field_width: MinFieldWidth::Variable,
832                     precision: Precision::None,
833                     length: None,
834                     primitive: *primitive,
835                     style: *style,
836                 })]
837             })
838         );
839     }
840 }
841 
842 #[test]
test_float_with_precision()843 fn test_float_with_precision() {
844     for (format_char, primitive, style) in FLOATS {
845         assert_eq!(
846             FormatString::parse_printf(&format!("%.4{format_char}")),
847             Ok(FormatString {
848                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
849                     argument: Argument::None,
850                     fill: ' ',
851                     alignment: Alignment::None,
852                     flags: [].into_iter().collect(),
853                     min_field_width: MinFieldWidth::None,
854                     precision: Precision::Fixed(4),
855                     length: None,
856                     primitive: *primitive,
857                     style: *style,
858                 })]
859             })
860         );
861     }
862 }
863 
864 #[test]
test_float_with_multidigit_precision()865 fn test_float_with_multidigit_precision() {
866     for (format_char, primitive, style) in FLOATS {
867         assert_eq!(
868             FormatString::parse_printf(&format!("%.10{format_char}")),
869             Ok(FormatString {
870                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
871                     argument: Argument::None,
872                     fill: ' ',
873                     alignment: Alignment::None,
874                     flags: [].into_iter().collect(),
875                     min_field_width: MinFieldWidth::None,
876                     precision: Precision::Fixed(10),
877                     length: None,
878                     primitive: *primitive,
879                     style: *style,
880                 })]
881             })
882         );
883     }
884 }
885 
886 #[test]
test_float_with_star_precision()887 fn test_float_with_star_precision() {
888     for (format_char, primitive, style) in FLOATS {
889         assert_eq!(
890             FormatString::parse_printf(&format!("%.*{format_char}")),
891             Ok(FormatString {
892                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
893                     argument: Argument::None,
894                     fill: ' ',
895                     alignment: Alignment::None,
896                     flags: [].into_iter().collect(),
897                     min_field_width: MinFieldWidth::None,
898                     precision: Precision::Variable,
899                     length: None,
900                     primitive: *primitive,
901                     style: *style,
902                 })]
903             })
904         );
905     }
906 }
907 
908 #[test]
test_float_with_star_width_and_star_precision()909 fn test_float_with_star_width_and_star_precision() {
910     for (format_char, primitive, style) in FLOATS {
911         assert_eq!(
912             FormatString::parse_printf(&format!("%*.*{format_char}")),
913             Ok(FormatString {
914                 fragments: vec![FormatFragment::Conversion(ConversionSpec {
915                     argument: Argument::None,
916                     fill: ' ',
917                     alignment: Alignment::None,
918                     flags: [].into_iter().collect(),
919                     min_field_width: MinFieldWidth::Variable,
920                     precision: Precision::Variable,
921                     length: None,
922                     primitive: *primitive,
923                     style: *style,
924                 })]
925             })
926         );
927     }
928 }
929 
930 #[test]
test_char()931 fn test_char() {
932     assert_eq!(
933         FormatString::parse_printf("%c"),
934         Ok(FormatString {
935             fragments: vec![FormatFragment::Conversion(ConversionSpec {
936                 argument: Argument::None,
937                 fill: ' ',
938                 alignment: Alignment::None,
939                 flags: [].into_iter().collect(),
940                 min_field_width: MinFieldWidth::None,
941                 precision: Precision::None,
942                 length: None,
943                 primitive: Primitive::Character,
944                 style: Style::None,
945             })]
946         })
947     );
948 }
949 
950 #[test]
test_char_with_minus()951 fn test_char_with_minus() {
952     assert_eq!(
953         FormatString::parse_printf("%-5c"),
954         Ok(FormatString {
955             fragments: vec![FormatFragment::Conversion(ConversionSpec {
956                 argument: Argument::None,
957                 fill: ' ',
958                 alignment: Alignment::Left,
959                 flags: [Flag::LeftJustify].into_iter().collect(),
960                 min_field_width: MinFieldWidth::Fixed(5),
961                 precision: Precision::None,
962                 length: None,
963                 primitive: Primitive::Character,
964                 style: Style::None,
965             })]
966         })
967     );
968 }
969 
970 #[test]
test_char_with_plus()971 fn test_char_with_plus() {
972     // TODO: b/281750433 - This test should fail.
973     assert!(FormatString::parse_printf("%+c").is_ok());
974 }
975 
976 #[test]
test_char_with_blank_space()977 fn test_char_with_blank_space() {
978     // TODO: b/281750433 - This test should fail.
979     assert!(FormatString::parse_printf("% c").is_ok());
980 }
981 
982 #[test]
test_char_with_hash()983 fn test_char_with_hash() {
984     // TODO: b/281750433 - This test should fail.
985     assert!(FormatString::parse_printf("%#c").is_ok());
986 }
987 
988 #[test]
test_char_with_zero()989 fn test_char_with_zero() {
990     // TODO: b/281750433 - This test should fail.
991     assert!(FormatString::parse_printf("%0c").is_ok());
992 }
993 
994 #[test]
test_char_with_length()995 fn test_char_with_length() {
996     // Length modifiers are ignored by %c but are still returned by the
997     // parser.
998     assert_eq!(
999         FormatString::parse_printf("%hhc"),
1000         Ok(FormatString {
1001             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1002                 argument: Argument::None,
1003                 fill: ' ',
1004                 alignment: Alignment::None,
1005                 flags: HashSet::new(),
1006                 min_field_width: MinFieldWidth::None,
1007                 precision: Precision::None,
1008                 length: Some(Length::Char),
1009                 primitive: Primitive::Character,
1010                 style: Style::None,
1011             })]
1012         })
1013     );
1014 
1015     assert_eq!(
1016         FormatString::parse_printf("%hc"),
1017         Ok(FormatString {
1018             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1019                 argument: Argument::None,
1020                 fill: ' ',
1021                 alignment: Alignment::None,
1022                 flags: HashSet::new(),
1023                 min_field_width: MinFieldWidth::None,
1024                 precision: Precision::None,
1025                 length: Some(Length::Short),
1026                 primitive: Primitive::Character,
1027                 style: Style::None,
1028             })]
1029         })
1030     );
1031 
1032     assert_eq!(
1033         FormatString::parse_printf("%lc"),
1034         Ok(FormatString {
1035             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1036                 argument: Argument::None,
1037                 fill: ' ',
1038                 alignment: Alignment::None,
1039                 flags: HashSet::new(),
1040                 min_field_width: MinFieldWidth::None,
1041                 precision: Precision::None,
1042                 length: Some(Length::Long),
1043                 primitive: Primitive::Character,
1044                 style: Style::None,
1045             })]
1046         })
1047     );
1048 
1049     assert_eq!(
1050         FormatString::parse_printf("%llc"),
1051         Ok(FormatString {
1052             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1053                 argument: Argument::None,
1054                 fill: ' ',
1055                 alignment: Alignment::None,
1056                 flags: HashSet::new(),
1057                 min_field_width: MinFieldWidth::None,
1058                 precision: Precision::None,
1059                 length: Some(Length::LongLong),
1060                 primitive: Primitive::Character,
1061                 style: Style::None,
1062             })]
1063         })
1064     );
1065 
1066     assert_eq!(
1067         FormatString::parse_printf("%jc"),
1068         Ok(FormatString {
1069             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1070                 argument: Argument::None,
1071                 fill: ' ',
1072                 alignment: Alignment::None,
1073                 flags: HashSet::new(),
1074                 min_field_width: MinFieldWidth::None,
1075                 precision: Precision::None,
1076                 length: Some(Length::IntMax),
1077                 primitive: Primitive::Character,
1078                 style: Style::None,
1079             })]
1080         })
1081     );
1082 
1083     assert_eq!(
1084         FormatString::parse_printf("%zc"),
1085         Ok(FormatString {
1086             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1087                 argument: Argument::None,
1088                 fill: ' ',
1089                 alignment: Alignment::None,
1090                 flags: HashSet::new(),
1091                 min_field_width: MinFieldWidth::None,
1092                 precision: Precision::None,
1093                 length: Some(Length::Size),
1094                 primitive: Primitive::Character,
1095                 style: Style::None,
1096             })]
1097         })
1098     );
1099 
1100     assert_eq!(
1101         FormatString::parse_printf("%tc"),
1102         Ok(FormatString {
1103             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1104                 argument: Argument::None,
1105                 fill: ' ',
1106                 alignment: Alignment::None,
1107                 flags: HashSet::new(),
1108                 min_field_width: MinFieldWidth::None,
1109                 precision: Precision::None,
1110                 length: Some(Length::PointerDiff),
1111                 primitive: Primitive::Character,
1112                 style: Style::None,
1113             })]
1114         })
1115     );
1116 
1117     assert_eq!(
1118         FormatString::parse_printf("%Lc"),
1119         Ok(FormatString {
1120             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1121                 argument: Argument::None,
1122                 fill: ' ',
1123                 alignment: Alignment::None,
1124                 flags: HashSet::new(),
1125                 min_field_width: MinFieldWidth::None,
1126                 precision: Precision::None,
1127                 length: Some(Length::LongDouble),
1128                 primitive: Primitive::Character,
1129                 style: Style::None,
1130             })]
1131         })
1132     );
1133 }
1134 
1135 #[test]
test_char_with_width()1136 fn test_char_with_width() {
1137     assert_eq!(
1138         FormatString::parse_printf("%5c"),
1139         Ok(FormatString {
1140             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1141                 argument: Argument::None,
1142                 fill: ' ',
1143                 alignment: Alignment::None,
1144                 flags: [].into_iter().collect(),
1145                 min_field_width: MinFieldWidth::Fixed(5),
1146                 precision: Precision::None,
1147                 length: None,
1148                 primitive: Primitive::Character,
1149                 style: Style::None,
1150             })]
1151         })
1152     );
1153 }
1154 
1155 #[test]
test_char_with_multidigit_width()1156 fn test_char_with_multidigit_width() {
1157     assert_eq!(
1158         FormatString::parse_printf("%10c"),
1159         Ok(FormatString {
1160             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1161                 argument: Argument::None,
1162                 fill: ' ',
1163                 alignment: Alignment::None,
1164                 flags: [].into_iter().collect(),
1165                 min_field_width: MinFieldWidth::Fixed(10),
1166                 precision: Precision::None,
1167                 length: None,
1168                 primitive: Primitive::Character,
1169                 style: Style::None,
1170             })]
1171         })
1172     );
1173 }
1174 
1175 #[test]
test_char_with_star_width()1176 fn test_char_with_star_width() {
1177     assert_eq!(
1178         FormatString::parse_printf("%*c"),
1179         Ok(FormatString {
1180             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1181                 argument: Argument::None,
1182                 fill: ' ',
1183                 alignment: Alignment::None,
1184                 flags: [].into_iter().collect(),
1185                 min_field_width: MinFieldWidth::Variable,
1186                 precision: Precision::None,
1187                 length: None,
1188                 primitive: Primitive::Character,
1189                 style: Style::None,
1190             })]
1191         })
1192     );
1193 }
1194 
1195 #[test]
test_char_with_precision()1196 fn test_char_with_precision() {
1197     // TODO: b/281750433 - This test should fail.
1198     assert!(FormatString::parse_printf("%.4c").is_ok());
1199 }
1200 
1201 #[test]
test_long_char_with_hash()1202 fn test_long_char_with_hash() {
1203     // TODO: b/281750433 - This test should fail.
1204     assert!(FormatString::parse_printf("%#lc").is_ok());
1205 }
1206 
1207 #[test]
test_long_char_with_zero()1208 fn test_long_char_with_zero() {
1209     // TODO: b/281750433 - This test should fail.
1210     assert!(FormatString::parse_printf("%0lc").is_ok());
1211 }
1212 
1213 #[test]
test_string()1214 fn test_string() {
1215     assert_eq!(
1216         FormatString::parse_printf("%s"),
1217         Ok(FormatString {
1218             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1219                 argument: Argument::None,
1220                 fill: ' ',
1221                 alignment: Alignment::None,
1222                 flags: [].into_iter().collect(),
1223                 min_field_width: MinFieldWidth::None,
1224                 precision: Precision::None,
1225                 length: None,
1226                 primitive: Primitive::String,
1227                 style: Style::None,
1228             })]
1229         })
1230     );
1231 }
1232 
1233 #[test]
test_string_with_minus()1234 fn test_string_with_minus() {
1235     assert_eq!(
1236         FormatString::parse_printf("%-6s"),
1237         Ok(FormatString {
1238             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1239                 argument: Argument::None,
1240                 fill: ' ',
1241                 alignment: Alignment::Left,
1242                 flags: [Flag::LeftJustify].into_iter().collect(),
1243                 min_field_width: MinFieldWidth::Fixed(6),
1244                 precision: Precision::None,
1245                 length: None,
1246                 primitive: Primitive::String,
1247                 style: Style::None,
1248             })]
1249         })
1250     );
1251 }
1252 
1253 #[test]
test_string_with_plus()1254 fn test_string_with_plus() {
1255     // TODO: b/281750433 - This test should fail.
1256     assert!(FormatString::parse_printf("%+s").is_ok());
1257 }
1258 
1259 #[test]
test_string_with_blank_space()1260 fn test_string_with_blank_space() {
1261     // TODO: b/281750433 - This test should fail.
1262     assert!(FormatString::parse_printf("% s").is_ok());
1263 }
1264 
1265 #[test]
test_string_with_hash()1266 fn test_string_with_hash() {
1267     // TODO: b/281750433 - This test should fail.
1268     assert!(FormatString::parse_printf("%#s").is_ok());
1269 }
1270 
1271 #[test]
test_string_with_zero()1272 fn test_string_with_zero() {
1273     // TODO: b/281750433 - This test should fail.
1274     assert!(FormatString::parse_printf("%0s").is_ok());
1275 }
1276 
1277 #[test]
test_string_with_length()1278 fn test_string_with_length() {
1279     // Length modifiers are ignored by %s but are still returned by the
1280     // parser.
1281     assert_eq!(
1282         FormatString::parse_printf("%hhs"),
1283         Ok(FormatString {
1284             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1285                 argument: Argument::None,
1286                 fill: ' ',
1287                 alignment: Alignment::None,
1288                 flags: HashSet::new(),
1289                 min_field_width: MinFieldWidth::None,
1290                 precision: Precision::None,
1291                 length: Some(Length::Char),
1292                 primitive: Primitive::String,
1293                 style: Style::None,
1294             })]
1295         })
1296     );
1297 
1298     assert_eq!(
1299         FormatString::parse_printf("%hs"),
1300         Ok(FormatString {
1301             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1302                 argument: Argument::None,
1303                 fill: ' ',
1304                 alignment: Alignment::None,
1305                 flags: HashSet::new(),
1306                 min_field_width: MinFieldWidth::None,
1307                 precision: Precision::None,
1308                 length: Some(Length::Short),
1309                 primitive: Primitive::String,
1310                 style: Style::None,
1311             })]
1312         })
1313     );
1314 
1315     assert_eq!(
1316         FormatString::parse_printf("%ls"),
1317         Ok(FormatString {
1318             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1319                 argument: Argument::None,
1320                 fill: ' ',
1321                 alignment: Alignment::None,
1322                 flags: HashSet::new(),
1323                 min_field_width: MinFieldWidth::None,
1324                 precision: Precision::None,
1325                 length: Some(Length::Long),
1326                 primitive: Primitive::String,
1327                 style: Style::None,
1328             })]
1329         })
1330     );
1331 
1332     assert_eq!(
1333         FormatString::parse_printf("%lls"),
1334         Ok(FormatString {
1335             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1336                 argument: Argument::None,
1337                 fill: ' ',
1338                 alignment: Alignment::None,
1339                 flags: HashSet::new(),
1340                 min_field_width: MinFieldWidth::None,
1341                 precision: Precision::None,
1342                 length: Some(Length::LongLong),
1343                 primitive: Primitive::String,
1344                 style: Style::None,
1345             })]
1346         })
1347     );
1348 
1349     assert_eq!(
1350         FormatString::parse_printf("%js"),
1351         Ok(FormatString {
1352             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1353                 argument: Argument::None,
1354                 fill: ' ',
1355                 alignment: Alignment::None,
1356                 flags: HashSet::new(),
1357                 min_field_width: MinFieldWidth::None,
1358                 precision: Precision::None,
1359                 length: Some(Length::IntMax),
1360                 primitive: Primitive::String,
1361                 style: Style::None,
1362             })]
1363         })
1364     );
1365 
1366     assert_eq!(
1367         FormatString::parse_printf("%zs"),
1368         Ok(FormatString {
1369             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1370                 argument: Argument::None,
1371                 fill: ' ',
1372                 alignment: Alignment::None,
1373                 flags: HashSet::new(),
1374                 min_field_width: MinFieldWidth::None,
1375                 precision: Precision::None,
1376                 length: Some(Length::Size),
1377                 primitive: Primitive::String,
1378                 style: Style::None,
1379             })]
1380         })
1381     );
1382 
1383     assert_eq!(
1384         FormatString::parse_printf("%ts"),
1385         Ok(FormatString {
1386             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1387                 argument: Argument::None,
1388                 fill: ' ',
1389                 alignment: Alignment::None,
1390                 flags: HashSet::new(),
1391                 min_field_width: MinFieldWidth::None,
1392                 precision: Precision::None,
1393                 length: Some(Length::PointerDiff),
1394                 primitive: Primitive::String,
1395                 style: Style::None,
1396             })]
1397         })
1398     );
1399 
1400     assert_eq!(
1401         FormatString::parse_printf("%Ls"),
1402         Ok(FormatString {
1403             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1404                 argument: Argument::None,
1405                 fill: ' ',
1406                 alignment: Alignment::None,
1407                 flags: HashSet::new(),
1408                 min_field_width: MinFieldWidth::None,
1409                 precision: Precision::None,
1410                 length: Some(Length::LongDouble),
1411                 primitive: Primitive::String,
1412                 style: Style::None,
1413             })]
1414         })
1415     );
1416 }
1417 
1418 #[test]
test_string_with_width()1419 fn test_string_with_width() {
1420     assert_eq!(
1421         FormatString::parse_printf("%6s"),
1422         Ok(FormatString {
1423             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1424                 argument: Argument::None,
1425                 fill: ' ',
1426                 alignment: Alignment::None,
1427                 flags: [].into_iter().collect(),
1428                 min_field_width: MinFieldWidth::Fixed(6),
1429                 precision: Precision::None,
1430                 length: None,
1431                 primitive: Primitive::String,
1432                 style: Style::None,
1433             })]
1434         })
1435     );
1436 }
1437 
1438 #[test]
test_string_with_multidigit_width()1439 fn test_string_with_multidigit_width() {
1440     assert_eq!(
1441         FormatString::parse_printf("%10s"),
1442         Ok(FormatString {
1443             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1444                 argument: Argument::None,
1445                 fill: ' ',
1446                 alignment: Alignment::None,
1447                 flags: [].into_iter().collect(),
1448                 min_field_width: MinFieldWidth::Fixed(10),
1449                 precision: Precision::None,
1450                 length: None,
1451                 primitive: Primitive::String,
1452                 style: Style::None,
1453             })]
1454         })
1455     );
1456 }
1457 
1458 #[test]
test_string_with_star_width()1459 fn test_string_with_star_width() {
1460     assert_eq!(
1461         FormatString::parse_printf("%*s"),
1462         Ok(FormatString {
1463             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1464                 argument: Argument::None,
1465                 fill: ' ',
1466                 alignment: Alignment::None,
1467                 flags: [].into_iter().collect(),
1468                 min_field_width: MinFieldWidth::Variable,
1469                 precision: Precision::None,
1470                 length: None,
1471                 primitive: Primitive::String,
1472                 style: Style::None,
1473             })]
1474         })
1475     );
1476 }
1477 
1478 #[test]
test_string_with_star_precision()1479 fn test_string_with_star_precision() {
1480     assert_eq!(
1481         FormatString::parse_printf("%.3s"),
1482         Ok(FormatString {
1483             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1484                 argument: Argument::None,
1485                 fill: ' ',
1486                 alignment: Alignment::None,
1487                 flags: [].into_iter().collect(),
1488                 min_field_width: MinFieldWidth::None,
1489                 precision: Precision::Fixed(3),
1490                 length: None,
1491                 primitive: Primitive::String,
1492                 style: Style::None,
1493             })]
1494         })
1495     );
1496 }
1497 
1498 #[test]
test_string_with_multidigit_precision()1499 fn test_string_with_multidigit_precision() {
1500     assert_eq!(
1501         FormatString::parse_printf("%.10s"),
1502         Ok(FormatString {
1503             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1504                 argument: Argument::None,
1505                 fill: ' ',
1506                 alignment: Alignment::None,
1507                 flags: [].into_iter().collect(),
1508                 min_field_width: MinFieldWidth::None,
1509                 precision: Precision::Fixed(10),
1510                 length: None,
1511                 primitive: Primitive::String,
1512                 style: Style::None,
1513             })]
1514         })
1515     );
1516 }
1517 
1518 #[test]
test_string_with_width_and_precision()1519 fn test_string_with_width_and_precision() {
1520     assert_eq!(
1521         FormatString::parse_printf("%10.3s"),
1522         Ok(FormatString {
1523             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1524                 argument: Argument::None,
1525                 fill: ' ',
1526                 alignment: Alignment::None,
1527                 flags: [].into_iter().collect(),
1528                 min_field_width: MinFieldWidth::Fixed(10),
1529                 precision: Precision::Fixed(3),
1530                 length: None,
1531                 primitive: Primitive::String,
1532                 style: Style::None,
1533             })]
1534         })
1535     );
1536 }
1537 
1538 #[test]
test_string_with_star_width_and_star_precision()1539 fn test_string_with_star_width_and_star_precision() {
1540     assert_eq!(
1541         FormatString::parse_printf("%*.*s"),
1542         Ok(FormatString {
1543             fragments: vec![FormatFragment::Conversion(ConversionSpec {
1544                 argument: Argument::None,
1545                 fill: ' ',
1546                 alignment: Alignment::None,
1547                 flags: [].into_iter().collect(),
1548                 min_field_width: MinFieldWidth::Variable,
1549                 precision: Precision::Variable,
1550                 length: None,
1551                 primitive: Primitive::String,
1552                 style: Style::None,
1553             })]
1554         })
1555     );
1556 }
1557 
1558 #[test]
test_long_string_with_hash()1559 fn test_long_string_with_hash() {
1560     // TODO: b/281750433 - This test should fail.
1561     assert!(FormatString::parse_printf("%#ls").is_ok());
1562 }
1563 
1564 #[test]
test_long_string_with_zero()1565 fn test_long_string_with_zero() {
1566     // TODO: b/281750433 - This test should fail.
1567     assert!(FormatString::parse_printf("%0ls").is_ok());
1568 }
1569