use libc::{c_char, c_int};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr;
use std::rc::{Rc, Weak};
use std::str;
use crate::bindings::*;
use crate::readonly::RoNode;
use crate::tree::node::Node;
pub(crate) type DocumentRef = Rc<RefCell<_Document>>;
pub(crate) type DocumentWeak = Weak<RefCell<_Document>>;
#[derive(Debug, Copy, Clone, Default)]
pub struct SaveOptions {
pub format: bool,
pub no_declaration: bool,
pub no_empty_tags: bool,
pub no_xhtml: bool,
pub xhtml: bool,
pub as_xml: bool,
pub as_html: bool,
pub non_significant_whitespace: bool,
}
#[derive(Debug)]
pub(crate) struct _Document {
pub(crate) doc_ptr: xmlDocPtr,
nodes: HashMap<xmlNodePtr, Node>,
}
impl _Document {
pub(crate) fn insert_node(&mut self, node_ptr: xmlNodePtr, node: Node) {
self.nodes.insert(node_ptr, node);
}
pub(crate) fn get_node(&self, node_ptr: xmlNodePtr) -> Option<&Node> {
self.nodes.get(&node_ptr)
}
pub(crate) fn forget_node(&mut self, node_ptr: xmlNodePtr) {
self.nodes.remove(&node_ptr);
}
}
#[derive(Clone)]
pub struct Document(pub(crate) DocumentRef);
impl Drop for _Document {
fn drop(&mut self) {
unsafe {
if !self.doc_ptr.is_null() {
xmlFreeDoc(self.doc_ptr);
}
}
}
}
impl fmt::Display for Document {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string_with_options(SaveOptions::default()))
}
}
impl Document {
pub fn new() -> Result<Self, ()> {
unsafe {
let c_version = CString::new("1.0").unwrap();
let c_version_bytes = c_version.as_bytes();
let doc_ptr = xmlNewDoc(c_version_bytes.as_ptr());
if doc_ptr.is_null() {
Err(())
} else {
let doc = _Document {
doc_ptr,
nodes: HashMap::new(),
};
Ok(Document(Rc::new(RefCell::new(doc))))
}
}
}
pub fn doc_ptr(&self) -> xmlDocPtr {
self.0.borrow().doc_ptr
}
pub fn new_ptr(doc_ptr: xmlDocPtr) -> Self {
let doc = _Document {
doc_ptr,
nodes: HashMap::new(),
};
Document(Rc::new(RefCell::new(doc)))
}
pub(crate) fn null_ref() -> DocumentRef {
Rc::new(RefCell::new(_Document {
doc_ptr: ptr::null_mut(),
nodes: HashMap::new(),
}))
}
pub fn save_file(&self, filename: &str) -> Result<c_int, ()> {
let c_filename = CString::new(filename).unwrap();
unsafe {
let retval = xmlSaveFile(c_filename.as_ptr(), self.doc_ptr());
if retval < 0 {
return Err(());
}
Ok(retval)
}
}
pub(crate) fn register_node(&self, node_ptr: xmlNodePtr) -> Node {
Node::wrap(node_ptr, &self.0)
}
pub fn get_root_element(&self) -> Option<Node> {
unsafe {
let node_ptr = xmlDocGetRootElement(self.doc_ptr());
if node_ptr.is_null() {
None
} else {
Some(self.register_node(node_ptr))
}
}
}
pub fn get_root_readonly(&self) -> Option<RoNode> {
unsafe {
let node_ptr = xmlDocGetRootElement(self.doc_ptr());
if node_ptr.is_null() {
None
} else {
Some(RoNode(node_ptr))
}
}
}
pub fn set_root_element(&mut self, root: &Node) {
unsafe {
xmlDocSetRootElement(self.doc_ptr(), root.node_ptr());
}
root.set_linked();
}
fn ptr_as_result(&mut self, node_ptr: xmlNodePtr) -> Result<Node, ()> {
if node_ptr.is_null() {
Err(())
} else {
let node = self.register_node(node_ptr);
Ok(node)
}
}
pub fn import_node(&mut self, node: &mut Node) -> Result<Node, ()> {
if !node.is_unlinked() {
return Err(());
}
node
.get_docref()
.upgrade()
.unwrap()
.borrow_mut()
.forget_node(node.node_ptr());
let node_ptr = unsafe { xmlDocCopyNode(node.node_ptr(), self.doc_ptr(), 1) };
node.set_linked();
self.ptr_as_result(node_ptr)
}
pub fn to_string_with_options(&self, options: SaveOptions) -> String {
unsafe {
let buf = xmlBufferCreate();
let c_utf8 = CString::new("UTF-8").unwrap();
let mut xml_options = 0;
if options.format {
xml_options += xmlSaveOption_XML_SAVE_FORMAT;
}
if options.no_declaration {
xml_options += xmlSaveOption_XML_SAVE_NO_DECL;
}
if options.no_empty_tags {
xml_options += xmlSaveOption_XML_SAVE_NO_EMPTY;
}
if options.no_xhtml {
xml_options += xmlSaveOption_XML_SAVE_NO_XHTML;
}
if options.xhtml {
xml_options += xmlSaveOption_XML_SAVE_XHTML;
}
if options.as_xml {
xml_options += xmlSaveOption_XML_SAVE_AS_XML;
}
if options.as_html {
xml_options += xmlSaveOption_XML_SAVE_AS_HTML;
}
if options.non_significant_whitespace {
xml_options += xmlSaveOption_XML_SAVE_WSNONSIG;
}
let save_ctx = xmlSaveToBuffer(buf, c_utf8.as_ptr(), xml_options as i32);
let _size = xmlSaveDoc(save_ctx, self.doc_ptr());
let _size = xmlSaveClose(save_ctx);
let result = xmlBufferContent(buf);
let c_string = CStr::from_ptr(result as *const c_char);
let node_string = c_string.to_string_lossy().into_owned();
xmlBufferFree(buf);
node_string
}
}
pub fn node_to_string(&self, node: &Node) -> String {
unsafe {
let buf = xmlBufferCreate();
xmlNodeDump(
buf,
self.doc_ptr(),
node.node_ptr(),
1, 0,
);
let result = xmlBufferContent(buf);
let c_string = CStr::from_ptr(result as *const c_char);
let node_string = c_string.to_string_lossy().into_owned();
xmlBufferFree(buf);
node_string
}
}
pub fn ronode_to_string(&self, node: &RoNode) -> String {
unsafe {
let buf = xmlBufferCreate();
xmlNodeDump(
buf,
self.doc_ptr(),
node.node_ptr(),
1, 0,
);
let result = xmlBufferContent(buf);
let c_string = CStr::from_ptr(result as *const c_char);
let node_string = c_string.to_string_lossy().into_owned();
xmlBufferFree(buf);
node_string
}
}
pub fn create_processing_instruction(&mut self, name: &str, content: &str) -> Result<Node, ()> {
unsafe {
let c_name = CString::new(name).unwrap();
let c_name_bytes = c_name.as_bytes();
let c_content = CString::new(content).unwrap();
let c_content_bytes = c_content.as_bytes();
let node_ptr: xmlNodePtr = xmlNewDocPI(
self.doc_ptr(),
c_name_bytes.as_ptr(),
c_content_bytes.as_ptr(),
);
if node_ptr.is_null() {
Err(())
} else {
Ok(self.register_node(node_ptr))
}
}
}
pub fn as_node(&self) -> Node {
self.register_node(self.doc_ptr() as xmlNodePtr)
}
pub fn dup(&self) -> Result<Self, ()> {
let doc_ptr = unsafe { xmlCopyDoc(self.doc_ptr(), 1) };
if doc_ptr.is_null() {
Err(())
} else {
let doc = _Document {
doc_ptr,
nodes: HashMap::new(),
};
Ok(Document(Rc::new(RefCell::new(doc))))
}
}
pub fn dup_from(&mut self, source: &Self) -> Result<(), ()> {
if !self.doc_ptr().is_null() {
return Err(());
}
let doc_ptr = unsafe { xmlCopyDoc(source.doc_ptr(), 1) };
if doc_ptr.is_null() {
return Err(());
}
self.0.borrow_mut().doc_ptr = doc_ptr;
Ok(())
}
}
mod c14n;