derive_more_impl/fmt/
parsing.rs

1//! Parsing of [Rust `fmt` syntax][0].
2//!
3//! [0]: std::fmt#syntax
4
5use std::iter;
6
7use unicode_xid::UnicodeXID as XID;
8
9/// Output of the [`format_string`] parser.
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub(crate) struct FormatString<'a> {
12    pub(crate) formats: Vec<Format<'a>>,
13}
14
15/// Output of the [`format`] parser.
16///
17/// [`format`]: fn@format
18#[derive(Debug, Clone, Copy, Eq, PartialEq)]
19pub(crate) struct Format<'a> {
20    pub(crate) arg: Option<Argument<'a>>,
21    pub(crate) spec: Option<FormatSpec<'a>>,
22}
23
24/// Output of the [`format_spec`] parser.
25#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub(crate) struct FormatSpec<'a> {
27    /// Parsed `[[fill]`[`align`]`]`.
28    pub(crate) align: Option<(Option<Fill>, Align)>,
29
30    /// Parsed `[`[`sign`]`]`.
31    pub(crate) sign: Option<Sign>,
32
33    /// Parsed `['#']` (alternation).
34    pub(crate) alternate: Option<Alternate>,
35
36    /// Parsed `['0']` (padding with zeros).
37    pub(crate) zero_padding: Option<ZeroPadding>,
38
39    /// Parsed `[width]`.
40    pub(crate) width: Option<Width<'a>>,
41
42    /// Parsed `['.' `[`precision`]`]`.
43    pub(crate) precision: Option<Precision<'a>>,
44
45    /// Parsed [`type`].
46    ///
47    /// [`type`]: type_
48    pub(crate) ty: Type,
49}
50
51/// Output of the [`argument`] parser.
52#[derive(Clone, Copy, Debug, Eq, PartialEq)]
53pub(crate) enum Argument<'a> {
54    Integer(usize),
55    Identifier(&'a str),
56}
57
58/// Output of the [`align`] parser.
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60pub(crate) enum Align {
61    Left,
62    Center,
63    Right,
64}
65
66/// Output of the [`sign`] parser.
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub(crate) enum Sign {
69    Plus,
70    Minus,
71}
72
73/// Type for the [`FormatSpec::alternate`].
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub(crate) struct Alternate;
76
77/// Type for the [`FormatSpec::zero_padding`].
78#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub(crate) struct ZeroPadding;
80
81/// Output of the [`precision`] parser.
82#[derive(Clone, Copy, Debug, Eq, PartialEq)]
83pub(crate) enum Precision<'a> {
84    Count(Count<'a>),
85    Star,
86}
87
88/// Output of the [`count`] parser.
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub(crate) enum Count<'a> {
91    Integer(usize),
92    Parameter(Parameter<'a>),
93}
94
95/// Output of the [`type_`] parser. See [formatting traits][0] for more info.
96///
97/// [0]: std::fmt#formatting-traits
98#[derive(Clone, Copy, Debug, Eq, PartialEq)]
99pub(crate) enum Type {
100    Display,
101    Debug,
102    LowerDebug,
103    UpperDebug,
104    Octal,
105    LowerHex,
106    UpperHex,
107    Pointer,
108    Binary,
109    LowerExp,
110    UpperExp,
111}
112
113impl Type {
114    /// Returns trait name of this [`Type`].
115    pub(crate) fn trait_name(&self) -> &'static str {
116        match self {
117            Self::Display => "Display",
118            Self::Debug | Self::LowerDebug | Self::UpperDebug => "Debug",
119            Self::Octal => "Octal",
120            Self::LowerHex => "LowerHex",
121            Self::UpperHex => "UpperHex",
122            Self::Pointer => "Pointer",
123            Self::Binary => "Binary",
124            Self::LowerExp => "LowerExp",
125            Self::UpperExp => "UpperExp",
126        }
127    }
128
129    /// Indicates whether this [`Type`] represents a trivial trait call without any modifications.
130    pub(crate) fn is_trivial(&self) -> bool {
131        match self {
132            Self::Display
133            | Self::Debug
134            | Self::Octal
135            | Self::LowerHex
136            | Self::UpperHex
137            | Self::Pointer
138            | Self::Binary
139            | Self::LowerExp
140            | Self::UpperExp => true,
141            Self::LowerDebug | Self::UpperDebug => false,
142        }
143    }
144}
145
146/// Type alias for the `fill` in the [`FormatSpec::align`].
147type Fill = char;
148
149/// Type alias for the [`FormatSpec::width`].
150type Width<'a> = Count<'a>;
151
152/// Output of the [`maybe_format`] parser.
153type MaybeFormat<'a> = Option<Format<'a>>;
154
155/// Output of the [`identifier`] parser.
156type Identifier<'a> = &'a str;
157
158/// Output of the [`parameter`] parser.
159type Parameter<'a> = Argument<'a>;
160
161/// [`str`] left to parse.
162///
163/// [`str`]: prim@str
164type LeftToParse<'a> = &'a str;
165
166/// Parses a `format_string` as defined in the [grammar spec][0].
167///
168/// # Grammar
169///
170/// [`format_string`]` := `[`text`]` [`[`maybe_format text`]`] *`
171///
172/// # Example
173///
174/// ```text
175/// Hello
176/// Hello, {}!
177/// {:?}
178/// Hello {people}!
179/// {} {}
180/// {:04}
181/// {par:-^#.0$?}
182/// ```
183///
184/// # Return value
185///
186/// - [`Some`] in case of successful parse.
187/// - [`None`] otherwise (not all characters are consumed by underlying
188///   parsers).
189///
190/// [0]: std::fmt#syntax
191pub(crate) fn format_string(input: &str) -> Option<FormatString<'_>> {
192    let (mut input, _) = optional_result(text)(input);
193
194    let formats = iter::repeat(())
195        .scan(&mut input, |input, _| {
196            let (curr, format) =
197                alt(&mut [&mut maybe_format, &mut map(text, |(i, _)| (i, None))])(
198                    input,
199                )?;
200            **input = curr;
201            Some(format)
202        })
203        .flatten()
204        .collect();
205
206    // Should consume all tokens for a successful parse.
207    input.is_empty().then_some(FormatString { formats })
208}
209
210/// Parses a `maybe_format` as defined in the [grammar spec][0].
211///
212/// # Grammar
213///
214/// [`maybe_format`]` := '{' '{' | '}' '}' | `[`format`]
215///
216/// # Example
217///
218/// ```text
219/// {{
220/// }}
221/// {:04}
222/// {:#?}
223/// {par:-^#.0$?}
224/// ```
225///
226/// [`format`]: fn@format
227/// [0]: std::fmt#syntax
228fn maybe_format(input: &str) -> Option<(LeftToParse<'_>, MaybeFormat<'_>)> {
229    alt(&mut [
230        &mut map(str("{{"), |i| (i, None)),
231        &mut map(str("}}"), |i| (i, None)),
232        &mut map(format, |(i, format)| (i, Some(format))),
233    ])(input)
234}
235
236/// Parses a `format` as defined in the [grammar spec][0].
237///
238/// # Grammar
239///
240/// [`format`]` := '{' [`[`argument`]`] [':' `[`format_spec`]`] '}'`
241///
242/// # Example
243///
244/// ```text
245/// {par}
246/// {:#?}
247/// {par:-^#.0$?}
248/// ```
249///
250/// [`format`]: fn@format
251/// [0]: std::fmt#syntax
252pub(crate) fn format(input: &str) -> Option<(LeftToParse<'_>, Format<'_>)> {
253    let input = char('{')(input)?;
254
255    let (input, arg) = optional_result(argument)(input);
256
257    let (input, spec) = map_or_else(
258        char(':'),
259        |i| Some((i, None)),
260        map(format_spec, |(i, s)| (i, Some(s))),
261    )(input)?;
262
263    let input = char('}')(input)?;
264
265    Some((input, Format { arg, spec }))
266}
267
268/// Parses an `argument` as defined in the [grammar spec][0].
269///
270/// # Grammar
271///
272/// [`argument`]` := `[`integer`]` | `[`identifier`]
273///
274/// # Example
275///
276/// ```text
277/// 0
278/// ident
279/// Минск
280/// ```
281///
282/// [0]: std::fmt#syntax
283fn argument(input: &str) -> Option<(LeftToParse<'_>, Argument)> {
284    alt(&mut [
285        &mut map(identifier, |(i, ident)| (i, Argument::Identifier(ident))),
286        &mut map(integer, |(i, int)| (i, Argument::Integer(int))),
287    ])(input)
288}
289
290/// Parses a `format_spec` as defined in the [grammar spec][0].
291///
292/// # Grammar
293///
294/// [`format_spec`]` := [[fill]`[`align`]`][`[`sign`]`]['#']['0'][width]`
295///                     `['.' `[`precision`]`]`[`type`]
296///
297/// # Example
298///
299/// ```text
300/// ^
301/// <^
302/// ->+#0width$.precision$x?
303/// ```
304///
305/// [`type`]: type_
306/// [0]: std::fmt#syntax
307fn format_spec(input: &str) -> Option<(LeftToParse<'_>, FormatSpec<'_>)> {
308    let (input, align) = optional_result(alt(&mut [
309        &mut and_then(take_any_char, |(i, fill)| {
310            map(align, |(i, align)| (i, (Some(fill), align)))(i)
311        }),
312        &mut map(align, |(i, align)| (i, (None, align))),
313    ]))(input);
314
315    let (input, sign) = optional_result(sign)(input);
316
317    let (input, alternate) = optional_result(map(char('#'), |i| (i, Alternate)))(input);
318
319    let (input, zero_padding) = optional_result(map(
320        try_seq(&mut [
321            &mut char('0'),
322            &mut lookahead(check_char(|c| !matches!(c, '$'))),
323        ]),
324        |i| (i, ZeroPadding),
325    ))(input);
326
327    let (input, width) = optional_result(count)(input);
328
329    let (input, precision) = map_or_else(
330        char('.'),
331        |i| Some((i, None)),
332        map(precision, |(i, p)| (i, Some(p))),
333    )(input)?;
334
335    let (input, ty) = type_(input)?;
336
337    Some((
338        input,
339        FormatSpec {
340            align,
341            sign,
342            alternate,
343            zero_padding,
344            width,
345            precision,
346            ty,
347        },
348    ))
349}
350
351/// Parses an `align` as defined in the [grammar spec][0].
352///
353/// # Grammar
354///
355/// [`align`]` := '<' | '^' | '>'`
356///
357/// # Example
358///
359/// ```text
360/// <
361/// ^
362/// >
363/// ```
364///
365/// [0]: std::fmt#syntax
366fn align(input: &str) -> Option<(LeftToParse<'_>, Align)> {
367    alt(&mut [
368        &mut map(char('<'), |i| (i, Align::Left)),
369        &mut map(char('^'), |i| (i, Align::Center)),
370        &mut map(char('>'), |i| (i, Align::Right)),
371    ])(input)
372}
373
374/// Parses a `sign` as defined in the [grammar spec][0].
375///
376/// # Grammar
377///
378/// [`sign`]` := '+' | '-'`
379///
380/// # Example
381///
382/// ```text
383/// +
384/// -
385/// ```
386///
387/// [0]: std::fmt#syntax
388fn sign(input: &str) -> Option<(LeftToParse<'_>, Sign)> {
389    alt(&mut [
390        &mut map(char('+'), |i| (i, Sign::Plus)),
391        &mut map(char('-'), |i| (i, Sign::Minus)),
392    ])(input)
393}
394
395/// Parses a `precision` as defined in the [grammar spec][0].
396///
397/// # Grammar
398///
399/// [`precision`]` := `[`count`]` | '*'`
400///
401/// # Example
402///
403/// ```text
404/// 0
405/// 42$
406/// par$
407/// *
408/// ```
409///
410/// [0]: std::fmt#syntax
411fn precision(input: &str) -> Option<(LeftToParse<'_>, Precision<'_>)> {
412    alt(&mut [
413        &mut map(count, |(i, c)| (i, Precision::Count(c))),
414        &mut map(char('*'), |i| (i, Precision::Star)),
415    ])(input)
416}
417
418/// Parses a `type` as defined in the [grammar spec][0].
419///
420/// # Grammar
421///
422/// [`type`]` := '' | '?' | 'x?' | 'X?' | identifier`
423///
424/// # Example
425///
426/// All possible [`Type`]s.
427///
428/// ```text
429/// ?
430/// x?
431/// X?
432/// o
433/// x
434/// X
435/// p
436/// b
437/// e
438/// E
439/// ```
440///
441/// [`type`]: type_
442/// [0]: std::fmt#syntax
443fn type_(input: &str) -> Option<(&str, Type)> {
444    alt(&mut [
445        &mut map(str("x?"), |i| (i, Type::LowerDebug)),
446        &mut map(str("X?"), |i| (i, Type::UpperDebug)),
447        &mut map(char('?'), |i| (i, Type::Debug)),
448        &mut map(char('o'), |i| (i, Type::Octal)),
449        &mut map(char('x'), |i| (i, Type::LowerHex)),
450        &mut map(char('X'), |i| (i, Type::UpperHex)),
451        &mut map(char('p'), |i| (i, Type::Pointer)),
452        &mut map(char('b'), |i| (i, Type::Binary)),
453        &mut map(char('e'), |i| (i, Type::LowerExp)),
454        &mut map(char('E'), |i| (i, Type::UpperExp)),
455        &mut map(lookahead(char('}')), |i| (i, Type::Display)),
456    ])(input)
457}
458
459/// Parses a `count` as defined in the [grammar spec][0].
460///
461/// # Grammar
462///
463/// [`count`]` := `[`parameter`]` | `[`integer`]
464///
465/// # Example
466///
467/// ```text
468/// 0
469/// 42$
470/// par$
471/// ```
472///
473/// [0]: std::fmt#syntax
474fn count(input: &str) -> Option<(LeftToParse<'_>, Count<'_>)> {
475    alt(&mut [
476        &mut map(parameter, |(i, p)| (i, Count::Parameter(p))),
477        &mut map(integer, |(i, int)| (i, Count::Integer(int))),
478    ])(input)
479}
480
481/// Parses a `parameter` as defined in the [grammar spec][0].
482///
483/// # Grammar
484///
485/// [`parameter`]` := `[`argument`]` '$'`
486///
487/// # Example
488///
489/// ```text
490/// 42$
491/// par$
492/// ```
493///
494/// [0]: std::fmt#syntax
495fn parameter(input: &str) -> Option<(LeftToParse<'_>, Parameter<'_>)> {
496    and_then(argument, |(i, arg)| map(char('$'), |i| (i, arg))(i))(input)
497}
498
499/// Parses an `identifier` as defined in the [grammar spec][0].
500///
501/// # Grammar
502///
503/// `IDENTIFIER_OR_KEYWORD : XID_Start XID_Continue* | _ XID_Continue+`
504///
505/// See [rust reference][2] for more info.
506///
507/// # Example
508///
509/// ```text
510/// identifier
511/// Минск
512/// ```
513///
514/// [0]: std::fmt#syntax
515/// [2]: https://doc.rust-lang.org/reference/identifiers.html
516fn identifier(input: &str) -> Option<(LeftToParse<'_>, Identifier<'_>)> {
517    map(
518        alt(&mut [
519            &mut map(
520                check_char(XID::is_xid_start),
521                take_while0(check_char(XID::is_xid_continue)),
522            ),
523            &mut and_then(char('_'), take_while1(check_char(XID::is_xid_continue))),
524        ]),
525        |(i, _)| (i, &input[..(input.len() - i.len())]),
526    )(input)
527}
528
529/// Parses an `integer` as defined in the [grammar spec][0].
530///
531/// [0]: std::fmt#syntax
532fn integer(input: &str) -> Option<(LeftToParse<'_>, usize)> {
533    and_then(
534        take_while1(check_char(|c| c.is_ascii_digit())),
535        |(i, int)| int.parse().ok().map(|int| (i, int)),
536    )(input)
537}
538
539/// Parses a `text` as defined in the [grammar spec][0].
540///
541/// [0]: std::fmt#syntax
542fn text(input: &str) -> Option<(LeftToParse<'_>, &str)> {
543    take_until1(any_char, one_of("{}"))(input)
544}
545
546type FallibleParser<'p> = &'p mut dyn FnMut(&str) -> Option<&str>;
547
548/// Tries to apply parsers in sequence. Returns [`None`] in case one of them
549/// returned [`None`].
550fn try_seq<'p>(
551    parsers: &'p mut [FallibleParser<'p>],
552) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + 'p {
553    move |input| parsers.iter_mut().try_fold(input, |i, p| (**p)(i))
554}
555
556/// Returns first successful parser or [`None`] in case all of them returned
557/// [`None`].
558fn alt<'p, 'i, T: 'i>(
559    parsers: &'p mut [&'p mut dyn FnMut(&'i str) -> Option<T>],
560) -> impl FnMut(&'i str) -> Option<T> + 'p {
561    move |input| parsers.iter_mut().find_map(|p| (**p)(input))
562}
563
564/// Maps output of the parser in case it returned [`Some`].
565fn map<'i, I: 'i, O: 'i>(
566    mut parser: impl FnMut(&'i str) -> Option<I>,
567    mut f: impl FnMut(I) -> O,
568) -> impl FnMut(&'i str) -> Option<O> {
569    move |input| parser(input).map(&mut f)
570}
571
572/// Maps output of the parser in case it returned [`Some`] or uses `default`.
573fn map_or_else<'i, I: 'i, O: 'i>(
574    mut parser: impl FnMut(&'i str) -> Option<I>,
575    mut default: impl FnMut(&'i str) -> O,
576    mut f: impl FnMut(I) -> O,
577) -> impl FnMut(&'i str) -> O {
578    move |input| parser(input).map_or_else(|| default(input), &mut f)
579}
580
581/// Returns [`None`] if the parser returned is [`None`], otherwise calls `f`
582/// with the wrapped value and returns the result.
583fn and_then<'i, I: 'i, O: 'i>(
584    mut parser: impl FnMut(&'i str) -> Option<I>,
585    mut f: impl FnMut(I) -> Option<O>,
586) -> impl FnMut(&'i str) -> Option<O> {
587    move |input| parser(input).and_then(&mut f)
588}
589
590/// Checks whether `parser` is successful while not advancing the original
591/// `input`.
592fn lookahead(
593    mut parser: impl FnMut(&str) -> Option<&str>,
594) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
595    move |input| map(&mut parser, |_| input)(input)
596}
597
598/// Makes underlying `parser` optional by returning the original `input` and
599/// [`None`] in case it returned [`None`].
600fn optional_result<'i, T: 'i>(
601    mut parser: impl FnMut(&'i str) -> Option<(&'i str, T)>,
602) -> impl FnMut(&'i str) -> (LeftToParse<'i>, Option<T>) {
603    move |input: &str| {
604        map_or_else(&mut parser, |i| (i, None), |(i, c)| (i, Some(c)))(input)
605    }
606}
607
608/// Parses while `parser` is successful. Never fails.
609fn take_while0(
610    mut parser: impl FnMut(&str) -> Option<&str>,
611) -> impl FnMut(&str) -> (LeftToParse<'_>, &str) {
612    move |input| {
613        let mut cur = input;
614        while let Some(step) = parser(cur) {
615            cur = step;
616        }
617        (cur, &input[..(input.len() - cur.len())])
618    }
619}
620
621/// Parses while `parser` is successful. Returns [`None`] in case `parser` never
622/// succeeded.
623fn take_while1(
624    mut parser: impl FnMut(&str) -> Option<&str>,
625) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
626    move |input| {
627        let mut cur = parser(input)?;
628        while let Some(step) = parser(cur) {
629            cur = step;
630        }
631        Some((cur, &input[..(input.len() - cur.len())]))
632    }
633}
634
635/// Parses with `basic` while `until` returns [`None`]. Returns [`None`] in case
636/// `until` succeeded initially or `basic` never succeeded. Doesn't consume
637/// [`char`]s parsed by `until`.
638///
639/// [`char`]: fn@char
640fn take_until1(
641    mut basic: impl FnMut(&str) -> Option<&str>,
642    mut until: impl FnMut(&str) -> Option<&str>,
643) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
644    move |input: &str| {
645        if until(input).is_some() {
646            return None;
647        }
648        let mut cur = basic(input)?;
649        loop {
650            if until(cur).is_some() {
651                break;
652            }
653            let Some(b) = basic(cur) else {
654                break;
655            };
656            cur = b;
657        }
658
659        Some((cur, &input[..(input.len() - cur.len())]))
660    }
661}
662
663/// Checks whether `input` starts with `s`.
664fn str(s: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
665    move |input| input.starts_with(s).then(|| &input[s.len()..])
666}
667
668/// Checks whether `input` starts with `c`.
669fn char(c: char) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
670    move |input| input.starts_with(c).then(|| &input[c.len_utf8()..])
671}
672
673/// Checks whether first [`char`] suits `check`.
674///
675/// [`char`]: fn@char
676fn check_char(
677    mut check: impl FnMut(char) -> bool,
678) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
679    move |input| {
680        input
681            .chars()
682            .next()
683            .and_then(|c| check(c).then(|| &input[c.len_utf8()..]))
684    }
685}
686
687/// Checks whether first [`char`] of input is present in `chars`.
688///
689/// [`char`]: fn@char
690fn one_of(chars: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
691    move |input: &str| chars.chars().find_map(|c| char(c)(input))
692}
693
694/// Parses any [`char`].
695///
696/// [`char`]: fn@char
697fn any_char(input: &str) -> Option<LeftToParse<'_>> {
698    input.chars().next().map(|c| &input[c.len_utf8()..])
699}
700
701/// Parses any [`char`] and returns it.
702///
703/// [`char`]: fn@char
704fn take_any_char(input: &str) -> Option<(LeftToParse<'_>, char)> {
705    input.chars().next().map(|c| (&input[c.len_utf8()..], c))
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711
712    #[test]
713    fn text() {
714        assert_eq!(format_string(""), Some(FormatString { formats: vec![] }));
715        assert_eq!(
716            format_string("test"),
717            Some(FormatString { formats: vec![] }),
718        );
719        assert_eq!(
720            format_string("Минск"),
721            Some(FormatString { formats: vec![] }),
722        );
723        assert_eq!(format_string("🦀"), Some(FormatString { formats: vec![] }));
724    }
725
726    #[test]
727    fn argument() {
728        assert_eq!(
729            format_string("{}"),
730            Some(FormatString {
731                formats: vec![Format {
732                    arg: None,
733                    spec: None,
734                }],
735            }),
736        );
737        assert_eq!(
738            format_string("{0}"),
739            Some(FormatString {
740                formats: vec![Format {
741                    arg: Some(Argument::Integer(0)),
742                    spec: None,
743                }],
744            }),
745        );
746        assert_eq!(
747            format_string("{par}"),
748            Some(FormatString {
749                formats: vec![Format {
750                    arg: Some(Argument::Identifier("par")),
751                    spec: None,
752                }],
753            }),
754        );
755        assert_eq!(
756            format_string("{Минск}"),
757            Some(FormatString {
758                formats: vec![Format {
759                    arg: Some(Argument::Identifier("Минск")),
760                    spec: None,
761                }],
762            }),
763        );
764    }
765
766    #[test]
767    fn spec() {
768        assert_eq!(
769            format_string("{:}"),
770            Some(FormatString {
771                formats: vec![Format {
772                    arg: None,
773                    spec: Some(FormatSpec {
774                        align: None,
775                        sign: None,
776                        alternate: None,
777                        zero_padding: None,
778                        width: None,
779                        precision: None,
780                        ty: Type::Display,
781                    }),
782                }],
783            }),
784        );
785        assert_eq!(
786            format_string("{:^}"),
787            Some(FormatString {
788                formats: vec![Format {
789                    arg: None,
790                    spec: Some(FormatSpec {
791                        align: Some((None, Align::Center)),
792                        sign: None,
793                        alternate: None,
794                        zero_padding: None,
795                        width: None,
796                        precision: None,
797                        ty: Type::Display,
798                    }),
799                }],
800            }),
801        );
802        assert_eq!(
803            format_string("{:-<}"),
804            Some(FormatString {
805                formats: vec![Format {
806                    arg: None,
807                    spec: Some(FormatSpec {
808                        align: Some((Some('-'), Align::Left)),
809                        sign: None,
810                        alternate: None,
811                        zero_padding: None,
812                        width: None,
813                        precision: None,
814                        ty: Type::Display,
815                    }),
816                }],
817            }),
818        );
819        assert_eq!(
820            format_string("{: <}"),
821            Some(FormatString {
822                formats: vec![Format {
823                    arg: None,
824                    spec: Some(FormatSpec {
825                        align: Some((Some(' '), Align::Left)),
826                        sign: None,
827                        alternate: None,
828                        zero_padding: None,
829                        width: None,
830                        precision: None,
831                        ty: Type::Display,
832                    }),
833                }],
834            }),
835        );
836        assert_eq!(
837            format_string("{:^<}"),
838            Some(FormatString {
839                formats: vec![Format {
840                    arg: None,
841                    spec: Some(FormatSpec {
842                        align: Some((Some('^'), Align::Left)),
843                        sign: None,
844                        alternate: None,
845                        zero_padding: None,
846                        width: None,
847                        precision: None,
848                        ty: Type::Display,
849                    }),
850                }],
851            }),
852        );
853        assert_eq!(
854            format_string("{:+}"),
855            Some(FormatString {
856                formats: vec![Format {
857                    arg: None,
858                    spec: Some(FormatSpec {
859                        align: None,
860                        sign: Some(Sign::Plus),
861                        alternate: None,
862                        zero_padding: None,
863                        width: None,
864                        precision: None,
865                        ty: Type::Display,
866                    }),
867                }],
868            }),
869        );
870        assert_eq!(
871            format_string("{:^<-}"),
872            Some(FormatString {
873                formats: vec![Format {
874                    arg: None,
875                    spec: Some(FormatSpec {
876                        align: Some((Some('^'), Align::Left)),
877                        sign: Some(Sign::Minus),
878                        alternate: None,
879                        zero_padding: None,
880                        width: None,
881                        precision: None,
882                        ty: Type::Display,
883                    }),
884                }],
885            }),
886        );
887        assert_eq!(
888            format_string("{:#}"),
889            Some(FormatString {
890                formats: vec![Format {
891                    arg: None,
892                    spec: Some(FormatSpec {
893                        align: None,
894                        sign: None,
895                        alternate: Some(Alternate),
896                        zero_padding: None,
897                        width: None,
898                        precision: None,
899                        ty: Type::Display,
900                    }),
901                }],
902            }),
903        );
904        assert_eq!(
905            format_string("{:+#}"),
906            Some(FormatString {
907                formats: vec![Format {
908                    arg: None,
909                    spec: Some(FormatSpec {
910                        align: None,
911                        sign: Some(Sign::Plus),
912                        alternate: Some(Alternate),
913                        zero_padding: None,
914                        width: None,
915                        precision: None,
916                        ty: Type::Display,
917                    }),
918                }],
919            }),
920        );
921        assert_eq!(
922            format_string("{:-<#}"),
923            Some(FormatString {
924                formats: vec![Format {
925                    arg: None,
926                    spec: Some(FormatSpec {
927                        align: Some((Some('-'), Align::Left)),
928                        sign: None,
929                        alternate: Some(Alternate),
930                        zero_padding: None,
931                        width: None,
932                        precision: None,
933                        ty: Type::Display,
934                    }),
935                }],
936            }),
937        );
938        assert_eq!(
939            format_string("{:^<-#}"),
940            Some(FormatString {
941                formats: vec![Format {
942                    arg: None,
943                    spec: Some(FormatSpec {
944                        align: Some((Some('^'), Align::Left)),
945                        sign: Some(Sign::Minus),
946                        alternate: Some(Alternate),
947                        zero_padding: None,
948                        width: None,
949                        precision: None,
950                        ty: Type::Display,
951                    }),
952                }],
953            }),
954        );
955        assert_eq!(
956            format_string("{:0}"),
957            Some(FormatString {
958                formats: vec![Format {
959                    arg: None,
960                    spec: Some(FormatSpec {
961                        align: None,
962                        sign: None,
963                        alternate: None,
964                        zero_padding: Some(ZeroPadding),
965                        width: None,
966                        precision: None,
967                        ty: Type::Display,
968                    }),
969                }],
970            }),
971        );
972        assert_eq!(
973            format_string("{:#0}"),
974            Some(FormatString {
975                formats: vec![Format {
976                    arg: None,
977                    spec: Some(FormatSpec {
978                        align: None,
979                        sign: None,
980                        alternate: Some(Alternate),
981                        zero_padding: Some(ZeroPadding),
982                        width: None,
983                        precision: None,
984                        ty: Type::Display,
985                    }),
986                }],
987            }),
988        );
989        assert_eq!(
990            format_string("{:-0}"),
991            Some(FormatString {
992                formats: vec![Format {
993                    arg: None,
994                    spec: Some(FormatSpec {
995                        align: None,
996                        sign: Some(Sign::Minus),
997                        alternate: None,
998                        zero_padding: Some(ZeroPadding),
999                        width: None,
1000                        precision: None,
1001                        ty: Type::Display,
1002                    }),
1003                }],
1004            }),
1005        );
1006        assert_eq!(
1007            format_string("{:^<0}"),
1008            Some(FormatString {
1009                formats: vec![Format {
1010                    arg: None,
1011                    spec: Some(FormatSpec {
1012                        align: Some((Some('^'), Align::Left)),
1013                        sign: None,
1014                        alternate: None,
1015                        zero_padding: Some(ZeroPadding),
1016                        width: None,
1017                        precision: None,
1018                        ty: Type::Display,
1019                    }),
1020                }],
1021            }),
1022        );
1023        assert_eq!(
1024            format_string("{:^<+#0}"),
1025            Some(FormatString {
1026                formats: vec![Format {
1027                    arg: None,
1028                    spec: Some(FormatSpec {
1029                        align: Some((Some('^'), Align::Left)),
1030                        sign: Some(Sign::Plus),
1031                        alternate: Some(Alternate),
1032                        zero_padding: Some(ZeroPadding),
1033                        width: None,
1034                        precision: None,
1035                        ty: Type::Display,
1036                    }),
1037                }],
1038            }),
1039        );
1040        assert_eq!(
1041            format_string("{:1}"),
1042            Some(FormatString {
1043                formats: vec![Format {
1044                    arg: None,
1045                    spec: Some(FormatSpec {
1046                        align: None,
1047                        sign: None,
1048                        alternate: None,
1049                        zero_padding: None,
1050                        width: Some(Count::Integer(1)),
1051                        precision: None,
1052                        ty: Type::Display,
1053                    }),
1054                }],
1055            }),
1056        );
1057        assert_eq!(
1058            format_string("{:1$}"),
1059            Some(FormatString {
1060                formats: vec![Format {
1061                    arg: None,
1062                    spec: Some(FormatSpec {
1063                        align: None,
1064                        sign: None,
1065                        alternate: None,
1066                        zero_padding: None,
1067                        width: Some(Count::Parameter(Argument::Integer(1))),
1068                        precision: None,
1069                        ty: Type::Display,
1070                    }),
1071                }],
1072            }),
1073        );
1074        assert_eq!(
1075            format_string("{:par$}"),
1076            Some(FormatString {
1077                formats: vec![Format {
1078                    arg: None,
1079                    spec: Some(FormatSpec {
1080                        align: None,
1081                        sign: None,
1082                        alternate: None,
1083                        zero_padding: None,
1084                        width: Some(Count::Parameter(Argument::Identifier("par"))),
1085                        precision: None,
1086                        ty: Type::Display,
1087                    }),
1088                }],
1089            }),
1090        );
1091        assert_eq!(
1092            format_string("{:-^-#0Минск$}"),
1093            Some(FormatString {
1094                formats: vec![Format {
1095                    arg: None,
1096                    spec: Some(FormatSpec {
1097                        align: Some((Some('-'), Align::Center)),
1098                        sign: Some(Sign::Minus),
1099                        alternate: Some(Alternate),
1100                        zero_padding: Some(ZeroPadding),
1101                        width: Some(Count::Parameter(Argument::Identifier("Минск"))),
1102                        precision: None,
1103                        ty: Type::Display,
1104                    }),
1105                }],
1106            }),
1107        );
1108        assert_eq!(
1109            format_string("{:.*}"),
1110            Some(FormatString {
1111                formats: vec![Format {
1112                    arg: None,
1113                    spec: Some(FormatSpec {
1114                        align: None,
1115                        sign: None,
1116                        alternate: None,
1117                        zero_padding: None,
1118                        width: None,
1119                        precision: Some(Precision::Star),
1120                        ty: Type::Display,
1121                    }),
1122                }],
1123            }),
1124        );
1125        assert_eq!(
1126            format_string("{:.0}"),
1127            Some(FormatString {
1128                formats: vec![Format {
1129                    arg: None,
1130                    spec: Some(FormatSpec {
1131                        align: None,
1132                        sign: None,
1133                        alternate: None,
1134                        zero_padding: None,
1135                        width: None,
1136                        precision: Some(Precision::Count(Count::Integer(0))),
1137                        ty: Type::Display,
1138                    }),
1139                }],
1140            }),
1141        );
1142        assert_eq!(
1143            format_string("{:.0$}"),
1144            Some(FormatString {
1145                formats: vec![Format {
1146                    arg: None,
1147                    spec: Some(FormatSpec {
1148                        align: None,
1149                        sign: None,
1150                        alternate: None,
1151                        zero_padding: None,
1152                        width: None,
1153                        precision: Some(Precision::Count(Count::Parameter(
1154                            Argument::Integer(0),
1155                        ))),
1156                        ty: Type::Display,
1157                    }),
1158                }],
1159            }),
1160        );
1161        assert_eq!(
1162            format_string("{:.par$}"),
1163            Some(FormatString {
1164                formats: vec![Format {
1165                    arg: None,
1166                    spec: Some(FormatSpec {
1167                        align: None,
1168                        sign: None,
1169                        alternate: None,
1170                        zero_padding: None,
1171                        width: None,
1172                        precision: Some(Precision::Count(Count::Parameter(
1173                            Argument::Identifier("par"),
1174                        ))),
1175                        ty: Type::Display,
1176                    }),
1177                }],
1178            }),
1179        );
1180        assert_eq!(
1181            format_string("{: >+#2$.par$}"),
1182            Some(FormatString {
1183                formats: vec![Format {
1184                    arg: None,
1185                    spec: Some(FormatSpec {
1186                        align: Some((Some(' '), Align::Right)),
1187                        sign: Some(Sign::Plus),
1188                        alternate: Some(Alternate),
1189                        zero_padding: None,
1190                        width: Some(Count::Parameter(Argument::Integer(2))),
1191                        precision: Some(Precision::Count(Count::Parameter(
1192                            Argument::Identifier("par"),
1193                        ))),
1194                        ty: Type::Display,
1195                    }),
1196                }],
1197            }),
1198        );
1199        assert_eq!(
1200            format_string("{:x?}"),
1201            Some(FormatString {
1202                formats: vec![Format {
1203                    arg: None,
1204                    spec: Some(FormatSpec {
1205                        align: None,
1206                        sign: None,
1207                        alternate: None,
1208                        zero_padding: None,
1209                        width: None,
1210                        precision: None,
1211                        ty: Type::LowerDebug,
1212                    }),
1213                }],
1214            }),
1215        );
1216        assert_eq!(
1217            format_string("{:E}"),
1218            Some(FormatString {
1219                formats: vec![Format {
1220                    arg: None,
1221                    spec: Some(FormatSpec {
1222                        align: None,
1223                        sign: None,
1224                        alternate: None,
1225                        zero_padding: None,
1226                        width: None,
1227                        precision: None,
1228                        ty: Type::UpperExp,
1229                    }),
1230                }],
1231            }),
1232        );
1233        assert_eq!(
1234            format_string("{: >+#par$.par$X?}"),
1235            Some(FormatString {
1236                formats: vec![Format {
1237                    arg: None,
1238                    spec: Some(FormatSpec {
1239                        align: Some((Some(' '), Align::Right)),
1240                        sign: Some(Sign::Plus),
1241                        alternate: Some(Alternate),
1242                        zero_padding: None,
1243                        width: Some(Count::Parameter(Argument::Identifier("par"))),
1244                        precision: Some(Precision::Count(Count::Parameter(
1245                            Argument::Identifier("par"),
1246                        ))),
1247                        ty: Type::UpperDebug,
1248                    }),
1249                }],
1250            }),
1251        );
1252    }
1253
1254    #[test]
1255    fn full() {
1256        assert_eq!(
1257            format_string("prefix{{{0:#?}postfix{par:-^par$.a$}}}"),
1258            Some(FormatString {
1259                formats: vec![
1260                    Format {
1261                        arg: Some(Argument::Integer(0)),
1262                        spec: Some(FormatSpec {
1263                            align: None,
1264                            sign: None,
1265                            alternate: Some(Alternate),
1266                            zero_padding: None,
1267                            width: None,
1268                            precision: None,
1269                            ty: Type::Debug,
1270                        }),
1271                    },
1272                    Format {
1273                        arg: Some(Argument::Identifier("par")),
1274                        spec: Some(FormatSpec {
1275                            align: Some((Some('-'), Align::Center)),
1276                            sign: None,
1277                            alternate: None,
1278                            zero_padding: None,
1279                            width: Some(Count::Parameter(Argument::Identifier("par"))),
1280                            precision: Some(Precision::Count(Count::Parameter(
1281                                Argument::Identifier("a"),
1282                            ))),
1283                            ty: Type::Display,
1284                        }),
1285                    },
1286                ],
1287            }),
1288        );
1289    }
1290
1291    #[test]
1292    fn error() {
1293        assert_eq!(format_string("{"), None);
1294        assert_eq!(format_string("}"), None);
1295        assert_eq!(format_string("{{}"), None);
1296        assert_eq!(format_string("{:x?"), None);
1297        assert_eq!(format_string("{:.}"), None);
1298        assert_eq!(format_string("{:q}"), None);
1299        assert_eq!(format_string("{:par}"), None);
1300        assert_eq!(format_string("{⚙️}"), None);
1301    }
1302}