use crate::parser::bang::parse_bang;
use crate::parser::chars::is_name_start_char;
use crate::parser::error::Result;
use crate::parser::pi::parse_pi;
use crate::parser::string::{parse_string, StringType};
use crate::parser::{parse_name, Iter};
use crate::{Element, Node};
pub(crate) fn parse_element(iter: &mut Iter<'_>) -> Result<Element> {
expect!(iter, '<')?;
iter.advance_or_die()?;
let name = parse_name(iter)?;
let mut element = Element::from_name(name);
iter.skip_whitespace()?;
if iter.is('/') {
iter.advance_or_die()?;
expect!(iter, '>')?;
iter.advance();
return Ok(element);
}
if iter.is_name_start_char() {
parse_attributes(iter, &mut element)?;
}
if iter.is('/') {
iter.advance_or_die()?;
expect!(iter, '>')?;
iter.advance();
return Ok(element);
}
expect!(iter, '>')?;
iter.advance_or_die()?; parse_children(iter, &mut element)?;
debug_assert_eq!('>', iter.st.c);
iter.advance(); debug_assert_ne!('>', iter.st.c);
Ok(element)
}
fn parse_attributes(iter: &mut Iter<'_>, element: &mut Element) -> Result<()> {
loop {
iter.skip_whitespace()?;
if iter.is('/') || iter.is('>') {
break;
}
let key = if iter.is_name_start_char() {
parse_name(iter)?
} else {
String::default()
};
iter.skip_whitespace()?;
expect!(iter, '=')?;
iter.advance_or_die()?;
iter.skip_whitespace()?;
let (start, string_type) = attribute_start_quote(iter)?;
iter.advance_or_die()?;
let value = parse_attribute_value(iter, string_type)?;
expect!(iter, start)?;
element.add_attribute(key, value);
if !iter.advance() {
break;
}
}
Ok(())
}
fn attribute_start_quote(iter: &Iter<'_>) -> Result<(char, StringType)> {
let c = iter.st.c;
match c {
'\'' => Ok((c, StringType::AttributeSingle)),
'"' => Ok((c, StringType::AttributeDouble)),
_ => parse_err!(
iter,
"expected attribute value to start with either a single or double quote, got '{}'",
c
),
}
}
fn parse_attribute_value(iter: &mut Iter<'_>, string_type: StringType) -> Result<String> {
debug_assert!(matches!(
string_type,
StringType::AttributeDouble | StringType::AttributeSingle
));
parse_string(iter, string_type)
}
fn parse_children(iter: &mut Iter<'_>, parent: &mut Element) -> Result<()> {
loop {
iter.skip_whitespace()?;
if iter.is('<') {
let lt_parse = parse_lt(iter, parent)?;
match lt_parse {
LtParse::EndTag => {
return Ok(());
}
LtParse::Skip => {
}
LtParse::Some(node) => match node {
Node::Element(elem) => parent.add_child(elem),
Node::Text(text) => parent.add_text(text),
Node::CData(cdata) => parent
.add_cdata(cdata)
.map_err(|e| create_parser_error!(&iter.st, "{}", e))?,
Node::Comment(comment) => parent
.add_comment(comment)
.map_err(|e| create_parser_error!(&iter.st, "{}", e))?,
Node::Pi(pi) => parent.add_pi(pi),
Node::DocType(_) => panic!("doctype unsupported"),
},
LtParse::DocType(_) => return parse_err!(iter, "doctype not allowed here"),
}
} else {
let text = parse_text(iter)?;
if !text.is_empty() {
parent.add_text(text);
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(super) enum LtParse {
EndTag,
#[allow(dead_code)] Skip,
Some(Node),
DocType(String),
}
fn parse_lt(iter: &mut Iter<'_>, parent: &mut Element) -> Result<LtParse> {
debug_assert_eq!('<', iter.st.c);
let next = iter.peek_or_die()?;
if is_name_start_char(next) {
let element = parse_element(iter)?;
debug_assert_ne!('>', iter.st.c);
return Ok(LtParse::Some(Node::Element(element)));
}
match next {
'/' => {
parse_end_tag_name(iter, parent)?;
Ok(LtParse::EndTag)
}
'?' => {
let pi = parse_pi(iter)?;
Ok(LtParse::Some(Node::Pi(pi)))
}
'!' => parse_bang(iter),
_ => {
iter.advance();
parse_err!(iter, "unexpected char following '<'")
}
}
}
fn parse_end_tag_name(iter: &mut Iter<'_>, parent: &Element) -> Result<()> {
expect!(iter, '<')?;
iter.advance_or_die()?;
expect!(iter, '/')?;
iter.advance_or_die()?;
iter.skip_whitespace()?;
iter.expect_name_start_char()?;
let mut name = String::default();
name.push(iter.st.c);
loop {
iter.advance_or_die()?;
if iter.is('>') || iter.is_whitespace() {
break;
} else if iter.is_name_char() {
name.push(iter.st.c);
} else {
return parse_err!(iter);
}
}
iter.skip_whitespace()?;
expect!(iter, '>')?;
if name != parent.fullname() {
return parse_err!(
iter,
"closing element name '{}' does not match openeing element name '{}'",
name,
parent.fullname()
);
}
Ok(())
}
fn parse_text(iter: &mut Iter<'_>) -> Result<String> {
parse_string(iter, StringType::Element)
}
#[test]
fn parse_attribute_value_test_1() {
let mut iter = Iter::new(r#"some "fun" attribute value'"#).unwrap();
let value = parse_attribute_value(&mut iter, StringType::AttributeSingle).unwrap();
assert_eq!(value, r#"some "fun" attribute value"#);
assert_eq!(iter.st.c, '\'');
}