use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(pub(crate) usize);
impl NodeId {
pub fn new(index: usize) -> Self {
NodeId(index)
}
pub fn index(&self) -> usize {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct QName<'a> {
pub namespace_uri: Option<Cow<'a, str>>,
pub prefix: Option<Cow<'a, str>>,
pub local_name: Cow<'a, str>,
}
impl<'a> QName<'a> {
pub fn local(name: impl Into<Cow<'a, str>>) -> Self {
QName {
namespace_uri: None,
prefix: None,
local_name: name.into(),
}
}
pub fn with_namespace(
namespace_uri: impl Into<Cow<'a, str>>,
local_name: impl Into<Cow<'a, str>>,
) -> Self {
QName {
namespace_uri: Some(namespace_uri.into()),
prefix: None,
local_name: local_name.into(),
}
}
pub fn full(
prefix: impl Into<Cow<'a, str>>,
namespace_uri: impl Into<Cow<'a, str>>,
local_name: impl Into<Cow<'a, str>>,
) -> Self {
QName {
namespace_uri: Some(namespace_uri.into()),
prefix: Some(prefix.into()),
local_name: local_name.into(),
}
}
pub fn matches(&self, namespace_uri: Option<&str>, local_name: &str) -> bool {
*self.local_name == *local_name && self.namespace_uri.as_deref() == namespace_uri
}
pub fn prefixed_name(&self) -> Cow<'_, str> {
match &self.prefix {
Some(p) => Cow::Owned(format!("{}:{}", p, self.local_name)),
None => Cow::Borrowed(&self.local_name),
}
}
pub fn into_static(self) -> QName<'static> {
QName {
namespace_uri: self.namespace_uri.map(|s| Cow::Owned(s.into_owned())),
prefix: self.prefix.map(|s| Cow::Owned(s.into_owned())),
local_name: Cow::Owned(self.local_name.into_owned()),
}
}
}
impl<'a> fmt::Display for QName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.namespace_uri, &self.prefix) {
(Some(ns), Some(p)) => write!(f, "{{{}}}{}:{}", ns, p, self.local_name),
(Some(ns), None) => write!(f, "{{{}}}{}", ns, self.local_name),
_ => write!(f, "{}", self.local_name),
}
}
}
pub struct ChildrenIter<'d, 'a> {
doc: &'d Document<'a>,
next: Option<NodeId>,
}
impl<'d, 'a> Iterator for ChildrenIter<'d, 'a> {
type Item = NodeId;
fn next(&mut self) -> Option<NodeId> {
let id = self.next?;
self.next = self.doc.nodes.get(id.0).and_then(|n| n.next_sibling);
Some(id)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute<'a> {
pub name: QName<'a>,
pub value: Cow<'a, str>,
}
impl<'a> Attribute<'a> {
pub fn into_static(self) -> Attribute<'static> {
Attribute {
name: self.name.into_static(),
value: Cow::Owned(self.value.into_owned()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct XmlDeclaration<'a> {
pub version: Cow<'a, str>,
pub encoding: Option<Cow<'a, str>>,
pub standalone: Option<bool>,
}
impl<'a> XmlDeclaration<'a> {
pub fn into_static(self) -> XmlDeclaration<'static> {
XmlDeclaration {
version: Cow::Owned(self.version.into_owned()),
encoding: self.encoding.map(|s| Cow::Owned(s.into_owned())),
standalone: self.standalone,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProcessingInstruction<'a> {
pub target: Cow<'a, str>,
pub data: Option<Cow<'a, str>>,
}
impl<'a> ProcessingInstruction<'a> {
pub fn into_static(self) -> ProcessingInstruction<'static> {
ProcessingInstruction {
target: Cow::Owned(self.target.into_owned()),
data: self.data.map(|s| Cow::Owned(s.into_owned())),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NodeKind<'a> {
Document,
Element(Element<'a>),
Text(Cow<'a, str>),
CData(Cow<'a, str>),
Comment(Cow<'a, str>),
ProcessingInstruction(ProcessingInstruction<'a>),
Attribute(QName<'a>, Cow<'a, str>),
}
impl<'a> NodeKind<'a> {
pub fn into_static(self) -> NodeKind<'static> {
match self {
NodeKind::Document => NodeKind::Document,
NodeKind::Element(e) => NodeKind::Element(e.into_static()),
NodeKind::Text(t) => NodeKind::Text(Cow::Owned(t.into_owned())),
NodeKind::CData(t) => NodeKind::CData(Cow::Owned(t.into_owned())),
NodeKind::Comment(t) => NodeKind::Comment(Cow::Owned(t.into_owned())),
NodeKind::ProcessingInstruction(pi) => {
NodeKind::ProcessingInstruction(pi.into_static())
}
NodeKind::Attribute(name, value) => {
NodeKind::Attribute(name.into_static(), Cow::Owned(value.into_owned()))
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Element<'a> {
pub name: QName<'a>,
pub attributes: Vec<Attribute<'a>>,
pub namespace_declarations: Vec<(Cow<'a, str>, Cow<'a, str>)>,
}
impl<'a> Element<'a> {
pub fn get_attribute(&self, local_name: &str) -> Option<&str> {
self.attributes
.iter()
.find(|a| *a.name.local_name == *local_name)
.map(|a| &*a.value)
}
pub fn get_attribute_ns(&self, namespace_uri: &str, local_name: &str) -> Option<&str> {
self.attributes
.iter()
.find(|a| {
*a.name.local_name == *local_name
&& a.name.namespace_uri.as_deref() == Some(namespace_uri)
})
.map(|a| &*a.value)
}
pub fn matches_name_ns(&self, namespace_uri: &str, local_name: &str) -> bool {
self.name.matches(Some(namespace_uri), local_name)
}
pub fn set_attribute(&mut self, name: QName<'a>, value: Cow<'a, str>) -> Option<Cow<'a, str>> {
for attr in &mut self.attributes {
if attr.name == name {
let old = std::mem::replace(&mut attr.value, value);
return Some(old);
}
}
self.attributes.push(Attribute { name, value });
None
}
pub fn remove_attribute(&mut self, local_name: &str) -> Option<Cow<'a, str>> {
if let Some(pos) = self
.attributes
.iter()
.position(|a| *a.name.local_name == *local_name)
{
Some(self.attributes.remove(pos).value)
} else {
None
}
}
pub fn into_static(self) -> Element<'static> {
Element {
name: self.name.into_static(),
attributes: self
.attributes
.into_iter()
.map(|a| a.into_static())
.collect(),
namespace_declarations: self
.namespace_declarations
.into_iter()
.map(|(k, v)| (Cow::Owned(k.into_owned()), Cow::Owned(v.into_owned())))
.collect::<Vec<_>>(),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct NodeData<'a> {
pub kind: NodeKind<'a>,
pub parent: Option<NodeId>,
pub first_child: Option<NodeId>,
pub last_child: Option<NodeId>,
pub next_sibling: Option<NodeId>,
pub prev_sibling: Option<NodeId>,
pub byte_pos: usize,
pub byte_end_pos: usize,
}
impl<'a> NodeData<'a> {
pub fn into_static(self) -> NodeData<'static> {
NodeData {
kind: self.kind.into_static(),
parent: self.parent,
first_child: self.first_child,
last_child: self.last_child,
next_sibling: self.next_sibling,
prev_sibling: self.prev_sibling,
byte_pos: self.byte_pos,
byte_end_pos: self.byte_end_pos,
}
}
}
#[derive(Debug, Clone)]
pub struct Document<'a> {
pub(crate) nodes: Vec<NodeData<'a>>,
root: NodeId,
pub xml_declaration: Option<XmlDeclaration<'a>>,
pub doctype: Option<Cow<'a, str>>,
pub(crate) attribute_nodes: HashMap<NodeId, Vec<NodeId>>,
pub(crate) input: &'a str,
}
impl<'a> Document<'a> {
pub fn new() -> Self {
let root_node = NodeData {
kind: NodeKind::Document,
parent: None,
first_child: None,
last_child: None,
next_sibling: None,
prev_sibling: None,
byte_pos: 0,
byte_end_pos: 0,
};
Document {
nodes: vec![root_node],
root: NodeId(0),
xml_declaration: None,
doctype: None,
attribute_nodes: HashMap::new(),
input: "",
}
}
pub fn into_static(self) -> Document<'static> {
Document {
nodes: self.nodes.into_iter().map(|n| n.into_static()).collect(),
root: self.root,
xml_declaration: self.xml_declaration.map(|d| d.into_static()),
doctype: self.doctype.map(|s| Cow::Owned(s.into_owned())),
attribute_nodes: self.attribute_nodes,
input: "",
}
}
pub fn root(&self) -> NodeId {
self.root
}
pub fn document_element(&self) -> Option<NodeId> {
self.children(self.root)
.into_iter()
.find(|&id| matches!(self.node_kind(id), Some(NodeKind::Element(_))))
}
pub(crate) fn alloc_node(&mut self, kind: NodeKind<'a>, byte_pos: usize) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(NodeData {
kind,
parent: None,
first_child: None,
last_child: None,
next_sibling: None,
prev_sibling: None,
byte_pos,
byte_end_pos: 0,
});
id
}
pub(crate) fn set_byte_end_pos(&mut self, id: NodeId, pos: usize) {
if let Some(node) = self.nodes.get_mut(id.0) {
node.byte_end_pos = pos;
}
}
pub(crate) fn build_attribute_nodes(&mut self, element_id: NodeId) {
let attrs: Vec<(QName<'a>, Cow<'a, str>)> = match self.node_kind(element_id) {
Some(NodeKind::Element(e)) => e
.attributes
.iter()
.map(|a| (a.name.clone(), a.value.clone()))
.collect(),
_ => return,
};
let mut attr_ids = Vec::with_capacity(attrs.len());
for (name, value) in attrs {
let attr_id = self.alloc_node(NodeKind::Attribute(name, value), 0);
if let Some(node) = self.nodes.get_mut(attr_id.0) {
node.parent = Some(element_id);
}
attr_ids.push(attr_id);
}
if !attr_ids.is_empty() {
self.attribute_nodes.insert(element_id, attr_ids);
}
}
pub fn get_attribute_nodes(&self, element_id: NodeId) -> &[NodeId] {
self.attribute_nodes
.get(&element_id)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn prepare_xpath(&mut self) {
if !self.attribute_nodes.is_empty() {
return; }
let element_ids: Vec<NodeId> = self
.nodes
.iter()
.enumerate()
.filter_map(|(i, n)| match &n.kind {
NodeKind::Element(e) if !e.attributes.is_empty() => Some(NodeId(i)),
_ => None,
})
.collect();
for elem_id in element_ids {
self.build_attribute_nodes(elem_id);
}
}
pub fn create_element(&mut self, name: QName<'a>) -> NodeId {
self.alloc_node(
NodeKind::Element(Element {
name,
attributes: Vec::new(),
namespace_declarations: Vec::new(),
}),
0,
)
}
pub fn create_text(&mut self, text: impl Into<Cow<'a, str>>) -> NodeId {
self.alloc_node(NodeKind::Text(text.into()), 0)
}
pub fn create_comment(&mut self, text: impl Into<Cow<'a, str>>) -> NodeId {
self.alloc_node(NodeKind::Comment(text.into()), 0)
}
pub fn create_processing_instruction(
&mut self,
target: impl Into<Cow<'a, str>>,
data: Option<Cow<'a, str>>,
) -> NodeId {
self.alloc_node(
NodeKind::ProcessingInstruction(ProcessingInstruction {
target: target.into(),
data,
}),
0,
)
}
pub fn create_cdata(&mut self, text: impl Into<Cow<'a, str>>) -> NodeId {
self.alloc_node(NodeKind::CData(text.into()), 0)
}
pub fn node_kind(&self, id: NodeId) -> Option<&NodeKind<'a>> {
self.nodes.get(id.0).map(|n| &n.kind)
}
pub fn node_kind_mut(&mut self, id: NodeId) -> Option<&mut NodeKind<'a>> {
self.nodes.get_mut(id.0).map(|n| &mut n.kind)
}
pub fn element(&self, id: NodeId) -> Option<&Element<'a>> {
match self.node_kind(id) {
Some(NodeKind::Element(e)) => Some(e),
_ => None,
}
}
pub fn element_mut(&mut self, id: NodeId) -> Option<&mut Element<'a>> {
match self.node_kind_mut(id) {
Some(NodeKind::Element(e)) => Some(e),
_ => None,
}
}
pub fn text_content(&self, id: NodeId) -> Option<&str> {
match self.node_kind(id) {
Some(NodeKind::Text(t)) => Some(t),
Some(NodeKind::CData(t)) => Some(t),
_ => None,
}
}
pub fn element_text(&self, id: NodeId) -> Option<&str> {
let mut child = self.nodes.get(id.0).and_then(|n| n.first_child);
while let Some(cid) = child {
match self.node_kind(cid) {
Some(NodeKind::Text(t)) => return Some(t),
Some(NodeKind::CData(t)) => return Some(t),
_ => {}
}
child = self.nodes.get(cid.0).and_then(|n| n.next_sibling);
}
None
}
pub fn get_attribute(&self, id: NodeId, local_name: &str) -> Option<&str> {
self.element(id)?.get_attribute(local_name)
}
pub fn get_attribute_ns(
&self,
id: NodeId,
namespace_uri: &str,
local_name: &str,
) -> Option<&str> {
self.element(id)?
.get_attribute_ns(namespace_uri, local_name)
}
pub fn parent(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.parent)
}
pub fn children(&self, id: NodeId) -> Vec<NodeId> {
let mut result = Vec::new();
let mut current = self.nodes.get(id.0).and_then(|n| n.first_child);
while let Some(child_id) = current {
result.push(child_id);
current = self.nodes.get(child_id.0).and_then(|n| n.next_sibling);
}
result
}
pub fn children_iter(&self, id: NodeId) -> ChildrenIter<'_, 'a> {
ChildrenIter {
doc: self,
next: self.nodes.get(id.0).and_then(|n| n.first_child),
}
}
pub fn node_line(&self, id: NodeId) -> usize {
let byte_pos = match self.nodes.get(id.0) {
Some(n) => n.byte_pos,
None => return 0,
};
if self.input.is_empty() || byte_pos == 0 {
return 1;
}
self.input.as_bytes()[..byte_pos]
.iter()
.filter(|&&b| b == b'\n')
.count()
+ 1
}
pub fn node_column(&self, id: NodeId) -> usize {
let byte_pos = match self.nodes.get(id.0) {
Some(n) => n.byte_pos,
None => return 0,
};
if self.input.is_empty() || byte_pos == 0 {
return 1;
}
let bytes = &self.input.as_bytes()[..byte_pos];
match bytes.iter().rposition(|&b| b == b'\n') {
Some(nl_pos) => byte_pos - nl_pos,
None => byte_pos + 1,
}
}
pub fn node_range(&self, id: NodeId) -> Option<std::ops::Range<usize>> {
let node = self.nodes.get(id.0)?;
if node.byte_end_pos == 0 && id.0 != 0 {
return None; }
Some(node.byte_pos..node.byte_end_pos)
}
pub fn node_source(&self, id: NodeId) -> Option<&'a str> {
let range = self.node_range(id)?;
if range.end > self.input.len() {
return None;
}
Some(&self.input[range])
}
pub fn input_text(&self) -> &'a str {
self.input
}
pub fn get_elements_by_tag_name(&self, local_name: &str) -> Vec<NodeId> {
let mut results = Vec::new();
self.collect_elements_by_tag_name(self.root, local_name, &mut results);
results
}
fn collect_elements_by_tag_name(
&self,
id: NodeId,
local_name: &str,
results: &mut Vec<NodeId>,
) {
if let Some(NodeKind::Element(e)) = self.node_kind(id) {
if *e.name.local_name == *local_name {
results.push(id);
}
}
for child in self.children(id) {
self.collect_elements_by_tag_name(child, local_name, results);
}
}
pub fn get_elements_by_tag_name_ns(
&self,
namespace_uri: &str,
local_name: &str,
) -> Vec<NodeId> {
let mut results = Vec::new();
self.collect_elements_by_tag_name_ns(self.root, namespace_uri, local_name, &mut results);
results
}
fn collect_elements_by_tag_name_ns(
&self,
id: NodeId,
namespace_uri: &str,
local_name: &str,
results: &mut Vec<NodeId>,
) {
if let Some(NodeKind::Element(e)) = self.node_kind(id) {
if *e.name.local_name == *local_name
&& e.name.namespace_uri.as_deref() == Some(namespace_uri)
{
results.push(id);
}
}
for child in self.children(id) {
self.collect_elements_by_tag_name_ns(child, namespace_uri, local_name, results);
}
}
pub fn first_child_element_by_name_ns(
&self,
parent: NodeId,
namespace_uri: &str,
local_name: &str,
) -> Option<NodeId> {
let mut child = self.nodes.get(parent.0).and_then(|n| n.first_child);
while let Some(cid) = child {
if let Some(elem) = self.element(cid) {
if elem.matches_name_ns(namespace_uri, local_name) {
return Some(cid);
}
}
child = self.nodes.get(cid.0).and_then(|n| n.next_sibling);
}
None
}
pub fn child_elements_by_name_ns(
&self,
parent: NodeId,
namespace_uri: &str,
local_name: &str,
) -> Vec<NodeId> {
let mut result = Vec::new();
let mut child = self.nodes.get(parent.0).and_then(|n| n.first_child);
while let Some(cid) = child {
if let Some(elem) = self.element(cid) {
if elem.matches_name_ns(namespace_uri, local_name) {
result.push(cid);
}
}
child = self.nodes.get(cid.0).and_then(|n| n.next_sibling);
}
result
}
pub fn text_content_deep(&self, id: NodeId) -> String {
let mut buf = String::new();
self.collect_text(id, &mut buf);
buf
}
fn collect_text(&self, id: NodeId, buf: &mut String) {
match self.node_kind(id) {
Some(NodeKind::Text(t)) => buf.push_str(t),
Some(NodeKind::CData(t)) => buf.push_str(t),
_ => {
for child in self.children(id) {
self.collect_text(child, buf);
}
}
}
}
pub fn append_child(&mut self, parent: NodeId, child: NodeId) {
self.detach(child);
self.append_child_unchecked(parent, child);
}
#[inline]
pub(crate) fn append_child_unchecked(&mut self, parent: NodeId, child: NodeId) {
self.nodes[child.0].parent = Some(parent);
let last = self.nodes[parent.0].last_child;
if let Some(last_id) = last {
self.nodes[last_id.0].next_sibling = Some(child);
self.nodes[child.0].prev_sibling = Some(last_id);
self.nodes[parent.0].last_child = Some(child);
} else {
self.nodes[parent.0].first_child = Some(child);
self.nodes[parent.0].last_child = Some(child);
}
}
pub fn insert_before(&mut self, parent: NodeId, new_child: NodeId, reference: NodeId) {
self.detach(new_child);
if let Some(node) = self.nodes.get_mut(new_child.0) {
node.parent = Some(parent);
}
let prev = self.nodes.get(reference.0).and_then(|n| n.prev_sibling);
if let Some(nc) = self.nodes.get_mut(new_child.0) {
nc.prev_sibling = prev;
nc.next_sibling = Some(reference);
}
if let Some(r) = self.nodes.get_mut(reference.0) {
r.prev_sibling = Some(new_child);
}
if let Some(prev_id) = prev {
if let Some(p) = self.nodes.get_mut(prev_id.0) {
p.next_sibling = Some(new_child);
}
} else {
if let Some(p) = self.nodes.get_mut(parent.0) {
p.first_child = Some(new_child);
}
}
}
pub fn insert_after(&mut self, parent: NodeId, new_child: NodeId, reference: NodeId) {
self.detach(new_child);
if let Some(node) = self.nodes.get_mut(new_child.0) {
node.parent = Some(parent);
}
let next = self.nodes.get(reference.0).and_then(|n| n.next_sibling);
if let Some(nc) = self.nodes.get_mut(new_child.0) {
nc.prev_sibling = Some(reference);
nc.next_sibling = next;
}
if let Some(r) = self.nodes.get_mut(reference.0) {
r.next_sibling = Some(new_child);
}
if let Some(next_id) = next {
if let Some(n) = self.nodes.get_mut(next_id.0) {
n.prev_sibling = Some(new_child);
}
} else {
if let Some(p) = self.nodes.get_mut(parent.0) {
p.last_child = Some(new_child);
}
}
}
pub fn remove_child(&mut self, _parent: NodeId, child: NodeId) {
self.detach(child);
}
pub fn replace_child(&mut self, parent: NodeId, new_child: NodeId, old_child: NodeId) {
self.detach(new_child);
let prev = self.nodes.get(old_child.0).and_then(|n| n.prev_sibling);
let next = self.nodes.get(old_child.0).and_then(|n| n.next_sibling);
if let Some(nc) = self.nodes.get_mut(new_child.0) {
nc.parent = Some(parent);
nc.prev_sibling = prev;
nc.next_sibling = next;
}
if let Some(prev_id) = prev {
if let Some(p) = self.nodes.get_mut(prev_id.0) {
p.next_sibling = Some(new_child);
}
} else if let Some(p) = self.nodes.get_mut(parent.0) {
p.first_child = Some(new_child);
}
if let Some(next_id) = next {
if let Some(n) = self.nodes.get_mut(next_id.0) {
n.prev_sibling = Some(new_child);
}
} else if let Some(p) = self.nodes.get_mut(parent.0) {
p.last_child = Some(new_child);
}
if let Some(oc) = self.nodes.get_mut(old_child.0) {
oc.parent = None;
oc.prev_sibling = None;
oc.next_sibling = None;
}
}
pub fn detach(&mut self, id: NodeId) {
let (parent_id, prev, next) = match self.nodes.get(id.0) {
Some(n) => (n.parent, n.prev_sibling, n.next_sibling),
None => return,
};
if let Some(parent_id) = parent_id {
if let Some(prev_id) = prev {
if let Some(p) = self.nodes.get_mut(prev_id.0) {
p.next_sibling = next;
}
} else if let Some(p) = self.nodes.get_mut(parent_id.0) {
p.first_child = next;
}
if let Some(next_id) = next {
if let Some(n) = self.nodes.get_mut(next_id.0) {
n.prev_sibling = prev;
}
} else if let Some(p) = self.nodes.get_mut(parent_id.0) {
p.last_child = prev;
}
if let Some(node) = self.nodes.get_mut(id.0) {
node.parent = None;
node.prev_sibling = None;
node.next_sibling = None;
}
}
}
pub fn first_child(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.first_child)
}
pub fn last_child(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.last_child)
}
pub fn next_sibling(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.next_sibling)
}
pub fn previous_sibling(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.prev_sibling)
}
pub fn ancestors(&self, id: NodeId) -> Vec<NodeId> {
let mut result = Vec::new();
let mut current = self.parent(id);
while let Some(pid) = current {
result.push(pid);
current = self.parent(pid);
}
result
}
pub fn descendants(&self, id: NodeId) -> Vec<NodeId> {
let mut result = Vec::new();
self.collect_descendants(id, &mut result);
result
}
fn collect_descendants(&self, id: NodeId, result: &mut Vec<NodeId>) {
for child in self.children(id) {
result.push(child);
self.collect_descendants(child, result);
}
}
pub fn to_xml(&self) -> String {
let mut output = String::new();
self.write_document_to(&mut output, &XmlWriteOptions::default())
.unwrap();
output
}
pub fn to_xml_with_options(&self, opts: &XmlWriteOptions) -> String {
let mut output = String::new();
self.write_document_to(&mut output, opts).unwrap();
output
}
pub fn node_to_xml(&self, id: NodeId) -> String {
let mut output = String::new();
self.write_node_to(id, &mut output, &XmlWriteOptions::default(), 0, false)
.unwrap();
output
}
pub fn node_to_xml_with_options(&self, id: NodeId, opts: &XmlWriteOptions) -> String {
let mut output = String::new();
self.write_node_to(id, &mut output, opts, 0, false).unwrap();
output
}
pub fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
let opts = XmlWriteOptions::default();
self.write_to_with_options(writer, &opts)
}
pub fn write_to_with_options(
&self,
writer: &mut dyn std::io::Write,
opts: &XmlWriteOptions,
) -> std::io::Result<()> {
let mut adapter = IoWriteAdapter { inner: writer };
self.write_document_to(&mut adapter, opts)
.map_err(|e| std::io::Error::other(e.to_string()))
}
fn write_document_to(&self, out: &mut dyn fmt::Write, opts: &XmlWriteOptions) -> fmt::Result {
if let Some(decl) = &self.xml_declaration {
out.write_str("<?xml version=\"")?;
out.write_str(&crate::writer::safe_xml_version(&decl.version))?;
out.write_char('"')?;
if let Some(enc) = &decl.encoding {
out.write_str(" encoding=\"")?;
out.write_str(&crate::writer::safe_xml_encoding(enc))?;
out.write_char('"')?;
}
if let Some(sa) = decl.standalone {
out.write_str(" standalone=\"")?;
out.write_str(if sa { "yes" } else { "no" })?;
out.write_char('"')?;
}
out.write_str("?>")?;
}
if let Some(dt) = &self.doctype {
out.write_str(dt)?;
}
for child in self.children(self.root) {
self.write_node_to(child, out, opts, 0, opts.indent.is_some())?;
}
Ok(())
}
fn write_node_to(
&self,
id: NodeId,
out: &mut dyn fmt::Write,
opts: &XmlWriteOptions,
depth: usize,
indent_self: bool,
) -> fmt::Result {
match self.node_kind(id) {
Some(NodeKind::Element(elem)) => {
if indent_self {
write_indent(out, opts, depth)?;
}
out.write_char('<')?;
let pname = elem.name.prefixed_name();
out.write_str(&pname)?;
for (prefix, uri) in &elem.namespace_declarations {
if prefix.is_empty() {
out.write_str(" xmlns=\"")?;
} else {
out.write_str(" xmlns:")?;
out.write_str(prefix)?;
out.write_str("=\"")?;
}
write_escaped_attr(out, uri)?;
out.write_char('"')?;
}
for attr in &elem.attributes {
out.write_char(' ')?;
let aname = attr.name.prefixed_name();
out.write_str(&aname)?;
out.write_str("=\"")?;
write_escaped_attr(out, &attr.value)?;
out.write_char('"')?;
}
let children = self.children(id);
if children.is_empty() {
if opts.expand_empty_elements {
out.write_str("></")?;
out.write_str(&pname)?;
out.write_char('>')?;
} else {
out.write_str("/>")?;
}
} else {
out.write_char('>')?;
let element_only = opts.indent.is_some()
&& children.iter().all(|&cid| {
!matches!(
self.node_kind(cid),
Some(NodeKind::Text(_)) | Some(NodeKind::CData(_))
)
});
if element_only {
out.write_char('\n')?;
}
for child in &children {
self.write_node_to(*child, out, opts, depth + 1, element_only)?;
}
if element_only {
write_indent(out, opts, depth)?;
}
out.write_str("</")?;
out.write_str(&pname)?;
out.write_char('>')?;
}
if indent_self {
out.write_char('\n')?;
}
}
Some(NodeKind::Text(text)) => {
write_escaped_text(out, text)?;
}
Some(NodeKind::CData(text)) => {
out.write_str("<![CDATA[")?;
out.write_str(&crate::writer::split_cdata_content(text))?;
out.write_str("]]>")?;
}
Some(NodeKind::Comment(text)) => {
if indent_self {
write_indent(out, opts, depth)?;
}
out.write_str("<!--")?;
out.write_str(&crate::writer::sanitize_comment_content(text))?;
out.write_str("-->")?;
if indent_self {
out.write_char('\n')?;
}
}
Some(NodeKind::ProcessingInstruction(pi)) => {
if indent_self {
write_indent(out, opts, depth)?;
}
out.write_str("<?")?;
out.write_str(&crate::writer::sanitize_pi_target(&pi.target))?;
if let Some(data) = &pi.data {
out.write_char(' ')?;
out.write_str(&crate::writer::sanitize_pi_data(data))?;
}
out.write_str("?>")?;
if indent_self {
out.write_char('\n')?;
}
}
Some(NodeKind::Document) => {
for child in self.children(id) {
self.write_node_to(child, out, opts, depth, indent_self)?;
}
}
Some(NodeKind::Attribute(_, _)) => {
}
None => {}
}
Ok(())
}
}
impl<'a> Default for Document<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> fmt::Display for Document<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_document_to(f, &XmlWriteOptions::default())
}
}
#[derive(Debug, Clone)]
pub struct XmlWriteOptions {
pub indent: Option<String>,
pub expand_empty_elements: bool,
}
impl XmlWriteOptions {
pub fn compact() -> Self {
XmlWriteOptions {
indent: None,
expand_empty_elements: false,
}
}
pub fn pretty(indent: impl Into<String>) -> Self {
XmlWriteOptions {
indent: Some(indent.into()),
expand_empty_elements: false,
}
}
pub fn with_expand_empty_elements(mut self, expand: bool) -> Self {
self.expand_empty_elements = expand;
self
}
}
impl Default for XmlWriteOptions {
fn default() -> Self {
Self::compact()
}
}
fn write_indent(out: &mut dyn fmt::Write, opts: &XmlWriteOptions, depth: usize) -> fmt::Result {
if let Some(ref indent) = opts.indent {
for _ in 0..depth {
out.write_str(indent)?;
}
}
Ok(())
}
fn write_escaped_text(out: &mut dyn fmt::Write, s: &str) -> fmt::Result {
for c in s.chars() {
match c {
'&' => out.write_str("&")?,
'<' => out.write_str("<")?,
'>' => out.write_str(">")?,
'\r' => out.write_str("
")?,
_ => out.write_char(c)?,
}
}
Ok(())
}
fn write_escaped_attr(out: &mut dyn fmt::Write, s: &str) -> fmt::Result {
for c in s.chars() {
match c {
'&' => out.write_str("&")?,
'<' => out.write_str("<")?,
'>' => out.write_str(">")?,
'"' => out.write_str(""")?,
'\t' => out.write_str("	")?,
'\n' => out.write_str("
")?,
'\r' => out.write_str("
")?,
_ => out.write_char(c)?,
}
}
Ok(())
}
struct IoWriteAdapter<'w> {
inner: &'w mut dyn std::io::Write,
}
impl<'w> fmt::Write for IoWriteAdapter<'w> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.inner.write_all(s.as_bytes()).map_err(|_| fmt::Error)
}
}