mod builder;
mod error;
mod iterators;
mod parser;
mod sync_cursor;
mod sync_iterators;
use std::fmt::Debug;
use std::marker::PhantomPinned;
use std::marker::Send;
use std::ptr::NonNull;
use std::ptr::null_mut;
use std::str::FromStr;
use crate::Arena;
use crate::ArenaStats;
use crate::NoMemory;
pub use crate::ParseError;
use super::entities::escape;
use super::entities::escape_fmt;
use super::entities::escaped_size;
pub use builder::DocumentBuilder;
use error::description;
pub use iterators::Ancestor;
pub use iterators::Attributes;
pub use iterators::Children;
pub use iterators::DescendantOrSelf;
pub use iterators::FollowingSibling;
pub use iterators::PrecedingSibling;
pub use parser::DocumentParser;
pub use sync_cursor::SyncCursor;
enum NodePayload {
Tag(*mut Tag),
CData(*mut CData),
}
struct Node {
next: *mut Node,
previous: *mut Node,
parent: *mut Node,
payload: NodePayload,
_pin: PhantomPinned,
}
struct Tag {
children: *mut Node,
last_child: *mut Node,
attributes: *mut Attribute,
last_attribute: *mut Attribute,
name: *const u8,
name_size: usize,
_pin: PhantomPinned,
}
impl Tag {
fn as_str(&self) -> &str {
unsafe {
let slice = std::slice::from_raw_parts(self.name, self.name_size);
std::str::from_utf8_unchecked(slice)
}
}
}
struct CData {
value: *const u8,
value_size: usize,
_pin: PhantomPinned,
}
impl CData {
fn as_str(&self) -> &str {
unsafe {
let slice = std::slice::from_raw_parts(self.value, self.value_size);
std::str::from_utf8_unchecked(slice)
}
}
}
struct Attribute {
next: *mut Attribute,
previous: *mut Attribute,
name: *const u8,
name_size: usize,
value: *const u8,
value_size: usize,
_pin: PhantomPinned,
}
impl Attribute {
fn name_as_str(&self) -> &str {
unsafe {
let slice = std::slice::from_raw_parts(self.name, self.name_size);
std::str::from_utf8_unchecked(slice)
}
}
fn value_as_str(&self) -> &str {
unsafe {
let slice = std::slice::from_raw_parts(self.value, self.value_size);
std::str::from_utf8_unchecked(slice)
}
}
}
trait ArenaExt {
fn alloc_node(&self, payload: NodePayload) -> Result<NonNull<Node>, NoMemory>;
fn alloc_tag(&self, tag_name: &str) -> Result<NonNull<Tag>, NoMemory>;
fn alloc_cdata(&self, cdata_value: &str) -> Result<NonNull<CData>, NoMemory>;
fn alloc_attribute(&self, name: &str, value: &str) -> Result<NonNull<Attribute>, NoMemory>;
}
impl ArenaExt for Arena {
fn alloc_node(&self, payload: NodePayload) -> Result<NonNull<Node>, NoMemory> {
let node = self.alloc_struct::<Node>()?.as_ptr();
unsafe {
(*node).next = null_mut();
(*node).previous = null_mut();
(*node).parent = null_mut();
(*node).payload = payload;
}
Ok(NonNull::new(node).unwrap())
}
fn alloc_tag(&self, tag_name: &str) -> Result<NonNull<Tag>, NoMemory> {
let name = self.push_str(tag_name)?;
let tag = self.alloc_struct::<Tag>()?.as_ptr();
unsafe {
(*tag).children = null_mut();
(*tag).last_child = null_mut();
(*tag).attributes = null_mut();
(*tag).last_attribute = null_mut();
(*tag).name = name.as_ptr();
(*tag).name_size = name.len();
}
Ok(NonNull::new(tag).unwrap())
}
fn alloc_cdata(&self, cdata_value: &str) -> Result<NonNull<CData>, NoMemory> {
let value = self.push_str(cdata_value)?;
let cdata = self.alloc_struct::<CData>()?.as_ptr();
unsafe {
(*cdata).value = value.as_ptr();
(*cdata).value_size = value.len();
}
Ok(NonNull::new(cdata).unwrap())
}
fn alloc_attribute(&self, name: &str, value: &str) -> Result<NonNull<Attribute>, NoMemory> {
let name = self.push_str(name)?;
let value = self.push_str(value)?;
let attribute = self.alloc_struct::<Attribute>()?.as_ptr();
unsafe {
(*attribute).next = null_mut();
(*attribute).previous = null_mut();
(*attribute).name = name.as_ptr();
(*attribute).name_size = name.len();
(*attribute).value = value.as_ptr();
(*attribute).value_size = value.len();
Ok(NonNull::new_unchecked(attribute))
}
}
}
struct Visitor {
going_down: bool,
current: *mut Node,
level: usize,
}
enum VisitorStep<'a> {
StartTag(&'a Tag),
EndTag(&'a Tag),
CData(&'a CData),
}
impl Visitor {
fn new(start: *mut Node) -> Visitor {
Visitor {
going_down: true,
current: start,
level: 0,
}
}
fn step(&mut self) {
unsafe {
if self.going_down
&& let NodePayload::Tag(tag) = (*self.current).payload
{
let child = (*tag).children;
if !child.is_null() {
self.current = child;
self.level += 1;
return;
}
}
if self.level == 0 {
self.current = null_mut();
return;
}
let next = (*self.current).next;
if next.is_null() {
self.level -= 1;
self.current = (*self.current).parent;
self.going_down = false;
} else {
self.current = next;
self.going_down = true;
}
}
}
fn next(&mut self) -> Option<VisitorStep<'_>> {
if self.current.is_null() {
return None;
}
unsafe {
let old = self.current;
let old_going_down = self.going_down;
self.step();
match (*old).payload {
NodePayload::Tag(tag) => {
if old_going_down {
Some(VisitorStep::StartTag(&*tag))
} else {
Some(VisitorStep::EndTag(&*tag))
}
}
NodePayload::CData(cdata) => Some(VisitorStep::CData(&*cdata)),
}
}
}
}
pub struct Document {
arena: Arena,
root_node: *mut Node,
}
impl Document {
pub fn new(root_tag_name: &str) -> Result<Document, ParseError> {
let arena = Arena::new()?;
let tag = arena.alloc_tag(root_tag_name)?.as_ptr();
let node = arena.alloc_node(NodePayload::Tag(tag))?.as_ptr();
Ok(Document {
arena,
root_node: node,
})
}
pub fn with_size_hint(
root_tag_name: &str,
xml_str_size: usize,
) -> Result<Document, ParseError> {
let arena = Arena::with_chunk_sizes((xml_str_size * 20) / 100, (xml_str_size * 95) / 100)?;
let tag = arena.alloc_tag(root_tag_name)?.as_ptr();
let node = arena.alloc_node(NodePayload::Tag(tag))?.as_ptr();
Ok(Document {
arena,
root_node: node,
})
}
pub fn root<'a>(&'a self) -> Cursor<'a> {
Cursor::new(self.root_node, &self.arena)
}
pub fn arena_stats(&self) -> ArenaStats {
self.arena.stats()
}
pub fn insert_tag<'a>(&'a self, tag_name: &str) -> Result<Cursor<'a>, ParseError> {
self.root().insert_tag(tag_name)
}
pub fn insert_cdata<'a>(&'a self, cdata: &str) -> Result<Cursor<'a>, ParseError> {
self.root().insert_cdata(cdata)
}
pub fn first_child<'a>(&'a self) -> Cursor<'a> {
self.root().first_child()
}
pub fn first_tag<'a>(&'a self) -> Cursor<'a> {
self.root().first_tag()
}
pub fn find_tag<'a>(&'a self, name: &str) -> Cursor<'a> {
self.root().find_tag(name)
}
pub fn find_tag_with_attribute<'a>(&'a self, attribute_name: &str) -> Cursor<'a> {
self.root().find_tag_with_attribute(attribute_name)
}
pub fn find_tag_with_attribute_value<'a>(
&'a self,
attribute_name: &str,
value: &str,
) -> Cursor<'a> {
self.root()
.find_tag_with_attribute_value(attribute_name, value)
}
pub fn str_size(&self) -> usize {
self.root().str_size()
}
#[expect(
clippy::inherent_to_string_shadow_display,
reason = "prereserving exact capacity makes this method significantly faster"
)]
pub fn to_string(&self) -> String {
self.root().to_string()
}
}
impl Debug for Document {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Document ({:?})", self.arena_stats())
}
}
impl std::fmt::Display for Document {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.root(), f)
}
}
impl FromStr for Document {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parser = DocumentParser::with_size_hint(s.len());
parser.parse_bytes(s.as_bytes())?;
parser.into_document()
}
}
unsafe impl Send for Document {}
macro_rules! null_cursor {
($x:expr) => {
Cursor::new(null_mut() as *mut Node, $x.arena)
};
}
macro_rules! null_cursor_guard {
($x:expr) => {
if ($x.node).is_null() {
return null_cursor!($x);
}
};
}
macro_rules! cursor_edit_guards {
($self:ident) => {{
let node = $self.get_node_ptr();
if node.is_null() {
return Err(ParseError::BadXml(description::NULL_CURSOR_EDIT));
}
node
}};
}
pub struct Cursor<'a> {
node: *mut Node,
arena: &'a Arena,
}
impl<'a> Cursor<'a> {
fn new(node: *mut Node, arena: &'a Arena) -> Cursor<'a> {
Cursor { node, arena }
}
fn get_node_ptr(&self) -> *mut Node {
self.node
}
fn visitor(&self) -> Visitor {
Visitor::new(self.node)
}
fn clear(&mut self) {
self.node = null_mut::<Node>();
}
pub fn insert_tag<'b>(self, tag_name: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
match (*node).payload {
NodePayload::CData(_) => {
Err(ParseError::BadXml(description::CDATA_CHILDREN))
}
NodePayload::Tag(tag) => {
let new_tag = self.arena.alloc_tag(tag_name)?.as_ptr();
let new_node = self.arena.alloc_node(NodePayload::Tag(new_tag))?.as_ptr();
(*new_node).parent = node;
if (*tag).children.is_null() {
(*tag).children = new_node;
}
if !(*tag).last_child.is_null() {
(*(*tag).last_child).next = new_node;
(*new_node).previous = (*tag).last_child;
}
(*tag).last_child = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
}
}
pub fn append_tag<'b>(self, tag_name: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
if (*node).parent.is_null() {
return Err(ParseError::BadXml(description::ROOT_SIBLING));
}
let new_tag = self.arena.alloc_tag(tag_name)?.as_ptr();
let new_node = self.arena.alloc_node(NodePayload::Tag(new_tag))?.as_ptr();
let parent = (*node).parent;
(*new_node).parent = parent;
let next = (*node).next;
(*new_node).next = next;
if next.is_null() {
match (*parent).payload {
NodePayload::CData(_) => {
unreachable!();
}
NodePayload::Tag(tag) => {
(*tag).last_child = new_node;
}
}
} else {
(*next).previous = new_node;
}
(*new_node).previous = node;
(*node).next = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
pub fn prepend_tag<'b>(self, tag_name: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
if (*node).parent.is_null() {
return Err(ParseError::BadXml(description::ROOT_SIBLING));
}
let new_tag = self.arena.alloc_tag(tag_name)?.as_ptr();
let new_node = self.arena.alloc_node(NodePayload::Tag(new_tag))?.as_ptr();
let parent = (*node).parent;
(*new_node).parent = parent;
let previous = (*node).previous;
(*new_node).previous = previous;
if previous.is_null() {
match (*parent).payload {
NodePayload::CData(_) => {
unreachable!();
}
NodePayload::Tag(tag) => {
(*tag).children = new_node;
}
}
} else {
(*previous).next = new_node;
}
(*new_node).next = node;
(*node).previous = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
pub fn insert_attribute<'b>(
&self,
name: &'b str,
value: &'b str,
) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
match (*node).payload {
NodePayload::CData(_) => Err(ParseError::BadXml(description::CDATA_ATTRIBUTE)),
NodePayload::Tag(tag) => {
let mut attr = (*tag).attributes;
while !attr.is_null() {
if name == (*attr).name_as_str() {
return Err(ParseError::BadXml(description::DUPLICATE_ATTRIBUTE));
}
attr = (*attr).next;
}
let attribute = self.arena.alloc_attribute(name, value)?.as_ptr();
if (*tag).attributes.is_null() {
(*tag).attributes = attribute;
}
if !(*tag).last_attribute.is_null() {
(*(*tag).last_attribute).next = attribute;
(*attribute).previous = (*tag).last_attribute;
}
(*tag).last_attribute = attribute;
Ok(Cursor::new(node, self.arena))
}
}
}
}
pub fn set_attribute<'b>(
&self,
name: &'b str,
value: Option<&'b str>,
) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
match (*node).payload {
NodePayload::CData(_) => Err(ParseError::BadXml(description::CDATA_ATTRIBUTE)),
NodePayload::Tag(tag) => {
let mut attr = (*tag).attributes;
while !attr.is_null() {
if name == (*attr).name_as_str() {
match value {
None => {
if !(*attr).next.is_null() {
(*(*attr).next).previous = (*attr).previous;
}
if !(*attr).previous.is_null() {
(*(*attr).previous).next = (*attr).next;
}
if (*tag).attributes == attr {
(*tag).attributes = (*attr).next;
}
if (*tag).last_attribute == attr {
(*tag).last_attribute = (*attr).previous;
}
}
Some(value) => {
let value = self.arena.push_str(value)?;
(*attr).value = value.as_ptr();
(*attr).value_size = value.len();
return Ok(Cursor::new(node, self.arena));
}
}
}
attr = (*attr).next;
}
match value {
None => {
Ok(Cursor::new(node, self.arena))
}
Some(value) => {
let attribute = self.arena.alloc_attribute(name, value)?.as_ptr();
if (*tag).attributes.is_null() {
(*tag).attributes = attribute;
}
if !(*tag).last_attribute.is_null() {
(*(*tag).last_attribute).next = attribute;
(*attribute).previous = (*tag).last_attribute;
}
(*tag).last_attribute = attribute;
Ok(Cursor::new(node, self.arena))
}
}
}
}
}
}
pub fn insert_cdata<'b>(self, cdata: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
match (*node).payload {
NodePayload::CData(_) => Err(ParseError::BadXml(description::CDATA_CHILDREN)),
NodePayload::Tag(tag) => {
let last = (*tag).last_child;
if !last.is_null()
&& let NodePayload::CData(cdata_node) = (*last).payload
{
let old_s = (*cdata_node).as_str();
let s = self.arena.concat_str(old_s, cdata)?;
(*cdata_node).value = s.as_ptr();
(*cdata_node).value_size = s.len();
return Ok(Cursor::new(last, self.arena));
}
let new_cdata = self.arena.alloc_cdata(cdata)?.as_ptr();
let new_node = self
.arena
.alloc_node(NodePayload::CData(new_cdata))?
.as_ptr();
(*new_node).parent = node;
if (*tag).children.is_null() {
(*tag).children = new_node;
}
if !last.is_null() {
(*last).next = new_node;
(*new_node).previous = last;
}
(*tag).last_child = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
}
}
pub fn append_cdata<'b>(self, cdata: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
if (*node).parent.is_null() {
return Err(ParseError::BadXml(description::ROOT_SIBLING));
}
if let NodePayload::CData(old_cdata) = (*node).payload {
let old_s = (*old_cdata).as_str();
let s = self.arena.concat_str(old_s, cdata)?;
(*old_cdata).value = s.as_ptr();
(*old_cdata).value_size = s.len();
return Ok(Cursor::new(node, self.arena));
}
let new_cdata = self.arena.alloc_cdata(cdata)?.as_ptr();
let new_node = self
.arena
.alloc_node(NodePayload::CData(new_cdata))?
.as_ptr();
let parent = (*node).parent;
(*new_node).parent = parent;
let next = (*node).next;
(*new_node).next = next;
if next.is_null() {
match (*parent).payload {
NodePayload::CData(_) => {
unreachable!();
}
NodePayload::Tag(tag) => {
(*tag).last_child = new_node;
}
}
} else {
(*next).previous = new_node;
}
(*new_node).previous = node;
(*node).next = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
pub fn prepend_cdata<'b>(self, cdata: &'b str) -> Result<Cursor<'a>, ParseError> {
let node = cursor_edit_guards!(self);
unsafe {
if (*node).parent.is_null() {
return Err(ParseError::BadXml(description::ROOT_SIBLING));
}
let new_cdata = self.arena.alloc_cdata(cdata)?.as_ptr();
let new_node = self
.arena
.alloc_node(NodePayload::CData(new_cdata))?
.as_ptr();
let parent = (*node).parent;
(*new_node).parent = parent;
let previous = (*node).previous;
(*new_node).previous = previous;
if previous.is_null() {
match (*parent).payload {
NodePayload::CData(_) => {
unreachable!();
}
NodePayload::Tag(tag) => {
(*tag).children = new_node;
}
}
} else {
(*previous).next = new_node;
}
(*new_node).next = node;
(*node).previous = new_node;
Ok(Cursor::new(new_node, self.arena))
}
}
pub fn remove(self) {
let node = self.get_node_ptr();
if node.is_null() {
return;
}
unsafe {
let parent = (*node).parent;
if parent.is_null() {
return;
}
if !(*node).next.is_null() {
(*(*node).next).previous = (*node).previous;
}
if !(*node).previous.is_null() {
(*(*node).previous).next = (*node).next;
}
match (*parent).payload {
NodePayload::Tag(tag) => {
if (*tag).children == node {
(*tag).children = (*node).next;
}
if (*tag).last_child == node {
(*tag).last_child = (*node).previous;
}
}
NodePayload::CData(_) => {}
}
(*node).parent = null_mut();
(*node).next = null_mut();
(*node).previous = null_mut();
}
}
pub fn next(self) -> Cursor<'a> {
null_cursor_guard!(self);
unsafe {
let node = self.node;
Cursor::new((*node).next, self.arena)
}
}
pub fn next_tag(self) -> Cursor<'a> {
null_cursor_guard!(self);
let mut next = self.next();
loop {
if next.is_null() || next.is_tag() {
break;
}
next = next.next();
}
next
}
pub fn previous(self) -> Cursor<'a> {
null_cursor_guard!(self);
unsafe {
let node = self.node;
Cursor::new((*node).previous, self.arena)
}
}
pub fn previous_tag(self) -> Cursor<'a> {
null_cursor_guard!(self);
let mut next = self.previous();
loop {
if next.is_null() || next.is_tag() {
break;
}
next = next.previous();
}
next
}
pub fn parent(self) -> Cursor<'a> {
null_cursor_guard!(self);
unsafe {
let node = self.node;
Cursor::new((*node).parent, self.arena)
}
}
pub fn root(self) -> Cursor<'a> {
null_cursor_guard!(self);
let mut current = self;
loop {
let parent = current.clone().parent();
if parent.is_null() {
break;
}
current = parent;
}
current
}
pub fn first_child(self) -> Cursor<'a> {
null_cursor_guard!(self);
unsafe {
let node = self.node;
match (*node).payload {
NodePayload::CData(_) => {
null_cursor!(self)
}
NodePayload::Tag(tag) => Cursor::new((*tag).children, self.arena),
}
}
}
pub fn last_child(self) -> Cursor<'a> {
null_cursor_guard!(self);
unsafe {
let node = self.node;
match (*node).payload {
NodePayload::CData(_) => {
null_cursor!(self)
}
NodePayload::Tag(tag) => Cursor::new((*tag).last_child, self.arena),
}
}
}
pub fn first_tag(self) -> Cursor<'a> {
null_cursor_guard!(self);
let child = self.clone().first_child();
if child.is_null() {
null_cursor!(self)
} else if child.is_tag() {
child
} else {
child.next_tag()
}
}
pub fn find_tag(self, name: &str) -> Cursor<'a> {
let mut child = self.first_child();
while !child.is_null() {
if child.name() == name {
break;
}
child = child.next();
}
child
}
pub fn find_tag_with_attribute(self, attribute_name: &str) -> Cursor<'a> {
let mut child = self.first_child();
while !child.is_null() {
if child.attribute(attribute_name).is_some() {
break;
}
child = child.next();
}
child
}
pub fn find_tag_with_attribute_value(self, attribute_name: &str, value: &str) -> Cursor<'a> {
let mut child = self.first_child();
while !child.is_null() {
if let Some(actual_value) = child.attribute(attribute_name)
&& actual_value == value
{
break;
}
child = child.next();
}
child
}
pub fn children(self) -> Children<'a> {
Children::new(self)
}
pub fn attributes(self) -> Attributes<'a> {
Attributes::new(self)
}
pub fn following_sibling(self) -> FollowingSibling<'a> {
FollowingSibling::new(self)
}
pub fn descendant_or_self(self) -> DescendantOrSelf<'a> {
DescendantOrSelf::new(self)
}
pub fn ancestor(self) -> Ancestor<'a> {
Ancestor::new(self)
}
pub fn preceding_sibling(self) -> PrecedingSibling<'a> {
PrecedingSibling::new(self)
}
pub fn is_null(&self) -> bool {
self.node.is_null()
}
pub fn is_tag(&self) -> bool {
unsafe {
let node = self.node;
if node.is_null() {
return false;
}
match (*node).payload {
NodePayload::CData(_) => false,
NodePayload::Tag(_) => true,
}
}
}
pub fn has_children(&self) -> bool {
unsafe {
let node = self.node;
if node.is_null() {
return false;
}
match (*node).payload {
NodePayload::CData(_) => false,
NodePayload::Tag(tag) => !(*tag).children.is_null(),
}
}
}
pub fn name(&self) -> &str {
unsafe {
let node = self.node;
if node.is_null() {
return "";
}
match (*node).payload {
NodePayload::CData(_) => {
""
}
NodePayload::Tag(tag) => (*tag).as_str(),
}
}
}
pub fn attribute(&self, name: &str) -> Option<&str> {
let node = self.get_node_ptr();
if node.is_null() {
return None;
}
unsafe {
if let NodePayload::Tag(tag) = (*node).payload {
let mut attr = (*tag).attributes;
while !attr.is_null() {
let attr_name = (*attr).name_as_str();
if attr_name == name {
return Some((*attr).value_as_str());
}
attr = (*attr).next;
}
}
}
None
}
pub fn cdata(&self) -> &str {
unsafe {
let node = self.node;
if node.is_null() {
return "";
}
match (*node).payload {
NodePayload::CData(cdata) => (*cdata).as_str(),
NodePayload::Tag(_) => {
""
}
}
}
}
pub fn str_size(&self) -> usize {
if self.node.is_null() {
return 0;
}
let mut size = 0;
let mut visitor = self.visitor();
while let Some(step) = visitor.next() {
match step {
VisitorStep::StartTag(tag) => {
size += 1; size += tag.name_size;
let mut attr = tag.attributes;
while !attr.is_null() {
size += 1; unsafe {
size += (*attr).name_size;
size += 2; size += escaped_size((*attr).value_as_str());
size += 1; attr = (*attr).next;
}
}
if tag.children.is_null() {
size += 2; } else {
size += 1;
}
}
VisitorStep::EndTag(tag) => {
if tag.children.is_null() {
} else {
size += 2; size += tag.name_size;
size += 1; }
}
VisitorStep::CData(cdata) => {
size += escaped_size(cdata.as_str());
}
}
}
size
}
#[expect(
clippy::inherent_to_string_shadow_display,
reason = "prereserving exact capacity makes this method significantly faster"
)]
fn to_string(&self) -> String {
let mut buf = String::with_capacity(self.str_size());
let mut visitor = self.visitor();
while let Some(step) = visitor.next() {
match step {
VisitorStep::StartTag(tag) => {
buf.push('<');
buf.push_str(tag.as_str());
let mut attr = tag.attributes;
while !attr.is_null() {
buf.push(' ');
unsafe {
buf.push_str((*attr).name_as_str());
buf.push_str("=\"");
escape((*attr).value_as_str(), &mut buf);
buf.push('"');
attr = (*attr).next;
}
}
if tag.children.is_null() {
buf.push_str("/>");
} else {
buf.push('>');
}
}
VisitorStep::EndTag(tag) => {
if tag.children.is_null() {
} else {
buf.push_str("</");
buf.push_str(tag.as_str());
buf.push('>');
}
}
VisitorStep::CData(cdata) => {
escape(cdata.as_str(), &mut buf);
}
}
}
buf
}
pub fn insert_document<'b>(self, cursor: Cursor<'b>) -> Result<Cursor<'a>, ParseError> {
if self.node.is_null() || cursor.is_null() {
return Err(ParseError::BadXml(description::NULL_CURSOR_EDIT));
}
let mut visitor = cursor.visitor();
let mut current = self.clone();
while let Some(step) = visitor.next() {
match step {
VisitorStep::StartTag(tag) => {
current = current.clone().insert_tag(tag.as_str())?;
let mut attr = tag.attributes;
while !attr.is_null() {
unsafe {
current
.insert_attribute((*attr).name_as_str(), (*attr).value_as_str())?;
attr = (*attr).next;
}
}
if tag.children.is_null() {
current = current.clone().parent();
}
}
VisitorStep::EndTag(_tag) => {
current = current.clone().parent();
}
VisitorStep::CData(cdata) => {
current.clone().insert_cdata(cdata.as_str())?;
}
}
}
Ok(self.clone())
}
pub fn to_document(&self) -> Result<Document, ParseError> {
if self.node.is_null() {
return Err(ParseError::BadXml(description::NULL_CURSOR_EDIT));
}
if !self.is_tag() {
return Err(ParseError::BadXml(description::CDATA_TO_DOCUMENT));
}
let doc = Document::with_size_hint(self.name(), self.str_size())?;
let mut visitor = self.visitor();
let _ = visitor.next(); let mut cursor = doc.root();
while let Some(step) = visitor.next() {
match step {
VisitorStep::StartTag(tag) => {
cursor = cursor.clone().insert_tag(tag.as_str())?;
let mut attr = tag.attributes;
while !attr.is_null() {
unsafe {
cursor
.insert_attribute((*attr).name_as_str(), (*attr).value_as_str())?;
attr = (*attr).next;
}
}
if tag.children.is_null() {
cursor = cursor.clone().parent();
}
}
VisitorStep::EndTag(_tag) => {
cursor = cursor.clone().parent();
}
VisitorStep::CData(cdata) => {
cursor.clone().insert_cdata(cdata.as_str())?;
}
}
}
Ok(doc)
}
}
impl Clone for Cursor<'_> {
fn clone(&self) -> Self {
Cursor {
node: self.get_node_ptr(),
arena: self.arena,
}
}
}
impl Debug for Cursor<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Cursor ({:?})", self.get_node_ptr())
}
}
impl<'a> std::fmt::Display for Cursor<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.node.is_null() {
return Result::Ok(());
}
let mut visitor = self.visitor();
while let Some(step) = visitor.next() {
match step {
VisitorStep::StartTag(tag) => {
f.write_str("<")?;
f.write_str(tag.as_str())?;
let mut attr = tag.attributes;
while !attr.is_null() {
f.write_str(" ")?;
unsafe {
f.write_str((*attr).name_as_str())?;
f.write_str("=\"")?;
escape_fmt((*attr).value_as_str(), f)?;
f.write_str("\"")?;
attr = (*attr).next;
}
}
if tag.children.is_null() {
f.write_str("/>")?;
} else {
f.write_str(">")?;
}
}
VisitorStep::EndTag(tag) => {
if tag.children.is_null() {
} else {
f.write_str("</")?;
f.write_str(tag.as_str())?;
f.write_str(">")?;
}
}
VisitorStep::CData(cdata) => {
escape_fmt(cdata.as_str(), f)?;
}
}
}
Result::Ok(())
}
}
#[cfg(test)]
mod tests;
mod nocompile;