use std::borrow::Cow;
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{alpha1, alphanumeric1, digit1},
combinator::opt,
multi::many0,
sequence::delimited,
IResult,
};
use crate::parse::Parse;
#[derive(Clone, PartialEq)]
pub struct XmlDecl<'a> {
pub version: Cow<'a, str>,
pub encoding: Option<Cow<'a, str>>,
pub standalone: Option<Cow<'a, str>>,
}
impl<'a> Parse<'a> for XmlDecl<'a> {
fn parse(input: &'a str) -> IResult<&'a str, XmlDecl<'a>> {
let (input, _) = tag("<?xml")(input)?;
let (input, version) = Self::parse_version_info(input)?;
let (input, encoding) = opt(Self::parse_encoding_decl)(input)?;
let (input, standalone) = opt(Self::parse_sd_decl)(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag("?>")(input)?;
Ok((
input,
Self {
version,
encoding,
standalone,
},
))
}
}
impl<'a> XmlDecl<'a> {
fn parse_version_info(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, _) = Self::parse_multispace1(input)?;
let (input, _) = tag("version")(input)?;
let (input, _) = Self::parse_eq(input)?;
let (input, version) = alt((
delimited(tag("'"), Self::parse_version_num, tag("'")),
delimited(tag("\""), Self::parse_version_num, tag("\"")),
))(input)?;
Ok((input, version))
}
fn parse_version_num(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, _) = tag("1.")(input)?;
let (input, version) = digit1(input)?;
let version_with_prefix = format!("1.{}", version);
Ok((input, version_with_prefix.into()))
}
fn parse_encoding_decl(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, _) = Self::parse_multispace1(input)?;
let (input, _) = tag("encoding")(input)?;
let (input, _) = Self::parse_eq(input)?;
let (input, encoding) = alt((
delimited(tag("'"), Self::parse_enc_name, tag("'")),
delimited(tag("\""), Self::parse_enc_name, tag("\"")),
))(input)?;
Ok((input, encoding))
}
fn parse_enc_name(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, first) = alt((alpha1, tag("-")))(input)?;
let (input, rest) = many0(alt((alphanumeric1, tag("."), tag("_"), tag("-"))))(input)?;
Ok((input, format!("{}{}", first, rest.join("")).into()))
}
fn parse_sd_decl(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, _) = Self::parse_multispace1(input)?;
let (input, _) = tag("standalone")(input)?;
let (input, _) = Self::parse_eq(input)?;
let (input, standalone) = alt((
delimited(tag("'"), alt((tag("yes"), tag("no"))), tag("'")),
delimited(tag("\""), alt((tag("yes"), tag("no"))), tag("\"")),
))(input)?;
Ok((input, standalone.into()))
}
}