use nom::{
branch::alt,
character::complete::{alpha1, alphanumeric1, digit1},
combinator::{map, opt},
multi::many0,
sequence::{delimited, pair, preceded, tuple},
};
use crate::{parse::Parse, tag, IResult};
#[derive(Clone, PartialEq, Eq)]
pub struct TextDecl {
pub version: Option<String>,
pub encoding: String,
}
impl<'a> Parse<'a> for TextDecl {
type Args = ();
type Output = IResult<&'a str, Self>;
fn parse(input: &'a str, _args: Self::Args) -> Self::Output {
map(
tuple((
tag("<?xml"),
opt(Self::parse_version_info),
Self::parse_encoding_decl,
Self::parse_multispace0,
tag("?>"),
)),
|(_start, version, encoding, _whitespace, _end)| Self { version, encoding },
)(input)
}
}
impl TextDecl {
fn parse_version_info(input: &str) -> IResult<&str, String> {
map(
tuple((
Self::parse_multispace1,
tag("version"),
Self::parse_eq,
alt((
delimited(tag("'"), Self::parse_version_num, tag("'")),
delimited(tag("\""), Self::parse_version_num, tag("\"")),
)),
)),
|(_whitespace, _version_literal, _eq, version)| version,
)(input)
}
fn parse_version_num(input: &str) -> IResult<&str, String> {
map(preceded(tag("1."), digit1), |version| {
format!("1.{}", version)
})(input)
}
fn parse_encoding_decl(input: &str) -> IResult<&str, String> {
map(
tuple((
Self::parse_multispace1,
tag("encoding"),
Self::parse_eq,
alt((
delimited(tag("'"), Self::parse_enc_name, tag("'")),
delimited(tag("\""), Self::parse_enc_name, tag("\"")),
)),
)),
|(_whitespace, _encoding_literal, _eq, encoding)| encoding,
)(input)
}
fn parse_enc_name(input: &str) -> IResult<&str, String> {
map(
pair(
alt((alpha1, tag("-"))),
many0(alt((alphanumeric1, tag("."), tag("_"), tag("-")))),
),
|(first, rest)| format!("{}{}", first, rest.join("")),
)(input)
}
}