Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL parsing.rs   Interaktion und
Portierbarkeitunbekannt

 
//! Parsing of [Rust `fmt` syntax][0].
//!
//! [0]: std::fmt#syntax

use std::{convert::identity, iter};

use unicode_xid::UnicodeXID as XID;

/// Output of the [`format_string`] parser.
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct FormatString<'a> {
    pub(crate) formats: Vec<Format<'a>>,
}

/// Output of the [`format`] parser.
///
/// [`format`]: fn@format
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) struct Format<'a> {
    pub(crate) arg: Option<Argument<'a>>,
    pub(crate) spec: Option<FormatSpec<'a>>,
}

/// Output of the [`format_spec`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) struct FormatSpec<'a> {
    pub(crate) width: Option<Width<'a>>,
    pub(crate) precision: Option<Precision<'a>>,
    pub(crate) ty: Type,
}

/// Output of the [`argument`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum Argument<'a> {
    Integer(usize),
    Identifier(&'a str),
}

/// Output of the [`precision`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum Precision<'a> {
    Count(Count<'a>),
    Star,
}

/// Output of the [`count`] parser.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum Count<'a> {
    Integer(usize),
    Parameter(Parameter<'a>),
}

/// Output of the [`type_`] parser. See [formatting traits][1] for more info.
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#formatting-traits
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum Type {
    Display,
    Debug,
    LowerDebug,
    UpperDebug,
    Octal,
    LowerHex,
    UpperHex,
    Pointer,
    Binary,
    LowerExp,
    UpperExp,
}

impl Type {
    /// Returns trait name of this [`Type`].
    pub(crate) fn trait_name(&self) -> &'static str {
        match self {
            Type::Display => "Display",
            Type::Debug | Type::LowerDebug | Type::UpperDebug => "Debug",
            Type::Octal => "Octal",
            Type::LowerHex => "LowerHex",
            Type::UpperHex => "UpperHex",
            Type::Pointer => "Pointer",
            Type::Binary => "Binary",
            Type::LowerExp => "LowerExp",
            Type::UpperExp => "UpperExp",
        }
    }
}

/// Type alias for the [`FormatSpec::width`].
type Width<'a> = Count<'a>;

/// Output of the [`maybe_format`] parser.
type MaybeFormat<'a> = Option<Format<'a>>;

/// Output of the [`identifier`] parser.
type Identifier<'a> = &'a str;

/// Output of the [`parameter`] parser.
type Parameter<'a> = Argument<'a>;

/// [`str`] left to parse.
///
/// [`str`]: prim@str
type LeftToParse<'a> = &'a str;

/// Parses a `format_string` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format_string`]` := `[`text`]` [`[`maybe_format text`]`] *`
///
/// # Example
///
/// ```text
/// Hello
/// Hello, {}!
/// {:?}
/// Hello {people}!
/// {} {}
/// {:04}
/// {par:-^#.0$?}
/// ```
///
/// # Return value
///
/// - [`Some`] in case of successful parse.
/// - [`None`] otherwise (not all characters are consumed by underlying
///   parsers).
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
pub(crate) fn format_string(input: &str) -> Option<FormatString<'_>> {
    let (mut input, _) = optional_result(text)(input);

    let formats = iter::repeat(())
        .scan(&mut input, |input, _| {
            let (curr, format) =
                alt(&mut [&mut maybe_format, &mut map(text, |(i, _)| (i, None))])(
                    input,
                )?;
            **input = curr;
            Some(format)
        })
        .flatten()
        .collect();

    // Should consume all tokens for a successful parse.
    input.is_empty().then_some(FormatString { formats })
}

/// Parses a `maybe_format` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`maybe_format`]` := '{' '{' | '}' '}' | `[`format`]
///
/// # Example
///
/// ```text
/// {{
/// }}
/// {:04}
/// {:#?}
/// {par:-^#.0$?}
/// ```
///
/// [`format`]: fn@format
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn maybe_format(input: &str) -> Option<(LeftToParse<'_>, MaybeFormat<'_>)> {
    alt(&mut [
        &mut map(str("{{"), |i| (i, None)),
        &mut map(str("}}"), |i| (i, None)),
        &mut map(format, |(i, format)| (i, Some(format))),
    ])(input)
}

/// Parses a `format` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format`]` := '{' [`[`argument`]`] [':' `[`format_spec`]`] '}'`
///
/// # Example
///
/// ```text
/// {par}
/// {:#?}
/// {par:-^#.0$?}
/// ```
///
/// [`format`]: fn@format
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn format(input: &str) -> Option<(LeftToParse<'_>, Format<'_>)> {
    let input = char('{')(input)?;

    let (input, arg) = optional_result(argument)(input);

    let (input, spec) = map_or_else(
        char(':'),
        |i| Some((i, None)),
        map(format_spec, |(i, s)| (i, Some(s))),
    )(input)?;

    let input = char('}')(input)?;

    Some((input, Format { arg, spec }))
}

/// Parses an `argument` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`argument`]` := `[`integer`]` | `[`identifier`]
///
/// # Example
///
/// ```text
/// 0
/// ident
/// Минск
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn argument(input: &str) -> Option<(LeftToParse<'_>, Argument)> {
    alt(&mut [
        &mut map(identifier, |(i, ident)| (i, Argument::Identifier(ident))),
        &mut map(integer, |(i, int)| (i, Argument::Integer(int))),
    ])(input)
}

/// Parses a `format_spec` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`format_spec`]` := [[fill]align][sign]['#']['0'][width]`
///                     `['.' `[`precision`]`]`[`type`]
///
/// # Example
///
/// ```text
/// ^
/// <^
/// ->+#0width$.precision$x?
/// ```
///
/// [`type`]: type_
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn format_spec(input: &str) -> Option<(LeftToParse<'_>, FormatSpec<'_>)> {
    let input = unwrap_or_else(
        alt(&mut [
            &mut try_seq(&mut [&mut any_char, &mut one_of("<^>")]),
            &mut one_of("<^>"),
        ]),
        identity,
    )(input);

    let input = seq(&mut [
        &mut optional(one_of("+-")),
        &mut optional(char('#')),
        &mut optional(try_seq(&mut [
            &mut char('0'),
            &mut lookahead(check_char(|c| !matches!(c, '$'))),
        ])),
    ])(input);

    let (input, width) = optional_result(count)(input);

    let (input, precision) = map_or_else(
        char('.'),
        |i| Some((i, None)),
        map(precision, |(i, p)| (i, Some(p))),
    )(input)?;

    let (input, ty) = type_(input)?;

    Some((
        input,
        FormatSpec {
            width,
            precision,
            ty,
        },
    ))
}

/// Parses a `precision` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`precision`]` := `[`count`]` | '*'`
///
/// # Example
///
/// ```text
/// 0
/// 42$
/// par$
/// *
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn precision(input: &str) -> Option<(LeftToParse<'_>, Precision<'_>)> {
    alt(&mut [
        &mut map(count, |(i, c)| (i, Precision::Count(c))),
        &mut map(char('*'), |i| (i, Precision::Star)),
    ])(input)
}

/// Parses a `type` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`type`]` := '' | '?' | 'x?' | 'X?' | identifier`
///
/// # Example
///
/// All possible [`Type`]s.
///
/// ```text
/// ?
/// x?
/// X?
/// o
/// x
/// X
/// p
/// b
/// e
/// E
/// ```
///
/// [`type`]: type_
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn type_(input: &str) -> Option<(&str, Type)> {
    alt(&mut [
        &mut map(str("x?"), |i| (i, Type::LowerDebug)),
        &mut map(str("X?"), |i| (i, Type::UpperDebug)),
        &mut map(char('?'), |i| (i, Type::Debug)),
        &mut map(char('o'), |i| (i, Type::Octal)),
        &mut map(char('x'), |i| (i, Type::LowerHex)),
        &mut map(char('X'), |i| (i, Type::UpperHex)),
        &mut map(char('p'), |i| (i, Type::Pointer)),
        &mut map(char('b'), |i| (i, Type::Binary)),
        &mut map(char('e'), |i| (i, Type::LowerExp)),
        &mut map(char('E'), |i| (i, Type::UpperExp)),
        &mut map(lookahead(char('}')), |i| (i, Type::Display)),
    ])(input)
}

/// Parses a `count` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`count`]` := `[`parameter`]` | `[`integer`]
///
/// # Example
///
/// ```text
/// 0
/// 42$
/// par$
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn count(input: &str) -> Option<(LeftToParse<'_>, Count<'_>)> {
    alt(&mut [
        &mut map(parameter, |(i, p)| (i, Count::Parameter(p))),
        &mut map(integer, |(i, int)| (i, Count::Integer(int))),
    ])(input)
}

/// Parses a `parameter` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// [`parameter`]` := `[`argument`]` '$'`
///
/// # Example
///
/// ```text
/// 42$
/// par$
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn parameter(input: &str) -> Option<(LeftToParse<'_>, Parameter<'_>)> {
    and_then(argument, |(i, arg)| map(char('$'), |i| (i, arg))(i))(input)
}

/// Parses an `identifier` as defined in the [grammar spec][1].
///
/// # Grammar
///
/// `IDENTIFIER_OR_KEYWORD : XID_Start XID_Continue* | _ XID_Continue+`
///
/// See [rust reference][2] for more info.
///
/// # Example
///
/// ```text
/// identifier
/// Минск
/// ```
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
/// [2]: https://doc.rust-lang.org/reference/identifiers.html
fn identifier(input: &str) -> Option<(LeftToParse<'_>, Identifier<'_>)> {
    map(
        alt(&mut [
            &mut map(
                check_char(XID::is_xid_start),
                take_while0(check_char(XID::is_xid_continue)),
            ),
            &mut and_then(char('_'), take_while1(check_char(XID::is_xid_continue))),
        ]),
        |(i, _)| (i, &input[..(input.as_bytes().len() - i.as_bytes().len())]),
    )(input)
}

/// Parses an `integer` as defined in the [grammar spec][1].
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn integer(input: &str) -> Option<(LeftToParse<'_>, usize)> {
    and_then(
        take_while1(check_char(|c| c.is_ascii_digit())),
        |(i, int)| int.parse().ok().map(|int| (i, int)),
    )(input)
}

/// Parses a `text` as defined in the [grammar spec][1].
///
/// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#syntax
fn text(input: &str) -> Option<(LeftToParse<'_>, &str)> {
    take_until1(any_char, one_of("{}"))(input)
}

type Parser<'p> = &'p mut dyn FnMut(&str) -> &str;

/// Applies non-failing parsers in sequence.
fn seq<'p>(parsers: &'p mut [Parser<'p>]) -> impl FnMut(&str) -> LeftToParse<'_> + 'p {
    move |input| parsers.iter_mut().fold(input, |i, p| (**p)(i))
}

type FallibleParser<'p> = &'p mut dyn FnMut(&str) -> Option<&str>;

/// Tries to apply parsers in sequence. Returns [`None`] in case one of them
/// returned [`None`].
fn try_seq<'p>(
    parsers: &'p mut [FallibleParser<'p>],
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + 'p {
    move |input| parsers.iter_mut().try_fold(input, |i, p| (**p)(i))
}

/// Returns first successful parser or [`None`] in case all of them returned
/// [`None`].
fn alt<'p, 'i, T: 'i>(
    parsers: &'p mut [&'p mut dyn FnMut(&'i str) -> Option<T>],
) -> impl FnMut(&'i str) -> Option<T> + 'p {
    move |input| parsers.iter_mut().find_map(|p| (**p)(input))
}

/// Maps output of the parser in case it returned [`Some`].
fn map<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut f: impl FnMut(I) -> O,
) -> impl FnMut(&'i str) -> Option<O> {
    move |input| parser(input).map(&mut f)
}

/// Maps output of the parser in case it returned [`Some`] or uses `default`.
fn map_or_else<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut default: impl FnMut(&'i str) -> O,
    mut f: impl FnMut(I) -> O,
) -> impl FnMut(&'i str) -> O {
    move |input| parser(input).map_or_else(|| default(input), &mut f)
}

/// Returns the contained [`Some`] value or computes it from a closure.
fn unwrap_or_else<'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<O>,
    mut f: impl FnMut(&'i str) -> O,
) -> impl FnMut(&'i str) -> O {
    move |input| parser(input).unwrap_or_else(|| f(input))
}

/// Returns [`None`] if the parser returned is [`None`], otherwise calls `f`
/// with the wrapped value and returns the result.
fn and_then<'i, I: 'i, O: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<I>,
    mut f: impl FnMut(I) -> Option<O>,
) -> impl FnMut(&'i str) -> Option<O> {
    move |input| parser(input).and_then(&mut f)
}

/// Checks whether `parser` is successful while not advancing the original
/// `input`.
fn lookahead(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| map(&mut parser, |_| input)(input)
}

/// Makes underlying `parser` optional by returning the original `input` in case
/// it returned [`None`].
fn optional(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> LeftToParse<'_> {
    move |input: &str| parser(input).unwrap_or(input)
}

/// Makes underlying `parser` optional by returning the original `input` and
/// [`None`] in case it returned [`None`].
fn optional_result<'i, T: 'i>(
    mut parser: impl FnMut(&'i str) -> Option<(&'i str, T)>,
) -> impl FnMut(&'i str) -> (LeftToParse<'i>, Option<T>) {
    move |input: &str| {
        map_or_else(&mut parser, |i| (i, None), |(i, c)| (i, Some(c)))(input)
    }
}

/// Parses while `parser` is successful. Never fails.
fn take_while0(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> (LeftToParse<'_>, &str) {
    move |input| {
        let mut cur = input;
        while let Some(step) = parser(cur) {
            cur = step;
        }
        (
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        )
    }
}

/// Parses while `parser` is successful. Returns [`None`] in case `parser` never
/// succeeded.
fn take_while1(
    mut parser: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
    move |input| {
        let mut cur = parser(input)?;
        while let Some(step) = parser(cur) {
            cur = step;
        }
        Some((
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        ))
    }
}

/// Parses with `basic` while `until` returns [`None`]. Returns [`None`] in case
/// `until` succeeded initially or `basic` never succeeded. Doesn't consume
/// [`char`]s parsed by `until`.
///
/// [`char`]: fn@char
fn take_until1(
    mut basic: impl FnMut(&str) -> Option<&str>,
    mut until: impl FnMut(&str) -> Option<&str>,
) -> impl FnMut(&str) -> Option<(LeftToParse<'_>, &str)> {
    move |input: &str| {
        if until(input).is_some() {
            return None;
        }
        let mut cur = basic(input)?;
        loop {
            if until(cur).is_some() {
                break;
            }
            let Some(b) = basic(cur) else {
                break;
            };
            cur = b;
        }

        Some((
            cur,
            &input[..(input.as_bytes().len() - cur.as_bytes().len())],
        ))
    }
}

/// Checks whether `input` starts with `s`.
fn str(s: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
    move |input| input.starts_with(s).then(|| &input[s.as_bytes().len()..])
}

/// Checks whether `input` starts with `c`.
fn char(c: char) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| input.starts_with(c).then(|| &input[c.len_utf8()..])
}

/// Checks whether first [`char`] suits `check`.
///
/// [`char`]: fn@char
fn check_char(
    mut check: impl FnMut(char) -> bool,
) -> impl FnMut(&str) -> Option<LeftToParse<'_>> {
    move |input| {
        input
            .chars()
            .next()
            .and_then(|c| check(c).then(|| &input[c.len_utf8()..]))
    }
}

/// Checks whether first [`char`] of input is present in `chars`.
///
/// [`char`]: fn@char
fn one_of(chars: &str) -> impl FnMut(&str) -> Option<LeftToParse<'_>> + '_ {
    move |input: &str| chars.chars().find_map(|c| char(c)(input))
}

/// Parses any [`char`].
///
/// [`char`]: fn@char
fn any_char(input: &str) -> Option<LeftToParse<'_>> {
    input.chars().next().map(|c| &input[c.len_utf8()..])
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn text() {
        assert_eq!(format_string(""), Some(FormatString { formats: vec![] }));
        assert_eq!(
            format_string("test"),
            Some(FormatString { formats: vec![] }),
        );
        assert_eq!(
            format_string("Минск"),
            Some(FormatString { formats: vec![] }),
        );
        assert_eq!(format_string("��"), Some(FormatString { formats: vec![] }));
    }

    #[test]
    fn argument() {
        assert_eq!(
            format_string("{}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Integer(0)),
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{par}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Identifier("par")),
                    spec: None,
                }],
            }),
        );
        assert_eq!(
            format_string("{Минск}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: Some(Argument::Identifier("Минск")),
                    spec: None,
                }],
            }),
        );
    }

    #[test]
    fn spec() {
        assert_eq!(
            format_string("{:}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-<}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: <}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:+}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<-}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:+#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-<#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<-#}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:#0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:^<+#0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:1}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Integer(1)),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:1$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Integer(1))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("par"))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:-^-#0Минск$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("Минск"))),
                        precision: None,
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.*}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Star),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.0}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Integer(0))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.0$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Integer(0),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:.par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: >+#2$.par$}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Integer(2))),
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::Display,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:x?}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::LowerDebug,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{:E}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: None,
                        precision: None,
                        ty: Type::UpperExp,
                    }),
                }],
            }),
        );
        assert_eq!(
            format_string("{: >+#par$.par$X?}"),
            Some(FormatString {
                formats: vec![Format {
                    arg: None,
                    spec: Some(FormatSpec {
                        width: Some(Count::Parameter(Argument::Identifier("par"))),
                        precision: Some(Precision::Count(Count::Parameter(
                            Argument::Identifier("par"),
                        ))),
                        ty: Type::UpperDebug,
                    }),
                }],
            }),
        );
    }

    #[test]
    fn full() {
        assert_eq!(
            format_string("prefix{{{0:#?}postfix{par:-^par$.a$}}}"),
            Some(FormatString {
                formats: vec![
                    Format {
                        arg: Some(Argument::Integer(0)),
                        spec: Some(FormatSpec {
                            width: None,
                            precision: None,
                            ty: Type::Debug,
                        }),
                    },
                    Format {
                        arg: Some(Argument::Identifier("par")),
                        spec: Some(FormatSpec {
                            width: Some(Count::Parameter(Argument::Identifier("par"))),
                            precision: Some(Precision::Count(Count::Parameter(
                                Argument::Identifier("a"),
                            ))),
                            ty: Type::Display,
                        }),
                    },
                ],
            }),
        );
    }

    #[test]
    fn error() {
        assert_eq!(format_string("{"), None);
        assert_eq!(format_string("}"), None);
        assert_eq!(format_string("{{}"), None);
        assert_eq!(format_string("{:x?"), None);
        assert_eq!(format_string("{:.}"), None);
        assert_eq!(format_string("{:q}"), None);
        assert_eq!(format_string("{:par}"), None);
        assert_eq!(format_string("{⚙️}"), None);
    }
}

[ Verzeichnis aufwärts0.65unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge