#![cfg_attr(feature = "cargo-clippy", allow(collapsible_if))]
#![doc(html_root_url = "https://docs.rs/roxmltree/0.6.0")]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate xmlparser;
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
pub use xmlparser::TextPos;
mod parse;
pub use parse::*;
pub const NS_XML_URI: &str = "http://www.w3.org/XML/1998/namespace";
pub const NS_XMLNS_URI: &str = "http://www.w3.org/2000/xmlns/";
type Range = std::ops::Range<usize>;
pub struct Document<'d> {
text: &'d str,
nodes: Vec<NodeData<'d>>,
attrs: Vec<Attribute<'d>>,
namespaces: Namespaces<'d>,
}
impl<'d> Document<'d> {
pub fn root<'a>(&'a self) -> Node<'a, 'd> {
Node { id: NodeId(0), d: &self.nodes[0], doc: self }
}
pub fn root_element(&self) -> Node {
self.root().first_element_child().unwrap()
}
pub fn descendants(&self) -> Descendants {
self.root().descendants()
}
pub fn text_pos_at(&self, pos: usize) -> TextPos {
xmlparser::Stream::from(self.text).gen_text_pos_from(pos)
}
}
impl<'d> fmt::Debug for Document<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if !self.root().has_children() {
return write!(f, "Document []");
}
macro_rules! writeln_indented {
($depth:expr, $f:expr, $fmt:expr) => {
for _ in 0..$depth { write!($f, " ")?; }
writeln!($f, $fmt)?;
};
($depth:expr, $f:expr, $fmt:expr, $($arg:tt)*) => {
for _ in 0..$depth { write!($f, " ")?; }
writeln!($f, $fmt, $($arg)*)?;
};
}
fn print_vec<T: fmt::Debug>(prefix: &str, data: &[T], depth: usize, f: &mut fmt::Formatter)
-> Result<(), fmt::Error>
{
if data.is_empty() {
return Ok(());
}
writeln_indented!(depth, f, "{}: [", prefix);
for v in data {
writeln_indented!(depth + 1, f, "{:?}", v);
}
writeln_indented!(depth, f, "]");
Ok(())
}
fn print_children(parent: Node, depth: usize, f: &mut fmt::Formatter)
-> Result<(), fmt::Error>
{
for child in parent.children() {
if child.is_element() {
writeln_indented!(depth, f, "Element {{");
writeln_indented!(depth, f, " tag_name: {:?}", child.tag_name());
print_vec("attributes", child.attributes(), depth + 1, f)?;
print_vec("namespaces", child.namespaces(), depth + 1, f)?;
if child.has_children() {
writeln_indented!(depth, f, " children: [");
print_children(child, depth + 2, f)?;
writeln_indented!(depth, f, " ]");
}
writeln_indented!(depth, f, "}}");
} else {
writeln_indented!(depth, f, "{:?}", child);
}
}
Ok(())
}
writeln!(f, "Document [")?;
print_children(self.root(), 1, f)?;
writeln!(f, "]")?;
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum NodeType {
Root,
Element,
PI,
Comment,
Text,
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub struct PI<'d> {
pub target: &'d str,
pub value: Option<&'d str>,
}
#[derive(Clone, Copy, PartialEq)]
struct NodeId(usize);
enum NodeKind<'d> {
Root,
Element {
tag_name: ExpandedNameOwned<'d>,
attributes: Range,
namespaces: Range,
},
PI(PI<'d>),
Comment(&'d str),
Text(Cow<'d, str>),
}
struct NodeData<'d> {
parent: Option<NodeId>,
prev_sibling: Option<NodeId>,
next_sibling: Option<NodeId>,
children: Option<(NodeId, NodeId)>,
kind: NodeKind<'d>,
range: Range,
}
pub struct Attribute<'d> {
name: ExpandedNameOwned<'d>,
value: Cow<'d, str>,
range: Range,
value_range: Range,
}
impl<'d> Attribute<'d> {
pub fn namespace(&self) -> Option<&str> {
self.name.ns.as_ref().map(Uri::as_str)
}
pub fn name(&self) -> &str {
self.name.name
}
pub fn value(&self) -> &str {
&self.value
}
pub fn range(&self) -> Range {
self.range.clone()
}
pub fn value_range(&self) -> Range {
self.value_range.clone()
}
}
impl<'d> PartialEq for Attribute<'d> {
fn eq(&self, other: &Attribute<'d>) -> bool {
self.name == other.name && self.value == other.value
}
}
impl<'d> fmt::Debug for Attribute<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Attribute {{ name: {:?}, value: {:?} }}",
self.name, self.value)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct Namespace<'d> {
name: Option<&'d str>,
uri: Uri,
}
impl<'d> Namespace<'d> {
pub fn name(&self) -> Option<&str> {
self.name
}
pub fn uri(&self) -> &str {
self.uri.as_str()
}
}
struct Namespaces<'d>(Vec<Namespace<'d>>);
impl<'d> Namespaces<'d> {
fn push_ns(&mut self, name: Option<&'d str>, uri: String) {
debug_assert_ne!(name, Some(""));
self.0.push(Namespace {
name,
uri: Uri::new(uri),
});
}
fn xml_uri(&self) -> Uri {
self[0].uri.clone()
}
fn exists(&self, start: usize, prefix: Option<&str>) -> bool {
self[start..].iter().any(|ns| ns.name == prefix)
}
}
impl<'d> Deref for Namespaces<'d> {
type Target = Vec<Namespace<'d>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct Uri(Rc<String>);
impl Uri {
fn new(text: String) -> Self {
Uri(Rc::new(text))
}
fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl Clone for Uri {
fn clone(&self) -> Self {
Uri(Rc::clone(&self.0))
}
}
impl PartialEq for Uri {
fn eq(&self, other: &Uri) -> bool {
self.0.as_str() == other.0.as_str()
}
}
impl fmt::Debug for Uri {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.0)
}
}
#[derive(Clone, PartialEq)]
struct ExpandedNameOwned<'d> {
ns: Option<Uri>,
name: &'d str,
}
impl<'d> ExpandedNameOwned<'d> {
fn as_ref(&self) -> ExpandedName {
ExpandedName { uri: self.ns.as_ref().map(Uri::as_str), name: self.name }
}
}
impl<'d> fmt::Debug for ExpandedNameOwned<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.ns {
Some(ref ns) => write!(f, "{{{}}}{}", ns.as_str(), self.name),
None => write!(f, "{}", self.name),
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct ExpandedName<'d> {
uri: Option<&'d str>,
name: &'d str,
}
impl<'d> ExpandedName<'d> {
pub fn namespace(&self) -> Option<&'d str> {
self.uri
}
pub fn name(&self) -> &'d str {
self.name
}
}
impl<'d> fmt::Debug for ExpandedName<'d> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.namespace() {
Some(ns) => write!(f, "{{{}}}{}", ns, self.name),
None => write!(f, "{}", self.name),
}
}
}
impl<'d> From<&'d str> for ExpandedName<'d> {
fn from(v: &'d str) -> Self {
ExpandedName {
uri: None,
name: v,
}
}
}
impl<'d> From<(&'d str, &'d str)> for ExpandedName<'d> {
fn from(v: (&'d str, &'d str)) -> Self {
ExpandedName {
uri: Some(v.0),
name: v.1,
}
}
}
#[derive(Clone, Copy)]
pub struct Node<'a, 'd: 'a> {
id: NodeId,
doc: &'a Document<'d>,
d: &'a NodeData<'d>,
}
impl<'a, 'd> Eq for Node<'a, 'd> {}
impl<'a, 'd> PartialEq for Node<'a, 'd> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.doc as *const _ == other.doc as *const _
&& self.d as *const _ == other.d as *const _
}
}
impl<'a, 'd: 'a> Node<'a, 'd> {
pub fn node_type(&self) -> NodeType {
match self.d.kind {
NodeKind::Root => NodeType::Root,
NodeKind::Element { .. } => NodeType::Element,
NodeKind::PI { .. } => NodeType::PI,
NodeKind::Comment(_) => NodeType::Comment,
NodeKind::Text(_) => NodeType::Text,
}
}
pub fn is_root(&self) -> bool {
self.node_type() == NodeType::Root
}
pub fn is_element(&self) -> bool {
self.node_type() == NodeType::Element
}
pub fn is_pi(&self) -> bool {
self.node_type() == NodeType::PI
}
pub fn is_comment(&self) -> bool {
self.node_type() == NodeType::Comment
}
pub fn is_text(&self) -> bool {
self.node_type() == NodeType::Text
}
pub fn document(&self) -> &'a Document<'d> {
self.doc
}
pub fn tag_name(&self) -> ExpandedName<'a> {
match self.d.kind {
NodeKind::Element { ref tag_name, .. } => tag_name.as_ref(),
_ => "".into()
}
}
pub fn has_tag_name<N>(&self, name: N) -> bool
where N: Into<ExpandedName<'a>>
{
let name = name.into();
match self.d.kind {
NodeKind::Element { ref tag_name, .. } => {
match name.namespace() {
Some(_) => tag_name.as_ref() == name,
None => tag_name.name == name.name,
}
}
_ => false,
}
}
pub fn default_namespace(&self) -> Option<&'a str> {
self.namespaces().iter().find(|ns| ns.name.is_none()).map(|v| v.uri.as_str())
}
pub fn resolve_tag_name_prefix(&self) -> Option<&'a str> {
if !self.is_element() {
return None;
}
let tag_ns = self.tag_name();
if self.default_namespace() == tag_ns.namespace() {
return None;
}
self.namespaces().iter().find(|ns| Some(ns.uri()) == tag_ns.namespace())
.map(|v| v.name).unwrap_or(None)
}
pub fn lookup_prefix(&self, uri: &str) -> Option<&'a str> {
if uri == NS_XML_URI {
return Some("xml");
}
self.namespaces().iter().find(|ns| ns.uri.as_str() == uri).map(|v| v.name).unwrap_or(None)
}
pub fn lookup_namespace_uri(&self, prefix: Option<&'a str>) -> Option<&'a str> {
self.namespaces().iter().find(|ns| ns.name == prefix).map(|v| v.uri.as_str())
}
pub fn attribute<N>(&self, name: N) -> Option<&'a str>
where N: Into<ExpandedName<'a>>
{
let name = name.into();
self.attributes().iter().find(|a| a.name.as_ref() == name).map(|a| a.value.as_ref())
}
pub fn attribute_node<N>(&self, name: N) -> Option<&'a Attribute<'d>>
where N: Into<ExpandedName<'a>>
{
let name = name.into();
self.attributes().iter().find(|a| a.name.as_ref() == name)
}
pub fn has_attribute<N>(&self, name: N) -> bool
where N: Into<ExpandedName<'a>>
{
let name = name.into();
self.attributes().iter().any(|a| a.name.as_ref() == name)
}
pub fn attributes(&self) -> &'a [Attribute<'d>] {
match self.d.kind {
NodeKind::Element { ref attributes, .. } => &self.doc.attrs[attributes.clone()],
_ => &[],
}
}
pub fn namespaces(&self) -> &'a [Namespace<'d>] {
match self.d.kind {
NodeKind::Element { ref namespaces, .. } => {
&self.doc.namespaces[namespaces.clone()]
}
_ => &[],
}
}
pub fn text(&self) -> Option<&'a str> {
match self.d.kind {
NodeKind::Element { .. } => {
match self.first_child() {
Some(child) if child.is_text() => {
match self.doc.nodes[child.id.0].kind {
NodeKind::Text(ref text) => Some(text),
_ => None
}
}
_ => None,
}
}
NodeKind::Comment(text) => Some(text),
NodeKind::Text(ref text) => Some(text),
_ => None,
}
}
pub fn tail(&self) -> Option<&'a str> {
if !self.is_element() {
return None;
}
match self.next_sibling().map(|n| n.id) {
Some(id) => {
match self.doc.nodes[id.0].kind {
NodeKind::Text(ref text) => Some(text),
_ => None
}
}
None => None,
}
}
pub fn pi(&self) -> Option<PI<'d>> {
match self.d.kind {
NodeKind::PI(pi) => Some(pi),
_ => None,
}
}
fn gen_node(&self, id: NodeId) -> Node<'a, 'd> {
Node { id, d: &self.doc.nodes[id.0], doc: self.doc }
}
pub fn parent(&self) -> Option<Self> {
self.d.parent.map(|id| self.gen_node(id))
}
pub fn parent_element(&self) -> Option<Self> {
self.ancestors().filter(|n| n.is_element()).nth(0)
}
pub fn prev_sibling(&self) -> Option<Self> {
self.d.prev_sibling.map(|id| self.gen_node(id))
}
pub fn next_sibling(&self) -> Option<Self> {
self.d.next_sibling.map(|id| self.gen_node(id))
}
pub fn first_child(&self) -> Option<Self> {
self.d.children.map(|(id, _)| self.gen_node(id))
}
pub fn first_element_child(&self) -> Option<Self> {
self.children().filter(|n| n.is_element()).nth(0)
}
pub fn last_child(&self) -> Option<Self> {
self.d.children.map(|(_, id)| self.gen_node(id))
}
pub fn last_element_child(&self) -> Option<Self> {
self.children().filter(|n| n.is_element()).last()
}
pub fn has_siblings(&self) -> bool {
self.d.prev_sibling.is_some() || self.d.next_sibling.is_some()
}
pub fn has_children(&self) -> bool {
self.d.children.is_some()
}
pub fn ancestors(&self) -> Ancestors<'a, 'd> {
Ancestors(self.parent())
}
pub fn prev_siblings(&self) -> PrevSiblings<'a, 'd> {
PrevSiblings(self.prev_sibling())
}
pub fn next_siblings(&self) -> NextSiblings<'a, 'd> {
NextSiblings(self.next_sibling())
}
pub fn first_children(&self) -> FirstChildren<'a, 'd> {
FirstChildren(self.first_child())
}
pub fn last_children(&self) -> LastChildren<'a, 'd> {
LastChildren(self.last_child())
}
pub fn children(&self) -> Children<'a, 'd> {
Children { front: self.first_child(), back: self.last_child() }
}
pub fn traverse(&self) -> Traverse<'a, 'd> {
Traverse { root: *self, edge: None }
}
pub fn descendants(&self) -> Descendants<'a, 'd> {
Descendants(self.traverse())
}
pub fn range(&self) -> Range {
self.d.range.clone()
}
}
impl<'a, 'd: 'a> fmt::Debug for Node<'a, 'd> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.d.kind {
NodeKind::Root => write!(f, "Root"),
NodeKind::Element { .. } => {
write!(f, "Element {{ tag_name: {:?}, attributes: {:?}, namespaces: {:?} }}",
self.tag_name(), self.attributes(), self.namespaces())
}
NodeKind::PI(pi) => {
write!(f, "PI {{ target: {:?}, value: {:?} }}", pi.target, pi.value)
}
NodeKind::Comment(text) => write!(f, "Comment({:?})", text),
NodeKind::Text(ref text) => write!(f, "Text({:?})", text),
}
}
}
macro_rules! axis_iterators {
($(#[$m:meta] $i:ident($f:path);)*) => {
$(
#[$m]
#[derive(Clone)]
pub struct $i<'a, 'd: 'a>(Option<Node<'a, 'd>>);
impl<'a, 'd: 'a> Iterator for $i<'a, 'd> {
type Item = Node<'a, 'd>;
fn next(&mut self) -> Option<Self::Item> {
let node = self.0.take();
self.0 = node.as_ref().and_then($f);
node
}
}
)*
};
}
axis_iterators! {
Ancestors(Node::parent);
PrevSiblings(Node::prev_sibling);
NextSiblings(Node::next_sibling);
FirstChildren(Node::first_child);
LastChildren(Node::last_child);
}
#[derive(Clone)]
pub struct Children<'a, 'd: 'a> {
front: Option<Node<'a, 'd>>,
back: Option<Node<'a, 'd>>,
}
impl<'a, 'd: 'a> Iterator for Children<'a, 'd> {
type Item = Node<'a, 'd>;
fn next(&mut self) -> Option<Self::Item> {
if self.front == self.back {
let node = self.front.take();
self.back = None;
node
} else {
let node = self.front.take();
self.front = node.as_ref().and_then(Node::next_sibling);
node
}
}
}
impl<'a, 'd: 'a> DoubleEndedIterator for Children<'a, 'd> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.back == self.front {
let node = self.back.take();
self.front = None;
node
} else {
let node = self.back.take();
self.back = node.as_ref().and_then(Node::prev_sibling);
node
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Edge<'a, 'd: 'a> {
Open(Node<'a, 'd>),
Close(Node<'a, 'd>),
}
#[derive(Clone)]
pub struct Traverse<'a, 'd: 'a> {
root: Node<'a, 'd>,
edge: Option<Edge<'a, 'd>>,
}
impl<'a, 'd: 'a> Iterator for Traverse<'a, 'd> {
type Item = Edge<'a, 'd>;
fn next(&mut self) -> Option<Self::Item> {
match self.edge {
Some(Edge::Open(node)) => {
self.edge = Some(match node.first_child() {
Some(first_child) => Edge::Open(first_child),
None => Edge::Close(node),
});
}
Some(Edge::Close(node)) => {
if node == self.root {
self.edge = None;
} else if let Some(next_sibling) = node.next_sibling() {
self.edge = Some(Edge::Open(next_sibling));
} else {
self.edge = node.parent().map(Edge::Close);
}
}
None => {
self.edge = Some(Edge::Open(self.root));
}
}
self.edge
}
}
#[derive(Clone)]
pub struct Descendants<'a, 'd: 'a>(Traverse<'a, 'd>);
impl<'a, 'd: 'a> Iterator for Descendants<'a, 'd> {
type Item = Node<'a, 'd>;
fn next(&mut self) -> Option<Self::Item> {
for edge in &mut self.0 {
if let Edge::Open(node) = edge {
return Some(node);
}
}
None
}
}