use std::{fmt, hash, marker::PhantomData};
use super::{QName, raw};
pub struct Storage<'d> {
storage: &'d raw::Storage,
identity: usize,
}
impl<'d> Storage<'d> {
pub fn new(storage: &'d raw::Storage, identity: usize) -> Storage<'d> {
Storage { storage, identity }
}
pub fn create_element<'n, N>(&'d self, name: N) -> Element<'d>
where
N: Into<QName<'n>>,
{
Element::wrap(self.storage.create_element(name), self.identity)
}
pub fn create_attribute<'n, N>(&'d self, name: N, value: &str) -> Attribute<'d>
where
N: Into<QName<'n>>,
{
Attribute::wrap(self.storage.create_attribute(name, value), self.identity)
}
pub fn create_text(&'d self, text: &str) -> Text<'d> {
Text::wrap(self.storage.create_text(text), self.identity)
}
pub fn create_comment(&'d self, text: &str) -> Comment<'d> {
Comment::wrap(self.storage.create_comment(text), self.identity)
}
pub fn create_processing_instruction(
&'d self,
target: &str,
value: Option<&str>,
) -> ProcessingInstruction<'d> {
ProcessingInstruction::wrap(
self.storage.create_processing_instruction(target, value),
self.identity,
)
}
pub fn element_set_name<'n, N>(&self, element: Element<'_>, name: N)
where
N: Into<QName<'n>>,
{
element.assert_identity(self.identity);
self.storage.element_set_name(element.node, name)
}
pub fn text_set_text(&self, text: Text<'_>, new_text: &str) {
text.assert_identity(self.identity);
self.storage.text_set_text(text.node, new_text)
}
pub fn comment_set_text(&self, comment: Comment<'_>, new_text: &str) {
comment.assert_identity(self.identity);
self.storage.comment_set_text(comment.node, new_text)
}
pub fn processing_instruction_set_target(
&self,
pi: ProcessingInstruction<'_>,
new_target: &str,
) {
pi.assert_identity(self.identity);
self.storage
.processing_instruction_set_target(pi.node, new_target)
}
pub fn processing_instruction_set_value(
&self,
pi: ProcessingInstruction<'_>,
new_value: Option<&str>,
) {
pi.assert_identity(self.identity);
self.storage
.processing_instruction_set_value(pi.node, new_value)
}
}
pub struct Connections<'d> {
connections: &'d raw::Connections,
identity: usize,
}
impl<'d> Connections<'d> {
pub fn new(connections: &'d raw::Connections, identity: usize) -> Connections<'d> {
Connections {
connections,
identity,
}
}
pub fn root(&self) -> Root<'d> {
Root::wrap(self.connections.root(), self.identity)
}
pub fn element_parent(&self, child: Element<'d>) -> Option<ParentOfChild<'d>> {
child.assert_identity(self.identity);
self.connections
.element_parent(child.node)
.map(|parent| ParentOfChild::wrap(parent, self.identity))
}
pub fn text_parent(&self, child: Text<'d>) -> Option<Element<'d>> {
child.assert_identity(self.identity);
self.connections
.text_parent(child.node)
.map(|parent| Element::wrap(parent, self.identity))
}
pub fn comment_parent(&self, child: Comment<'d>) -> Option<ParentOfChild<'d>> {
child.assert_identity(self.identity);
self.connections
.comment_parent(child.node)
.map(|parent| ParentOfChild::wrap(parent, self.identity))
}
pub fn processing_instruction_parent(
&self,
child: ProcessingInstruction<'d>,
) -> Option<ParentOfChild<'d>> {
child.assert_identity(self.identity);
self.connections
.processing_instruction_parent(child.node)
.map(|parent| ParentOfChild::wrap(parent, self.identity))
}
pub fn append_root_child<C>(&mut self, child: C)
where
C: Into<ChildOfRoot<'d>>,
{
let child = child.into();
child.assert_identity(self.identity);
self.connections.append_root_child(child.as_raw())
}
pub fn append_element_child<C>(&mut self, parent: Element<'d>, child: C)
where
C: Into<ChildOfElement<'d>>,
{
let child = child.into();
parent.assert_identity(self.identity);
child.assert_identity(self.identity);
self.connections
.append_element_child(parent.node, child.as_raw())
}
pub fn root_children(&self) -> RootChildren<'_> {
RootChildren {
snapshot: Snapshot::new(self.connections.root_children().to_vec(), self.identity),
}
}
pub fn element_children(&self, parent: Element<'_>) -> ElementChildren<'_> {
parent.assert_identity(self.identity);
ElementChildren {
snapshot: Snapshot::new(
self.connections.element_children(parent.node).to_vec(),
self.identity,
),
}
}
pub fn element_preceding_siblings(&self, element: Element<'_>) -> Siblings<'_> {
element.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.element_preceding_siblings(element.node)
.collect(),
self.identity,
),
}
}
pub fn element_following_siblings(&self, element: Element<'_>) -> Siblings<'_> {
element.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.element_following_siblings(element.node)
.collect(),
self.identity,
),
}
}
pub fn text_preceding_siblings(&self, text: Text<'_>) -> Siblings<'_> {
text.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.text_preceding_siblings(text.node)
.collect(),
self.identity,
),
}
}
pub fn text_following_siblings(&self, text: Text<'_>) -> Siblings<'_> {
text.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.text_following_siblings(text.node)
.collect(),
self.identity,
),
}
}
pub fn comment_preceding_siblings(&self, comment: Comment<'_>) -> Siblings<'_> {
comment.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.comment_preceding_siblings(comment.node)
.collect(),
self.identity,
),
}
}
pub fn comment_following_siblings(&self, comment: Comment<'_>) -> Siblings<'_> {
comment.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.comment_following_siblings(comment.node)
.collect(),
self.identity,
),
}
}
pub fn processing_instruction_preceding_siblings(
&self,
pi: ProcessingInstruction<'_>,
) -> Siblings<'_> {
pi.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.processing_instruction_preceding_siblings(pi.node)
.collect(),
self.identity,
),
}
}
pub fn processing_instruction_following_siblings(
&self,
pi: ProcessingInstruction<'_>,
) -> Siblings<'_> {
pi.assert_identity(self.identity);
Siblings {
snapshot: Snapshot::new(
self.connections
.processing_instruction_following_siblings(pi.node)
.collect(),
self.identity,
),
}
}
pub fn attribute_parent(&self, attribute: Attribute<'d>) -> Option<Element<'d>> {
attribute.assert_identity(self.identity);
self.connections
.attribute_parent(attribute.node)
.map(|parent| Element::wrap(parent, self.identity))
}
pub fn attributes(&self, parent: Element<'d>) -> Attributes<'d> {
parent.assert_identity(self.identity);
Attributes {
snapshot: Snapshot::new(
self.connections.attributes(parent.node).to_vec(),
self.identity,
),
}
}
pub fn set_attribute(&mut self, parent: Element<'d>, attribute: Attribute<'d>) {
parent.assert_identity(self.identity);
attribute.assert_identity(self.identity);
self.connections.set_attribute(parent.node, attribute.node);
}
pub fn attribute_value(&self, parent: Element<'d>, name: &str) -> Option<&'d str> {
parent.assert_identity(self.identity);
self.connections.attribute(parent.node, name).map(|a| {
let a_r = unsafe { &*a };
a_r.value()
})
}
}
struct Snapshot<'d, T> {
iter: std::vec::IntoIter<T>,
identity: usize,
lifetime: PhantomData<&'d raw::Connections>,
}
impl<T> Snapshot<'_, T> {
fn new(values: Vec<T>, identity: usize) -> Self {
Self {
iter: values.into_iter(),
identity,
lifetime: PhantomData,
}
}
}
pub struct RootChildren<'d> {
snapshot: Snapshot<'d, raw::ChildOfRoot>,
}
impl<'d> Iterator for RootChildren<'d> {
type Item = ChildOfRoot<'d>;
fn next(&mut self) -> Option<ChildOfRoot<'d>> {
self.snapshot
.iter
.next()
.map(|child| ChildOfRoot::wrap(child, self.snapshot.identity))
}
}
pub struct ElementChildren<'d> {
snapshot: Snapshot<'d, raw::ChildOfElement>,
}
impl<'d> Iterator for ElementChildren<'d> {
type Item = ChildOfElement<'d>;
fn next(&mut self) -> Option<ChildOfElement<'d>> {
self.snapshot
.iter
.next()
.map(|child| ChildOfElement::wrap(child, self.snapshot.identity))
}
}
pub struct Attributes<'d> {
snapshot: Snapshot<'d, *mut raw::Attribute>,
}
impl<'d> Iterator for Attributes<'d> {
type Item = Attribute<'d>;
fn next(&mut self) -> Option<Attribute<'d>> {
self.snapshot
.iter
.next()
.map(|attribute| Attribute::wrap(attribute, self.snapshot.identity))
}
}
pub struct Siblings<'d> {
snapshot: Snapshot<'d, raw::ChildOfElement>,
}
impl<'d> Iterator for Siblings<'d> {
type Item = ChildOfElement<'d>;
fn next(&mut self) -> Option<ChildOfElement<'d>> {
self.snapshot
.iter
.next()
.map(|child| ChildOfElement::wrap(child, self.snapshot.identity))
}
}
macro_rules! node(
($name:ident, $raw:ty) => (
#[derive(Copy,Clone)]
pub struct $name<'d> {
node: *mut $raw,
identity: usize,
lifetime: PhantomData<Storage<'d>>,
}
impl<'d> $name<'d> {
fn wrap(node: *mut $raw, identity: usize) -> $name<'d> {
$name {
node,
identity,
lifetime: PhantomData,
}
}
#[allow(dead_code)]
fn assert_identity(self, identity: usize) {
assert!(
self.identity == identity,
"thin DOM nodes must belong to the same Package",
);
}
#[allow(dead_code)]
fn node(&self) -> &'d $raw { unsafe { &*self.node } }
}
impl<'d> PartialEq for $name<'d> {
fn eq(&self, other: &$name<'d>) -> bool {
self.node == other.node && self.identity == other.identity
}
}
impl<'d> Eq for $name<'d> {}
impl<'d> hash::Hash for $name<'d> {
fn hash<H>(&self, state: &mut H)
where H: hash::Hasher
{
self.node.hash(state);
self.identity.hash(state);
}
}
)
);
node!(Root, raw::Root);
impl<'d> fmt::Debug for Root<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Root")
}
}
node!(Element, raw::Element);
impl<'d> Element<'d> {
pub fn name(self) -> QName<'d> {
self.node().name()
}
}
impl<'d> fmt::Debug for Element<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Element {{ name: {:?} }}", self.name())
}
}
node!(Attribute, raw::Attribute);
impl<'d> Attribute<'d> {
pub fn name(&self) -> QName<'_> {
self.node().name()
}
pub fn value(&self) -> &str {
self.node().value()
}
}
impl<'d> fmt::Debug for Attribute<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Attribute {{ name: {:?}, value: {:?} }}",
self.name(),
self.value()
)
}
}
node!(Text, raw::Text);
impl<'d> Text<'d> {
pub fn text(&self) -> &str {
self.node().text()
}
}
impl<'d> fmt::Debug for Text<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Text {{ text: {:?} }}", self.text())
}
}
node!(Comment, raw::Comment);
impl<'d> Comment<'d> {
pub fn text(&self) -> &str {
self.node().text()
}
}
impl<'d> fmt::Debug for Comment<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Comment {{ text: {:?} }}", self.text())
}
}
node!(ProcessingInstruction, raw::ProcessingInstruction);
impl<'d> ProcessingInstruction<'d> {
pub fn target(self) -> &'d str {
self.node().target()
}
pub fn value(self) -> Option<&'d str> {
self.node().value()
}
}
impl<'d> fmt::Debug for ProcessingInstruction<'d> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ProcessingInstruction {{ target: {:?}, value: {:?} }}",
self.target(),
self.value()
)
}
}
macro_rules! unpack(
($enum_name:ident, $name:ident, $wrapper:ident, $inner:ident) => (
pub fn $name(self) -> Option<$inner<'d>> {
match self {
$enum_name::$wrapper(n) => Some(n),
_ => None,
}
}
)
);
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ChildOfRoot<'d> {
Element(Element<'d>),
Comment(Comment<'d>),
ProcessingInstruction(ProcessingInstruction<'d>),
}
impl<'d> ChildOfRoot<'d> {
unpack!(ChildOfRoot, element, Element, Element);
unpack!(ChildOfRoot, comment, Comment, Comment);
unpack!(
ChildOfRoot,
processing_instruction,
ProcessingInstruction,
ProcessingInstruction
);
pub fn wrap(node: raw::ChildOfRoot, identity: usize) -> ChildOfRoot<'d> {
match node {
raw::ChildOfRoot::Element(n) => ChildOfRoot::Element(Element::wrap(n, identity)),
raw::ChildOfRoot::Comment(n) => ChildOfRoot::Comment(Comment::wrap(n, identity)),
raw::ChildOfRoot::ProcessingInstruction(n) => {
ChildOfRoot::ProcessingInstruction(ProcessingInstruction::wrap(n, identity))
}
}
}
fn assert_identity(self, identity: usize) {
match self {
Self::Element(node) => node.assert_identity(identity),
Self::Comment(node) => node.assert_identity(identity),
Self::ProcessingInstruction(node) => node.assert_identity(identity),
}
}
pub fn as_raw(&self) -> raw::ChildOfRoot {
match *self {
ChildOfRoot::Element(n) => raw::ChildOfRoot::Element(n.node),
ChildOfRoot::Comment(n) => raw::ChildOfRoot::Comment(n.node),
ChildOfRoot::ProcessingInstruction(n) => {
raw::ChildOfRoot::ProcessingInstruction(n.node)
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ChildOfElement<'d> {
Element(Element<'d>),
Text(Text<'d>),
Comment(Comment<'d>),
ProcessingInstruction(ProcessingInstruction<'d>),
}
impl<'d> ChildOfElement<'d> {
unpack!(ChildOfElement, element, Element, Element);
unpack!(ChildOfElement, text, Text, Text);
unpack!(ChildOfElement, comment, Comment, Comment);
unpack!(
ChildOfElement,
processing_instruction,
ProcessingInstruction,
ProcessingInstruction
);
pub fn wrap(node: raw::ChildOfElement, identity: usize) -> ChildOfElement<'d> {
match node {
raw::ChildOfElement::Element(n) => ChildOfElement::Element(Element::wrap(n, identity)),
raw::ChildOfElement::Text(n) => ChildOfElement::Text(Text::wrap(n, identity)),
raw::ChildOfElement::Comment(n) => ChildOfElement::Comment(Comment::wrap(n, identity)),
raw::ChildOfElement::ProcessingInstruction(n) => {
ChildOfElement::ProcessingInstruction(ProcessingInstruction::wrap(n, identity))
}
}
}
fn assert_identity(self, identity: usize) {
match self {
Self::Element(node) => node.assert_identity(identity),
Self::Text(node) => node.assert_identity(identity),
Self::Comment(node) => node.assert_identity(identity),
Self::ProcessingInstruction(node) => node.assert_identity(identity),
}
}
pub fn as_raw(&self) -> raw::ChildOfElement {
match *self {
ChildOfElement::Element(n) => raw::ChildOfElement::Element(n.node),
ChildOfElement::Text(n) => raw::ChildOfElement::Text(n.node),
ChildOfElement::Comment(n) => raw::ChildOfElement::Comment(n.node),
ChildOfElement::ProcessingInstruction(n) => {
raw::ChildOfElement::ProcessingInstruction(n.node)
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ParentOfChild<'d> {
Root(Root<'d>),
Element(Element<'d>),
}
impl<'d> ParentOfChild<'d> {
unpack!(ParentOfChild, root, Root, Root);
unpack!(ParentOfChild, element, Element, Element);
pub fn wrap(node: raw::ParentOfChild, identity: usize) -> ParentOfChild<'d> {
match node {
raw::ParentOfChild::Root(n) => ParentOfChild::Root(Root::wrap(n, identity)),
raw::ParentOfChild::Element(n) => ParentOfChild::Element(Element::wrap(n, identity)),
}
}
}
macro_rules! conversion_trait(
($res_type:ident, {
$($leaf_type:ident => $variant:expr),*
}) => (
$(impl<'d> From<$leaf_type<'d>> for $res_type<'d>{
fn from(other: $leaf_type<'d>) -> $res_type<'d> {
$variant(other)
}
})*
)
);
conversion_trait!(
ChildOfRoot, {
Element => ChildOfRoot::Element,
Comment => ChildOfRoot::Comment,
ProcessingInstruction => ChildOfRoot::ProcessingInstruction
}
);
conversion_trait!(
ChildOfElement, {
Element => ChildOfElement::Element,
Text => ChildOfElement::Text,
Comment => ChildOfElement::Comment,
ProcessingInstruction => ChildOfElement::ProcessingInstruction
}
);
impl<'d> From<ChildOfRoot<'d>> for ChildOfElement<'d> {
fn from(val: ChildOfRoot<'d>) -> Self {
match val {
ChildOfRoot::Element(n) => ChildOfElement::Element(n),
ChildOfRoot::Comment(n) => ChildOfElement::Comment(n),
ChildOfRoot::ProcessingInstruction(n) => ChildOfElement::ProcessingInstruction(n),
}
}
}
#[cfg(test)]
mod test {
use super::{
super::{Package, QName},
ChildOfElement, ChildOfRoot, ParentOfChild,
};
macro_rules! assert_qname_eq(
($l:expr, $r:expr) => (assert_eq!(Into::<QName<'_>>::into($l), $r.into()));
);
#[test]
fn root_can_have_element_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("alpha");
c.append_root_child(element);
let children: Vec<_> = c.root_children().collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfRoot::Element(element));
}
#[test]
fn root_has_maximum_of_one_element_child() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let alpha = s.create_element("alpha");
let beta = s.create_element("beta");
c.append_root_child(alpha);
c.append_root_child(beta);
let children: Vec<_> = c.root_children().collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfRoot::Element(beta));
}
#[test]
fn root_can_have_comment_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let comment = s.create_comment("Now is the winter of our discontent.");
c.append_root_child(comment);
let children: Vec<_> = c.root_children().collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfRoot::Comment(comment));
}
#[test]
fn root_can_have_processing_instruction_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let pi = s.create_processing_instruction("device", None);
c.append_root_child(pi);
let children: Vec<_> = c.root_children().collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfRoot::ProcessingInstruction(pi));
}
#[test]
fn root_child_knows_its_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let alpha = s.create_element("alpha");
c.append_root_child(alpha);
assert_eq!(Some(ParentOfChild::Root(c.root())), c.element_parent(alpha));
}
#[test]
fn elements_can_have_element_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let alpha = s.create_element("alpha");
let beta = s.create_element("beta");
c.append_element_child(alpha, beta);
let children: Vec<_> = c.element_children(alpha).collect();
assert_eq!(children[0], ChildOfElement::Element(beta));
}
#[test]
fn element_children_are_ordered() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let greek = s.create_element("greek");
let alpha = s.create_element("alpha");
let omega = s.create_element("omega");
c.append_element_child(greek, alpha);
c.append_element_child(greek, omega);
let children: Vec<_> = c.element_children(greek).collect();
assert_eq!(children[0], ChildOfElement::Element(alpha));
assert_eq!(children[1], ChildOfElement::Element(omega));
}
#[test]
fn element_children_know_their_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let alpha = s.create_element("alpha");
let beta = s.create_element("beta");
c.append_element_child(alpha, beta);
assert_eq!(Some(ParentOfChild::Element(alpha)), c.element_parent(beta));
}
#[test]
fn elements_know_preceding_siblings() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let parent = s.create_element("parent");
let a = s.create_element("a");
let b = s.create_element("b");
c.append_element_child(parent, a);
c.append_element_child(parent, b);
let preceding: Vec<_> = c.element_preceding_siblings(b).collect();
assert_eq!(vec![ChildOfElement::Element(a)], preceding);
}
#[test]
fn changing_parent_of_element_removes_element_from_original_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let parent1 = s.create_element("parent1");
let parent2 = s.create_element("parent2");
let child = s.create_element("child");
c.append_element_child(parent1, child);
c.append_element_child(parent2, child);
assert_eq!(0, c.element_children(parent1).count());
assert_eq!(1, c.element_children(parent2).count());
}
#[test]
fn elements_can_be_renamed() {
let package = Package::new();
let (s, _) = package.as_thin_document();
let alpha = s.create_element("alpha");
s.element_set_name(alpha, "beta");
assert_qname_eq!(alpha.name(), "beta");
}
#[test]
fn elements_have_attributes() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let attr = s.create_attribute("hello", "world");
c.set_attribute(element, attr);
assert_eq!(Some("world"), c.attribute_value(element, "hello"));
}
#[test]
fn iterators_survive_mutation_through_another_connection() {
let package = Package::new();
let (storage, mut reader) = package.as_thin_document();
let (_, mut writer) = package.as_thin_document();
let parent = storage.create_element("parent");
let first = storage.create_element("first");
let second = storage.create_element("second");
reader.append_root_child(parent);
reader.append_element_child(parent, first);
reader.append_element_child(parent, second);
let original = storage.create_attribute("original", "value");
reader.set_attribute(parent, original);
let attributes = reader.attributes(parent);
let children = reader.element_children(parent);
let siblings = reader.element_following_siblings(first);
let roots = reader.root_children();
for index in 0..128 {
writer.set_attribute(
parent,
storage.create_attribute(format!("new{index}").as_str(), "value"),
);
writer.append_element_child(parent, storage.create_element("new"));
writer.append_root_child(storage.create_comment("new"));
}
assert_eq!(attributes.collect::<Vec<_>>(), vec![original]);
assert_eq!(
children.collect::<Vec<_>>(),
vec![
ChildOfElement::Element(first),
ChildOfElement::Element(second)
]
);
assert_eq!(
siblings.collect::<Vec<_>>(),
vec![ChildOfElement::Element(second)]
);
assert_eq!(
roots.collect::<Vec<_>>(),
vec![ChildOfRoot::Element(parent)]
);
}
#[test]
fn attributes_know_their_element() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let attr = s.create_attribute("hello", "world");
c.set_attribute(element, attr);
assert_eq!(Some(element), c.attribute_parent(attr));
}
#[test]
fn attributes_belong_to_one_element() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element1 = s.create_element("element1");
let element2 = s.create_element("element2");
let attr = s.create_attribute("hello", "world");
c.set_attribute(element1, attr);
c.set_attribute(element2, attr);
assert_eq!(0, c.attributes(element1).count());
assert_eq!(1, c.attributes(element2).count());
}
#[test]
fn attributes_can_be_reset() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let attr1 = s.create_attribute("hello", "world");
let attr2 = s.create_attribute("hello", "galaxy");
c.set_attribute(element, attr1);
c.set_attribute(element, attr2);
assert_eq!(Some("galaxy"), c.attribute_value(element, "hello"));
}
#[test]
fn attributes_can_be_iterated() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let attr1 = s.create_attribute("name1", "value1");
let attr2 = s.create_attribute("name2", "value2");
c.set_attribute(element, attr1);
c.set_attribute(element, attr2);
let mut attrs: Vec<_> = c.attributes(element).collect();
attrs.sort_by(|a, b| a.name().namespace_uri().cmp(&b.name().namespace_uri()));
assert_eq!(2, attrs.len());
assert_qname_eq!("name1", attrs[0].name());
assert_eq!("value1", attrs[0].value());
assert_qname_eq!("name2", attrs[1].name());
assert_eq!("value2", attrs[1].value());
}
#[test]
fn elements_can_have_text_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let sentence = s.create_element("sentence");
let text = s.create_text("Now is the winter of our discontent.");
c.append_element_child(sentence, text);
let children: Vec<_> = c.element_children(sentence).collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfElement::Text(text));
}
#[test]
fn text_knows_its_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let sentence = s.create_element("sentence");
let text = s.create_text("Now is the winter of our discontent.");
c.append_element_child(sentence, text);
assert_eq!(c.text_parent(text), Some(sentence));
}
#[test]
fn text_can_be_changed() {
let package = Package::new();
let (s, _) = package.as_thin_document();
let text = s.create_text("Now is the winter of our discontent.");
s.text_set_text(text, "Made glorious summer by this sun of York");
assert_eq!(text.text(), "Made glorious summer by this sun of York");
}
#[test]
fn elements_can_have_comment_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let sentence = s.create_element("sentence");
let comment = s.create_comment("Now is the winter of our discontent.");
c.append_element_child(sentence, comment);
let children: Vec<_> = c.element_children(sentence).collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfElement::Comment(comment));
}
#[test]
fn comment_knows_its_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let sentence = s.create_element("sentence");
let comment = s.create_comment("Now is the winter of our discontent.");
c.append_element_child(sentence, comment);
assert_eq!(
c.comment_parent(comment),
Some(ParentOfChild::Element(sentence))
);
}
#[test]
fn comment_can_be_changed() {
let package = Package::new();
let (s, _) = package.as_thin_document();
let comment = s.create_comment("Now is the winter of our discontent.");
s.comment_set_text(comment, "Made glorious summer by this sun of York");
assert_eq!(comment.text(), "Made glorious summer by this sun of York");
}
#[test]
fn elements_can_have_processing_instruction_children() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let pi = s.create_processing_instruction("device", None);
c.append_element_child(element, pi);
let children: Vec<_> = c.element_children(element).collect();
assert_eq!(1, children.len());
assert_eq!(children[0], ChildOfElement::ProcessingInstruction(pi));
}
#[test]
fn processing_instruction_knows_its_parent() {
let package = Package::new();
let (s, mut c) = package.as_thin_document();
let element = s.create_element("element");
let pi = s.create_processing_instruction("device", None);
c.append_element_child(element, pi);
assert_eq!(
c.processing_instruction_parent(pi),
Some(ParentOfChild::Element(element))
);
}
#[test]
fn processing_instruction_can_be_changed() {
let package = Package::new();
let (s, _) = package.as_thin_document();
let pi = s.create_processing_instruction("device", None);
s.processing_instruction_set_target(pi, "output");
s.processing_instruction_set_value(pi, Some("full-screen"));
assert_eq!(pi.target(), "output");
assert_eq!(pi.value(), Some("full-screen"));
}
#[test]
fn can_return_a_populated_package() {
fn populate() -> Package {
let package = Package::new();
{
let (s, mut c) = package.as_thin_document();
let element = s.create_element("hello");
c.append_root_child(element);
}
package
}
let package = populate();
let (_, c) = package.as_thin_document();
let children: Vec<_> = c.root_children().collect();
let element = children[0].element().unwrap();
assert_qname_eq!(element.name(), "hello");
}
#[test]
#[should_panic(expected = "same Package")]
fn root_rejects_a_child_from_another_package() {
let package = Package::new();
let foreign = Package::new();
let (_, mut connections) = package.as_thin_document();
let (foreign_storage, _) = foreign.as_thin_document();
connections.append_root_child(foreign_storage.create_element("foreign"));
}
#[test]
#[should_panic(expected = "same Package")]
fn element_rejects_nodes_from_another_package() {
let package = Package::new();
let foreign = Package::new();
let (storage, mut connections) = package.as_thin_document();
let (foreign_storage, _) = foreign.as_thin_document();
connections.append_element_child(
storage.create_element("local"),
foreign_storage.create_text("foreign"),
);
}
}