use crate::document::{Document, Node};
use crate::error::{EditXMLError, Result};
use crate::utils::HashMap;
#[cfg(feature = "document-breakdown")]
mod breakdown;
#[cfg(feature = "document-breakdown")]
pub use breakdown::*;
mod builder;
mod debug;
pub use builder::ElementBuilder;
pub use debug::ElementDebug;
#[derive(Debug, Default)]
pub(crate) struct ElementData {
full_name: String,
attributes: HashMap<String, String>, namespace_decls: HashMap<String, String>, parent: Option<Element>,
children: Vec<Node>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Element {
id: usize,
}
impl Element {
pub fn new<S: Into<String>>(doc: &mut Document, full_name: S) -> Self {
Self::with_data(doc, full_name.into(), HashMap::new(), HashMap::new())
}
pub fn new_with_text(doc: &mut Document, full_name: &str, text: &str) -> Self {
let elem = Self::new(doc, full_name);
elem.set_text_content(doc, text);
elem
}
pub fn build<S: Into<String>>(name: S) -> ElementBuilder {
ElementBuilder::new(name.into())
}
pub(crate) fn with_data(
doc: &mut Document,
full_name: String,
attributes: HashMap<String, String>,
namespace_decls: HashMap<String, String>,
) -> Element {
let elem = Element { id: doc.counter };
let elem_data = ElementData {
full_name,
attributes,
namespace_decls,
..Default::default()
};
doc.store.push(elem_data);
doc.counter += 1;
elem
}
pub(crate) fn container() -> (Element, ElementData) {
let elem_data = ElementData::default();
let elem = Element { id: 0 };
(elem, elem_data)
}
pub fn is_container(&self) -> bool {
self.id == 0
}
#[deprecated(note = "Use Into<Node> instead. This method will be removed in future versions.")]
pub fn as_node(&self) -> Node {
self.into()
}
pub fn separate_prefix_name(full_name: &str) -> (&str, &str) {
match full_name.split_once(":") {
Some((prefix, name)) => (prefix, name),
None => ("", full_name),
}
}
pub fn debug<'element, 'doc>(
&'element self,
doc: &'doc Document,
) -> ElementDebug<'element, 'doc> {
ElementDebug { element: self, doc }
}
}
impl Element {
fn data<'a>(&self, doc: &'a Document) -> &'a ElementData {
doc.store.get(self.id).unwrap()
}
fn mut_data<'a>(&self, doc: &'a mut Document) -> &'a mut ElementData {
doc.store.get_mut(self.id).unwrap()
}
pub fn is_root(&self, doc: &Document) -> bool {
self.parent(doc).map_or(false, |p| p.is_container())
}
pub fn full_name<'a>(&self, doc: &'a Document) -> &'a str {
&self.data(doc).full_name
}
pub fn set_full_name<S: Into<String>>(&self, doc: &mut Document, name: S) {
self.mut_data(doc).full_name = name.into();
}
pub fn prefix_name<'a>(&self, doc: &'a Document) -> (&'a str, &'a str) {
Self::separate_prefix_name(self.full_name(doc))
}
pub fn prefix<'a>(&self, doc: &'a Document) -> &'a str {
self.prefix_name(doc).0
}
pub fn set_prefix<S: Into<String>>(&self, doc: &mut Document, prefix: S) {
let data = self.mut_data(doc);
let (_, name) = Self::separate_prefix_name(&data.full_name);
let prefix: String = prefix.into();
if prefix.is_empty() {
data.full_name = name.to_string();
} else {
data.full_name = format!("{}:{}", prefix, name);
}
}
pub fn name<'a>(&self, doc: &'a Document) -> &'a str {
self.prefix_name(doc).1
}
pub fn set_name<S: Into<String>>(&self, doc: &mut Document, name: S) {
let data = self.mut_data(doc);
let (prefix, _) = Self::separate_prefix_name(&data.full_name);
if prefix.is_empty() {
data.full_name = name.into();
} else {
data.full_name = format!("{}:{}", prefix, name.into());
}
}
pub fn attributes<'a>(&self, doc: &'a Document) -> &'a HashMap<String, String> {
&self.data(doc).attributes
}
pub fn attribute<'a>(&self, doc: &'a Document, name: &str) -> Option<&'a str> {
self.attributes(doc).get(name).map(|v| v.as_str())
}
pub fn set_attribute<S, T>(&self, doc: &mut Document, name: S, value: T)
where
S: Into<String>,
T: Into<String>,
{
self.mut_attributes(doc).insert(name.into(), value.into());
}
pub fn mut_attributes<'a>(&self, doc: &'a mut Document) -> &'a mut HashMap<String, String> {
&mut self.mut_data(doc).attributes
}
pub fn namespace<'a>(&self, doc: &'a Document) -> Option<&'a str> {
self.namespace_for_prefix(doc, self.prefix(doc))
}
pub fn namespace_decls<'a>(&self, doc: &'a Document) -> &'a HashMap<String, String> {
&self.data(doc).namespace_decls
}
pub fn mut_namespace_decls<'a>(
&self,
doc: &'a mut Document,
) -> &'a mut HashMap<String, String> {
&mut self.mut_data(doc).namespace_decls
}
pub fn set_namespace_decl<S, T>(&self, doc: &mut Document, prefix: S, namespace: T)
where
S: Into<String>,
T: Into<String>,
{
self.mut_namespace_decls(doc)
.insert(prefix.into(), namespace.into());
}
pub fn namespace_for_prefix<'a>(&self, doc: &'a Document, prefix: &str) -> Option<&'a str> {
match prefix {
"xml" => return Some("http://www.w3.org/XML/1998/namespace"),
"xmlns" => return Some("http://www.w3.org/2000/xmlns/"),
_ => (),
};
let mut elem = *self;
loop {
let data = elem.data(doc);
if let Some(value) = data.namespace_decls.get(prefix) {
return Some(value);
}
elem = elem.parent(doc)?;
}
}
pub(crate) fn build_text_content<'a>(&self, doc: &'a Document, buf: &'a mut String) {
for child in self.children(doc) {
child.build_text_content(doc, buf);
}
}
pub fn text_content(&self, doc: &Document) -> String {
let mut buf = String::new();
self.build_text_content(doc, &mut buf);
buf
}
pub fn set_text_content<S: Into<String>>(&self, doc: &mut Document, text: S) {
self.clear_children(doc);
let node = Node::Text(text.into());
self.push_child(doc, node).unwrap();
}
}
impl Element {
pub fn parent(&self, doc: &Document) -> Option<Element> {
self.data(doc).parent
}
#[inline]
pub fn has_parent(&self, doc: &Document) -> bool {
self.parent(doc).is_some()
}
pub fn children<'a>(&self, doc: &'a Document) -> &'a Vec<Node> {
&self.data(doc).children
}
pub fn push_children_recursive<'a>(&self, doc: &'a Document, nodes: &mut Vec<&'a Node>) {
let children = self.children(doc);
nodes.reserve(children.len());
for node in children {
nodes.push(node);
if let Node::Element(elem) = &node {
elem.push_children_recursive(doc, nodes);
}
}
}
#[inline]
pub fn children_recursive<'a>(&self, doc: &'a Document) -> Vec<&'a Node> {
let mut nodes = Vec::new();
self.push_children_recursive(doc, &mut nodes);
nodes
}
#[inline]
pub fn has_children(&self, doc: &Document) -> bool {
!self.children(doc).is_empty()
}
pub fn has_children_ignore_whitespace(&self, doc: &Document) -> bool {
self.children(doc)
.iter()
.any(|n| !n.is_text() || !n.text_content(doc).trim().is_empty())
}
pub fn child_elements(&self, doc: &Document) -> Vec<Element> {
self.children(doc)
.iter()
.filter_map(|node| {
if let Node::Element(elemid) = node {
Some(*elemid)
} else {
None
}
})
.collect()
}
pub fn child_elements_recursive(&self, doc: &Document) -> Vec<Element> {
self.children_recursive(doc)
.iter()
.filter_map(|node| {
if let Node::Element(elemid) = node {
Some(*elemid)
} else {
None
}
})
.collect()
}
pub fn find(&self, doc: &Document, name: &str) -> Option<Element> {
self.children(doc)
.iter()
.filter_map(|n| n.as_element())
.find(|e| e.name(doc) == name)
}
pub fn find_all(&self, doc: &Document, name: &str) -> Vec<Element> {
self.children(doc)
.iter()
.filter_map(|n| n.as_element())
.filter(|e| e.name(doc) == name)
.collect()
}
}
impl Element {
pub fn push_child(&self, doc: &mut Document, node: impl Into<Node>) -> Result<()> {
let node = node.into();
if let Node::Element(new_child) = node {
return self.push_child_element(doc, new_child);
}
self.mut_data(doc).children.push(node);
Ok(())
}
pub(crate) fn push_child_element(&self, doc: &mut Document, new_child: Element) -> Result<()> {
if new_child.is_container() {
return Err(EditXMLError::ContainerCannotMove);
}
let child_data = new_child.mut_data(doc);
if child_data.parent.is_some() {
return Err(EditXMLError::HasAParent);
}
child_data.parent = Some(*self);
self.mut_data(doc).children.push(Node::Element(new_child));
Ok(())
}
pub fn create_child<N, F>(&self, doc: &mut Document, name: N, f: F)
where
N: Into<String>,
F: FnOnce(ElementBuilder) -> ElementBuilder,
{
let elem = f(ElementBuilder::new(name.into()));
let elem = elem.finish(doc);
self.push_child_element(doc, elem).unwrap();
}
pub fn insert_child(&self, doc: &mut Document, index: usize, node: Node) -> Result<()> {
if let Node::Element(elem) = node {
if elem.is_container() {
return Err(EditXMLError::ContainerCannotMove);
}
let data = elem.mut_data(doc);
if data.parent.is_some() {
return Err(EditXMLError::HasAParent);
}
data.parent = Some(*self);
}
self.mut_data(doc).children.insert(index, node);
Ok(())
}
pub fn remove_child(&self, doc: &mut Document, index: usize) -> Node {
let node = self.mut_data(doc).children.remove(index);
if let Node::Element(elem) = node {
elem.mut_data(doc).parent = None;
}
node
}
pub fn pop_child(&self, doc: &mut Document) -> Option<Node> {
let child = self.mut_data(doc).children.pop();
if let Some(Node::Element(elem)) = &child {
elem.mut_data(doc).parent = None;
}
child
}
pub fn clear_children(&self, doc: &mut Document) -> Vec<Node> {
let count = self.children(doc).len();
let mut removed = Vec::with_capacity(count);
for _ in 0..count {
let child = self.remove_child(doc, 0);
removed.push(child);
}
removed
}
pub fn detach(&self, doc: &mut Document) -> Result<()> {
if self.is_container() {
return Err(EditXMLError::ContainerCannotMove);
}
let data = self.mut_data(doc);
if let Some(parent) = data.parent {
let pos = parent
.children(doc)
.iter()
.position(|n| n.as_element() == Some(*self))
.unwrap();
parent.remove_child(doc, pos);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Document, Element, Node};
#[test]
fn test_children() {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<outer>
inside outer
<middle>
<inner>
inside
</inner>
after inside
</middle>
<after>
inside after
</after>
</outer>
"#;
let doc = Document::parse_str(xml).unwrap();
let outer = doc.container().child_elements(&doc)[0];
let middle = outer.child_elements(&doc)[0];
let inner = middle.child_elements(&doc)[0];
let after = outer.child_elements(&doc)[1];
assert_eq!(doc.container().child_elements(&doc).len(), 1);
assert_eq!(outer.name(&doc), "outer");
assert_eq!(middle.name(&doc), "middle");
assert_eq!(inner.name(&doc), "inner");
assert_eq!(after.name(&doc), "after");
assert_eq!(outer.children(&doc).len(), 3);
assert_eq!(outer.child_elements(&doc).len(), 2);
assert_eq!(doc.container().children_recursive(&doc).len(), 8);
assert_eq!(
doc.container().child_elements_recursive(&doc),
vec![outer, middle, inner, after]
);
}
#[test]
fn test_namespace() {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="ns" xmlns:p="pns">
<p:foo xmlns="inner">
Hello
</p:foo>
<p:bar xmlns:p="in2">
<c />
World!
</p:bar>
</root>"#;
let doc = Document::parse_str(xml).unwrap();
let container = doc.container().children(&doc)[0].as_element().unwrap();
let child_elements = container.child_elements(&doc);
let foo = *child_elements.first().unwrap();
let bar = *child_elements.get(1).unwrap();
let c = bar.child_elements(&doc)[0];
assert_eq!(c.prefix_name(&doc), ("", "c"));
assert_eq!(bar.full_name(&doc), "p:bar");
assert_eq!(bar.prefix(&doc), "p");
assert_eq!(bar.name(&doc), "bar");
assert_eq!(c.namespace(&doc).unwrap(), "ns");
assert_eq!(c.namespace_for_prefix(&doc, "p").unwrap(), "in2");
assert!(c.namespace_for_prefix(&doc, "random").is_none());
assert_eq!(bar.namespace(&doc).unwrap(), "in2");
assert_eq!(bar.namespace_for_prefix(&doc, "").unwrap(), "ns");
assert_eq!(foo.namespace(&doc).unwrap(), "pns");
assert_eq!(foo.namespace_for_prefix(&doc, "").unwrap(), "inner");
assert_eq!(foo.namespace_for_prefix(&doc, "p").unwrap(), "pns");
assert_eq!(container.namespace(&doc).unwrap(), "ns");
}
#[test]
fn test_find_text_content() {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<core>
<p>Text</p>
<b>Text2</b>
</core>
"#;
let doc = Document::parse_str(xml).unwrap();
assert_eq!(
doc.root_element()
.unwrap()
.find(&doc, "p")
.unwrap()
.text_content(&doc),
"Text"
);
assert_eq!(
doc.root_element()
.unwrap()
.find(&doc, "b")
.unwrap()
.text_content(&doc),
"Text2"
);
assert_eq!(doc.root_element().unwrap().text_content(&doc), "TextText2")
}
#[test]
fn test_mutate_tree() -> anyhow::Result<()> {
let mut doc = Document::new();
let container = doc.container();
assert_eq!(container.parent(&doc), None);
assert_eq!(container.children(&doc).len(), 0);
let root = Element::build("root").push_to(&mut doc, container);
assert_eq!(root.parent(&doc).unwrap(), container);
assert_eq!(doc.root_element().unwrap(), root);
let a = Element::new(&mut doc, "a");
assert_eq!(a.parent(&doc), None);
root.push_child(&mut doc, Node::Element(a)).unwrap();
assert_eq!(root.children(&doc)[0].as_element().unwrap(), a);
assert_eq!(a.parent(&doc).unwrap(), root);
let popped = root.pop_child(&mut doc).unwrap().as_element().unwrap();
assert_eq!(popped, a);
assert_eq!(root.children(&doc).len(), 0);
assert_eq!(a.parent(&doc), None);
root.push_child(&mut doc, Node::Element(a)).unwrap();
assert_eq!(root.children(&doc)[0].as_element().unwrap(), a);
assert_eq!(a.parent(&doc).unwrap(), root);
root.remove_child(&mut doc, 0);
assert_eq!(root.children(&doc).len(), 0);
assert_eq!(a.parent(&doc), None);
let a = Element::new(&mut doc, "a");
root.insert_child(&mut doc, 0, Node::Element(a)).unwrap();
assert_eq!(root.children(&doc)[0].as_element().unwrap(), a);
assert_eq!(a.parent(&doc).unwrap(), root);
a.detach(&mut doc).unwrap();
assert_eq!(root.children(&doc).len(), 0);
assert_eq!(a.parent(&doc), None);
Ok(())
}
}