nom_xml/prolog/
textdecl.rs

1use nom::{
2    branch::alt,
3    character::complete::{alpha1, alphanumeric1, digit1},
4    combinator::{map, opt},
5    multi::many0,
6    sequence::{delimited, pair, preceded, tuple},
7};
8
9use crate::{parse::Parse, tag, IResult};
10
11#[derive(Clone, PartialEq, Eq)]
12pub struct TextDecl {
13    pub version: Option<String>,
14    pub encoding: String,
15}
16impl<'a> Parse<'a> for TextDecl {
17    type Args = ();
18    type Output = IResult<&'a str, Self>;
19    //[77] TextDecl ::='<?xml' VersionInfo? EncodingDecl S? '?>'
20    fn parse(input: &'a str, _args: Self::Args) -> Self::Output {
21        map(
22            tuple((
23                tag("<?xml"),
24                opt(Self::parse_version_info),
25                Self::parse_encoding_decl,
26                Self::parse_multispace0,
27                tag("?>"),
28            )),
29            |(_start, version, encoding, _whitespace, _end)| Self { version, encoding },
30        )(input)
31    }
32}
33
34impl TextDecl {
35    // [24] VersionInfo	::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
36    fn parse_version_info(input: &str) -> IResult<&str, String> {
37        map(
38            tuple((
39                Self::parse_multispace1,
40                tag("version"),
41                Self::parse_eq,
42                alt((
43                    delimited(tag("'"), Self::parse_version_num, tag("'")),
44                    delimited(tag("\""), Self::parse_version_num, tag("\"")),
45                )),
46            )),
47            |(_whitespace, _version_literal, _eq, version)| version,
48        )(input)
49    }
50
51    // [26] VersionNum	::= '1.' [0-9]+
52    fn parse_version_num(input: &str) -> IResult<&str, String> {
53        map(preceded(tag("1."), digit1), |version| {
54            format!("1.{}", version)
55        })(input)
56    }
57    // [80] EncodingDecl	::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
58    fn parse_encoding_decl(input: &str) -> IResult<&str, String> {
59        map(
60            tuple((
61                Self::parse_multispace1,
62                tag("encoding"),
63                Self::parse_eq,
64                alt((
65                    delimited(tag("'"), Self::parse_enc_name, tag("'")),
66                    delimited(tag("\""), Self::parse_enc_name, tag("\"")),
67                )),
68            )),
69            |(_whitespace, _encoding_literal, _eq, encoding)| encoding,
70        )(input)
71    }
72
73    // [81] EncName	::= [A-Za-z] ([A-Za-z0-9._] | '-')*
74    fn parse_enc_name(input: &str) -> IResult<&str, String> {
75        map(
76            pair(
77                alt((alpha1, tag("-"))),
78                many0(alt((alphanumeric1, tag("."), tag("_"), tag("-")))),
79            ),
80            |(first, rest)| format!("{}{}", first, rest.join("")),
81        )(input)
82    }
83}