tamer: obj::xmle::xir::tree: Parse Text into Element

This simply adds support for Text nodes as a child of Element.  This support
unit tests for the upcoming change for xmle fragments.
main
Mike Gerwitz 2021-10-08 16:16:33 -04:00
parent f0f6f89745
commit 929a6c9815
2 changed files with 90 additions and 1 deletions

View File

@ -191,7 +191,7 @@
//! For more information,
//! see [`AttrParts`].
use super::{AttrValue, QName, Token};
use super::{AttrValue, QName, Text, Token};
use crate::span::Span;
use std::{fmt::Display, mem::take};
@ -217,6 +217,12 @@ pub enum Tree {
/// XML element.
Element(Element),
/// Text node.
///
/// A text node cannot contain other [`Tree`] elements;
/// sibling text nodes must exist within an [`Element`].
Text(Text, Span),
/// This variant exists purely because `#[non_exhaustive]` has no effect
/// within the crate.
///
@ -237,6 +243,16 @@ impl Into<Option<Element>> for Tree {
}
}
impl Into<Option<Text>> for Tree {
#[inline]
fn into(self) -> Option<Text> {
match self {
Self::Text(text, _) => Some(text),
_ => None,
}
}
}
impl Tree {
/// Yield a reference to the inner value if it is an [`Element`],
/// otherwise [`None`].
@ -260,6 +276,23 @@ impl Tree {
pub fn is_element(&self) -> bool {
matches!(self, Self::Element(_))
}
/// Yield a reference to the inner value if it is a [`Text`],
/// otherwise [`None`].
#[inline]
pub fn as_text<'a>(&'a self) -> Option<&'a Text> {
match self {
Self::Text(text, _) => Some(text),
_ => None,
}
}
/// Yield the inner value if it is a [`Text`],
/// otherwise [`None`].
#[inline]
pub fn into_text(self) -> Option<Text> {
self.into()
}
}
/// Element node.
@ -595,6 +628,20 @@ impl Stack {
_ => todo! {},
})
}
/// Appends a text node as a child of an element.
///
/// This is valid only for a [`Stack::BuddingElement`].
fn text(self, value: Text, span: Span) -> Result<Self> {
Ok(match self {
Self::BuddingElement(mut ele) => {
ele.element.children.push(Tree::Text(value, span));
Self::BuddingElement(ele)
}
_ => todo! {},
})
}
}
/// State while parsing a XIR token stream into a tree.
@ -669,6 +716,7 @@ impl ParserState {
stack.push_attr_value(value, span)
}
Token::AttrValue(value, span) => stack.close_attr(value, span),
Token::Text(value, span) => stack.text(value, span),
todo => Err(ParseError::Todo(todo, stack)),
}

View File

@ -31,6 +31,8 @@ lazy_static! {
}
mod tree {
use crate::ir::xir::Text;
use super::*;
#[test]
@ -45,6 +47,20 @@ mod tree {
let tree = Tree::Element(ele.clone());
assert_eq!(Some(&ele), tree.as_element());
assert_eq!(None, tree.as_text());
}
#[test]
fn text_from_tree() {
let text = Text::Escaped("foo".intern());
let tree = Tree::Text(text, *S);
assert!(!tree.is_element());
assert_eq!(None, tree.as_element());
assert_eq!(None, tree.clone().into_element());
assert_eq!(Some(&text), tree.as_text());
assert_eq!(Some(text), tree.into_text());
}
}
@ -278,6 +294,31 @@ fn element_with_child_with_attributes() {
assert_eq!(sut.next(), None);
}
#[test]
fn element_with_text() {
let parent = "parent".unwrap_into();
let text = Text::Escaped("inner text".into());
let toks = [
Token::Open(parent, *S),
Token::Text(text, *S2),
Token::Close(Some(parent), *S3),
]
.into_iter();
let expected = Element {
name: parent,
attrs: AttrList::new(),
children: vec![Tree::Text(text, *S2)],
span: (*S, *S3),
};
let mut sut = parser_from(toks);
assert_eq!(sut.next(), Some(Ok(Tree::Element(expected))));
assert_eq!(sut.next(), None);
}
#[test]
fn parser_from_filters_incomplete() {
let name = ("ns", "elem").unwrap_into();