use crate::attribute::Attributes;
use crate::defs::{NodeIdx, XmlIdx};
use crate::document::{Document, Nodes};
use crate::node_info::NodeInfo;
use crate::node_type::NodeType;
#[cfg(feature = "use_cstr")]
use std::ffi::CStr;
#[must_use]
#[derive(Debug, Clone)]
pub struct Node<'xml> {
pub idx: NodeIdx,
#[cfg(feature = "forward_only")]
pub parent_idx: NodeIdx,
pub node_info: &'xml NodeInfo,
pub doc: &'xml Document,
}
impl<'xml> Node<'xml> {
#[inline]
pub(crate) fn new(
idx: NodeIdx,
#[cfg(feature = "forward_only")] parent_idx: NodeIdx,
node_info: &'xml NodeInfo,
doc: &'xml Document,
) -> Self {
Node {
idx,
#[cfg(feature = "forward_only")]
parent_idx,
node_info,
doc,
}
}
#[inline]
#[must_use]
pub fn idx(&self) -> NodeIdx {
self.idx
}
#[inline]
#[must_use]
pub(crate) fn parent_idx(&self) -> Option<NodeIdx> {
if self.idx <= 1 {
None } else {
#[cfg(feature = "forward_only")]
if self.parent_idx != 0 {
Some(self.parent_idx)
} else {
None }
#[cfg(not(feature = "forward_only"))]
self.node_info.parent_idx()
}
}
#[inline]
#[must_use]
pub fn tag_name(&self) -> &str {
match &self.node_info.node_type() {
#[cfg(not(feature = "use_cstr"))]
NodeType::Element { name, .. } => self.doc.get_str_from_location(name.clone()),
#[cfg(feature = "use_cstr")]
NodeType::Element { name, .. } => self.doc.get_str_from_location(*name),
_ => "", }
}
#[inline]
#[must_use]
pub fn tag_name_bytes(&self) -> &[u8] {
match &self.node_info.node_type() {
#[cfg(feature = "use_cstr")]
NodeType::Element { name, .. } => self.doc.get_cstr_from_location(*name).to_bytes(),
#[cfg(not(feature = "use_cstr"))]
NodeType::Element { name, .. } => &self.doc.xml[name.start as usize..name.end as usize],
_ => b"", }
}
#[cfg(feature = "use_cstr")]
#[inline]
#[must_use]
pub fn tag_name_cstr(&self) -> &CStr {
match &self.node_info.node_type() {
NodeType::Element { name, .. } => self.doc.get_cstr_from_location(*name),
_ => c"", }
}
#[inline]
#[must_use]
pub fn is(&self, tag_name: &str) -> bool {
self.tag_name() == tag_name
}
#[inline]
#[must_use]
pub fn is_bytes(&self, tag_name: &[u8]) -> bool {
self.tag_name_bytes() == tag_name
}
#[cfg(feature = "use_cstr")]
#[inline]
#[must_use]
pub fn is_cstr(&self, tag_name: &CStr) -> bool {
self.tag_name_cstr() == tag_name
}
#[inline]
#[must_use]
pub fn text(&self) -> Option<&'xml str> {
match &self.node_info.node_type() {
#[cfg(not(feature = "use_cstr"))]
NodeType::Text(text_location) => {
Some(self.doc.get_str_from_location(text_location.clone()))
}
#[cfg(feature = "use_cstr")]
NodeType::Text(text_location) => Some(self.doc.get_str_from_location(*text_location)),
_ => None,
}
}
#[inline]
#[must_use]
pub fn text_bytes(&self) -> Option<&'xml [u8]> {
match &self.node_info.node_type() {
#[cfg(not(feature = "use_cstr"))]
NodeType::Text(text_location) => {
Some(&self.doc.xml[text_location.start as usize..text_location.end as usize])
}
#[cfg(feature = "use_cstr")]
NodeType::Text(text_location) => {
Some(self.doc.get_cstr_from_location(*text_location).to_bytes())
}
_ => None,
}
}
#[cfg(feature = "use_cstr")]
#[inline]
#[must_use]
pub fn text_cstr(&self) -> Option<&'xml CStr> {
match &self.node_info.node_type() {
NodeType::Text(text_location) => Some(self.doc.get_cstr_from_location(*text_location)),
_ => None,
}
}
#[inline]
#[must_use]
pub fn attributes(&self) -> Attributes<'xml> {
Attributes::new(self)
}
#[inline]
#[must_use]
pub fn first_child_idx(&self) -> Option<NodeIdx> {
if self.node_info.first_child_idx() == 0 {
None
} else {
Some(self.node_info.first_child_idx())
}
}
#[inline]
#[must_use]
pub fn first_child(&self) -> Option<Node<'xml>> {
self.first_child_idx().map(|first_child_idx| {
Node::new(
first_child_idx,
#[cfg(feature = "forward_only")]
self.idx,
&self.doc.nodes[first_child_idx as usize],
self.doc,
)
})
}
#[cfg(not(feature = "forward_only"))]
#[inline]
#[must_use]
pub fn last_child(&self) -> Option<Node<'xml>> {
if self.node_info.first_child_idx() == 0 {
None
} else {
let first_child_idx = self.node_info.first_child_idx();
let last_child_idx = self.doc.nodes[first_child_idx as usize].prev_sibling_idx();
Some(Node::new(
last_child_idx,
&self.doc.nodes[last_child_idx as usize],
self.doc,
))
}
}
#[inline]
#[must_use]
pub fn next_sibling(&self) -> Option<Node<'xml>> {
if self.node_info.next_sibling_idx() == 0 {
None
} else {
Some(Node::new(
self.node_info.next_sibling_idx(),
#[cfg(feature = "forward_only")]
self.parent_idx,
&self.doc.nodes[self.node_info.next_sibling_idx() as usize],
self.doc,
))
}
}
#[cfg(not(feature = "forward_only"))]
#[inline]
#[must_use]
pub fn prev_sibling(&self) -> Option<Node<'xml>> {
let node_info = &self.doc.nodes[self.node_info.prev_sibling_idx() as usize];
if node_info.next_sibling_idx() == 0 {
None } else {
Some(Node::new(
self.node_info.prev_sibling_idx(),
#[cfg(feature = "forward_only")]
self.parent_idx,
node_info,
self.doc,
))
}
}
#[inline]
#[must_use]
pub fn children(&self) -> NodeChildren<'xml> {
if self.has_children() {
#[cfg(not(feature = "forward_only"))]
{
NodeChildren {
front: self.first_child(),
back: self.last_child(),
}
}
#[cfg(feature = "forward_only")]
{
NodeChildren {
front: self.first_child(),
back: None,
}
}
} else {
NodeChildren {
front: None,
back: None,
}
}
}
#[inline]
#[must_use]
pub fn descendants(&self) -> Nodes<'xml> {
Nodes::descendants(self.doc, self.idx)
}
#[inline]
#[must_use]
pub fn is_root(&self) -> bool {
self.idx == 1 }
#[inline]
#[must_use]
pub fn has_children(&self) -> bool {
self.first_child_idx().is_some()
}
#[inline]
#[must_use]
pub fn is_element(&self) -> bool {
matches!(self.node_info.node_type(), NodeType::Element { .. })
}
#[inline]
#[must_use]
pub fn is_text(&self) -> bool {
matches!(self.node_info.node_type(), NodeType::Text(_))
}
#[inline]
#[must_use]
pub fn get_node_type(&self) -> &NodeType {
self.node_info.node_type()
}
#[must_use]
pub fn get_child(&self, tag_name: &str) -> Option<Node<'xml>> {
self.children().find(|child| child.is(tag_name))
}
#[must_use]
pub fn get_sibling(&self, tag_name: &str) -> Option<Node<'xml>> {
self.parent()
.and_then(|parent| parent.children().find(|sibling| sibling.is(tag_name)))
}
#[inline]
#[must_use]
pub fn get_attribute(&self, name: &str) -> Option<&'xml str> {
for attr in self.attributes() {
if attr.name() == name {
return Some(attr.value());
}
}
None
}
#[inline]
#[must_use]
pub fn parent(&self) -> Option<Node<'xml>> {
#[cfg(not(feature = "forward_only"))]
return self.parent_idx().map(|parent_idx| {
Node::new(parent_idx, &self.doc.nodes[parent_idx as usize], self.doc)
});
#[cfg(feature = "forward_only")]
return self.parent_idx().map(|parent_idx| {
Node::new(
parent_idx,
0, &self.doc.nodes[parent_idx as usize],
self.doc,
)
});
}
#[inline]
#[must_use]
pub fn position(&self) -> XmlIdx {
self.node_info.position()
}
}
impl Eq for Node<'_> {}
impl PartialEq for Node<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
pub struct NodeChildren<'a> {
front: Option<Node<'a>>,
back: Option<Node<'a>>,
}
impl<'a> Iterator for NodeChildren<'a> {
type Item = Node<'a>;
#[inline]
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
}
}
}
#[cfg(not(feature = "forward_only"))]
impl DoubleEndedIterator for NodeChildren<'_> {
#[inline]
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
}
}
}