use std::borrow::Cow;
use crate::{namespaces::ParseNamespace, parse::Parse, reference::Reference, QualifiedName};
use nom::{
branch::alt,
bytes::complete::{is_not, tag},
character::complete::char,
combinator::{map, opt, value},
multi::{many0, separated_list1},
sequence::{delimited, pair, tuple},
IResult,
};
#[derive(Clone, PartialEq)]
pub enum Prefix<'a> {
Default,
Prefix(Cow<'a, str>),
}
#[derive(Clone, PartialEq)]
pub enum Attribute<'a> {
Definition {
name: QualifiedName<'a>,
att_type: AttType<'a>,
default_decl: DefaultDecl<'a>,
},
Reference(Reference<'a>),
Instance {
name: QualifiedName<'a>,
value: Cow<'a, str>,
},
Required,
Implied,
Namespace {
prefix: Prefix<'a>,
uri: Cow<'a, str>,
},
}
impl<'a> Parse<'a> for Attribute<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Attribute<'a>> {
let (input, name) = Self::parse_name(input)?;
let (input, _) = Self::parse_eq(input)?;
let (input, value) = Self::parse_attvalue(input)?;
Ok((
input,
Attribute::Instance {
name: QualifiedName {
prefix: None,
local_part: name,
},
value,
},
))
}
}
impl<'a> ParseNamespace<'a> for Attribute<'a> {}
impl<'a> Attribute<'a> {
pub fn parse_definition(input: &'a str) -> IResult<&'a str, Attribute<'a>> {
let (input, (_, name, _, att_type, _, default_decl)) = tuple((
Self::parse_multispace1,
Self::parse_name,
Self::parse_multispace1,
AttType::parse,
Self::parse_multispace1,
DefaultDecl::parse,
))(input)?;
let attribute = Attribute::Definition {
name: QualifiedName {
prefix: None,
local_part: Cow::Owned(name.into()),
},
att_type,
default_decl,
};
Ok((input, attribute))
}
pub fn parse_qualified_definition(input: &'a str) -> IResult<&'a str, Attribute<'a>> {
let (input, (_, name, _, att_type, _, default_decl)) = tuple((
Self::parse_multispace1,
alt((
Self::parse_qualified_name,
map(Self::parse_namespace_attribute_name, |name| QualifiedName {
prefix: Some(Cow::Borrowed("xmlns")),
local_part: name,
}),
)),
Self::parse_multispace1,
AttType::parse,
Self::parse_multispace1,
DefaultDecl::parse,
))(input)?;
let attribute = Attribute::Definition {
name,
att_type,
default_decl,
};
Ok((input, attribute))
}
pub fn parse_attvalue(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
alt((
delimited(
tag("\""),
many0(alt((
map(is_not("<&\""), Cow::Borrowed),
map(Reference::parse, |reference| {
Cow::Owned(format!("{:?}", reference))
}),
))),
tag("\""),
),
delimited(
tag("'"),
many0(alt((
map(is_not("<&'"), Cow::Borrowed),
map(Reference::parse, |reference| {
Cow::Owned(format!("{:?}", reference))
}),
))),
char('\''),
),
))(input)
.map(|(remaining, contents)| (remaining, Cow::Owned(contents.concat())))
}
pub fn parse_qualified_attribute(input: &'a str) -> IResult<&'a str, Attribute<'a>> {
alt((
map(
tuple((
Self::parse_namespace_attribute_name,
Self::parse_eq,
Attribute::parse_attvalue,
)),
|(name, _, value)| {
if &*name == "xmlns" {
Attribute::Namespace {
prefix: Prefix::Default,
uri: value,
}
} else {
Attribute::Namespace {
prefix: Prefix::Prefix(name),
uri: value,
}
}
},
),
map(
tuple((
Self::parse_qualified_name,
Self::parse_eq,
Self::parse_attvalue,
)),
|(QualifiedName { prefix, local_part }, _, value)| Attribute::Instance {
name: QualifiedName { prefix, local_part },
value,
},
),
))(input)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TokenizedType {
ID,
IDREF,
IDREFS,
ENTITY,
ENTITIES,
NMTOKEN,
NMTOKENS,
}
impl TokenizedType {
fn parse(input: &str) -> IResult<&str, TokenizedType> {
alt((
value(TokenizedType::ID, tag("ID")),
value(TokenizedType::IDREF, tag("IDREF")),
value(TokenizedType::IDREFS, tag("IDREFS")),
value(TokenizedType::ENTITY, tag("ENTITY")),
value(TokenizedType::ENTITIES, tag("ENTITIES")),
value(TokenizedType::NMTOKEN, tag("NMTOKEN")),
value(TokenizedType::NMTOKENS, tag("NMTOKENS")),
))(input)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum AttType<'a> {
CDATA,
Tokenized(TokenizedType),
Enumerated {
notation: Option<Vec<Cow<'a, str>>>,
enumeration: Option<Vec<Cow<'a, str>>>,
},
}
impl<'a> Parse<'a> for AttType<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, att_type) = alt((
value(AttType::CDATA, tag("CDATA")),
map(TokenizedType::parse, AttType::Tokenized),
Self::parse_enumerated_type,
))(input)?;
Ok((input, att_type))
}
}
impl<'a> AttType<'a> {
fn parse_enumerated_type(input: &'a str) -> IResult<&'a str, AttType<'a>> {
alt((Self::parse_notation_type, Self::parse_enumeration))(input)
}
fn parse_notation_type(input: &'a str) -> IResult<&'a str, AttType<'a>> {
let (input, (_, _, names)) = tuple((
tag("NOTATION"),
Self::parse_multispace1,
delimited(
char('('),
delimited(
Self::parse_multispace0,
separated_list1(
delimited(Self::parse_multispace0, char('|'), Self::parse_multispace0),
Self::parse_name,
),
Self::parse_multispace0,
),
char(')'),
),
))(input)?;
let names = names.into_iter().collect();
Ok((
input,
AttType::Enumerated {
notation: Some(names),
enumeration: None,
},
))
}
fn parse_enumeration(input: &'a str) -> IResult<&'a str, AttType<'a>> {
let mut parser = delimited(
char('('),
separated_list1(
tuple((Self::parse_multispace0, char('|'), Self::parse_multispace0)),
Self::parse_nmtoken,
),
char(')'),
);
let (input, enumeration) = parser(input)?;
Ok((
input,
AttType::Enumerated {
notation: None,
enumeration: Some(enumeration),
},
))
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum DefaultDecl<'a> {
Required,
Implied,
Fixed(Cow<'a, str>),
Value(Cow<'a, str>),
}
impl<'a> Parse<'a> for DefaultDecl<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
alt((
value(DefaultDecl::Required, tag("#REQUIRED")),
value(DefaultDecl::Implied, tag("#IMPLIED")),
map(
pair(
opt(tuple((tag("#FIXED"), Self::parse_multispace1))),
Attribute::parse_attvalue,
),
|(fixed, attvalue)| match fixed {
Some(_) => DefaultDecl::Fixed(attvalue),
None => DefaultDecl::Value(attvalue),
},
),
))(input)
}
}