use super::{declaration_content::DeclarationContent, external_id::ExternalID};
use crate::{
attribute::Attribute,
namespaces::{ParseNamespace, QualifiedName},
parse::Parse,
processing_instruction::ProcessingInstruction,
reference::{ParseReference, Reference},
};
use nom::{
branch::alt,
bytes::complete::{is_not, tag},
combinator::{map, opt},
multi::many0,
sequence::{delimited, tuple},
IResult,
};
use std::borrow::Cow;
#[derive(Clone, PartialEq)]
pub enum EntityValue<'a> {
Value(Cow<'a, str>),
Reference(Reference<'a>),
PerameterReference(Reference<'a>),
}
#[derive(Clone, PartialEq)]
pub struct GeneralEntityDeclaration<'a> {
pub name: Cow<'a, str>,
pub entity_def: EntityDefinition<'a>,
}
#[derive(Clone, PartialEq)]
pub enum EntityDefinition<'a> {
EntityValue(EntityValue<'a>),
External {
id: ExternalID<'a>,
n_data: Option<Cow<'a, str>>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParameterEntityDefinition<'a> {
EntityValue(EntityValue<'a>),
ExternalID(ExternalID<'a>),
}
#[derive(Clone, PartialEq)]
pub enum EntityDeclaration<'a> {
General(GeneralEntityDeclaration<'a>),
Parameter(ParameterEntityDefinition<'a>),
}
#[derive(Clone, PartialEq)]
pub enum InternalSubset<'a> {
Element {
name: QualifiedName<'a>,
content_spec: Option<DeclarationContent<'a>>,
},
AttList {
name: QualifiedName<'a>,
att_defs: Option<Vec<Attribute<'a>>>, },
Entity(EntityDeclaration<'a>),
DeclSep(Reference<'a>),
ProcessingInstruction(ProcessingInstruction<'a>),
}
impl<'a> Parse<'a> for InternalSubset<'a> {}
impl<'a> ParseNamespace<'a> for InternalSubset<'a> {}
impl<'a> InternalSubset<'a> {
pub fn parse_internal_subset(input: &'a str) -> IResult<&'a str, Vec<InternalSubset<'a>>> {
let mut parsed: Vec<InternalSubset<'a>> = Vec::new();
let mut current_input = input;
loop {
let original_input = current_input; match Self::parse_markup_decl(current_input) {
Ok((new_input, markup_decl)) => {
parsed.push(markup_decl);
current_input = new_input;
}
Err(nom::Err::Error(_)) => {
match Self::parse_decl_sep(current_input) {
Ok((new_input, maybe_decl_sep)) => {
if let Some(decl_sep) = maybe_decl_sep {
parsed.push(decl_sep);
}
current_input = new_input;
}
Err(e) => {
return Err(e);
}
}
}
Err(e) => {
return Err(e);
}
}
if current_input == original_input {
break;
}
}
Ok((current_input, parsed))
}
fn parse_decl_sep(input: &'a str) -> IResult<&'a str, Option<InternalSubset<'a>>> {
match Self::parse_multispace0(input) {
Ok((new_input, _)) => Ok((new_input, None)),
Err(nom::Err::Error(_)) => {
match Reference::parse_parameter_reference(input) {
Ok((new_input, reference)) => {
Ok((new_input, Some(InternalSubset::DeclSep(reference))))
}
Err(e) => Err(e),
}
}
Err(e) => Err(e),
}
}
fn parse_element_declaration(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ELEMENT")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_name(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, content_spec) = DeclarationContent::parse_spec(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::Element {
name: QualifiedName {
prefix: None,
local_part: name,
},
content_spec: Some(content_spec),
},
))
}
fn parse_qualified_element_declaration(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ELEMENT")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_qualified_name(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, content_spec) = DeclarationContent::parse_spec(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::Element {
name,
content_spec: Some(content_spec),
},
))
}
fn parse_processing_instruction(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, processing_instruction) = ProcessingInstruction::parse(input)?;
Ok((
input,
InternalSubset::ProcessingInstruction(processing_instruction),
))
}
pub fn parse_attlist_declaration(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ATTLIST")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_name(input)?;
let name = QualifiedName {
prefix: None,
local_part: name,
};
let (input, att_defs) = many0(Attribute::parse_definition)(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::AttList {
name,
att_defs: Some(att_defs),
},
))
}
pub fn parse_qualified_attlist_declaration(
input: &'a str,
) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ATTLIST")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_qualified_name(input)?;
let (input, att_defs) = many0(Attribute::parse_definition)(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::AttList {
name,
att_defs: Some(att_defs),
},
))
}
fn parse_entity(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
alt((
Self::parse_general_entity_declaration,
Self::parse_parameter_entity_declaration,
))(input)
}
fn parse_general_entity_declaration(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ENTITY")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_name(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, entity_def) = Self::parse_entity_def(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::Entity(EntityDeclaration::General(GeneralEntityDeclaration {
name,
entity_def,
})),
))
}
fn parse_parameter_entity_declaration(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
let (input, _) = tag("<!ENTITY")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, _) = tag("%")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_name(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, pedef) = Self::parse_parameter_definition(input)?;
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag(">")(input)?;
Ok((
input,
InternalSubset::Entity(EntityDeclaration::Parameter(pedef)),
))
}
fn parse_parameter_definition(
input: &'a str,
) -> IResult<&'a str, ParameterEntityDefinition<'a>> {
alt((
map(
Self::parse_entity_value,
ParameterEntityDefinition::EntityValue,
),
map(ExternalID::parse, ParameterEntityDefinition::ExternalID),
))(input)
}
fn parse_entity_def(input: &'a str) -> IResult<&'a str, EntityDefinition<'a>> {
alt((
map(Self::parse_entity_value, EntityDefinition::EntityValue),
map(
tuple((ExternalID::parse, opt(Self::parse_ndata_declaration))),
|(id, n_data)| EntityDefinition::External { id, n_data },
),
))(input)
}
fn parse_ndata_declaration(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, _) = Self::parse_multispace1(input)?;
let (input, _) = tag("NDATA")(input)?;
let (input, _) = Self::parse_multispace1(input)?;
let (input, name) = Self::parse_name(input)?;
Ok((input, name))
}
fn parse_entity_value(input: &'a str) -> IResult<&'a str, EntityValue<'a>> {
let (input, data) = alt((
delimited(tag("\""), many0(Self::parse_entity_content), tag("\"")),
delimited(tag("\'"), many0(Self::parse_entity_content), tag("\'")),
))(input)?;
let value = data.into_iter().collect::<String>();
Ok((input, EntityValue::Value(Cow::Owned(value))))
}
fn parse_entity_content(input: &'a str) -> IResult<&'a str, String> {
alt((
map(Reference::parse, |reference| match reference {
Reference::EntityRef(value) => value.into_owned(),
Reference::CharRef { value, .. } => value.into_owned(),
}),
map(is_not("%&\"'"), ToString::to_string),
))(input)
}
fn parse_perameter_definition(
input: &'a str,
) -> IResult<&'a str, ParameterEntityDefinition<'a>> {
alt((
map(
Self::parse_entity_value,
ParameterEntityDefinition::EntityValue,
),
map(ExternalID::parse, ParameterEntityDefinition::ExternalID),
))(input)
}
fn parse_markup_decl(input: &'a str) -> IResult<&'a str, InternalSubset<'a>> {
alt((
Self::parse_element_declaration,
Self::parse_attlist_declaration,
Self::parse_entity,
Self::parse_processing_instruction,
))(input)
}
}