use libc;
use libc::{c_char, c_int, c_void};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::ptr;
use std::rc::{Rc, Weak};
use std::str;
use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::node::Node;
pub(crate) type DocumentRef = Rc<RefCell<_Document>>;
pub(crate) type DocumentWeak = Weak<RefCell<_Document>>;
#[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 Document {
pub fn new() -> Result<Self, ()> {
unsafe {
let c_version = CString::new("1.0").unwrap();
let doc_ptr = xmlNewDoc(c_version.as_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 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(&self, format: bool) -> String {
unsafe {
let mut receiver = ptr::null_mut();
let mut size: c_int = 0;
let c_utf8 = CString::new("UTF-8").unwrap();
let c_format = if format { 1 } else { 0 };
setIndentTreeOutput(c_format);
xmlDocDumpFormatMemoryEnc(
self.doc_ptr(),
&mut receiver,
&mut size,
c_utf8.as_ptr(),
c_format,
);
let c_string = CStr::from_ptr(receiver as *const c_char);
let node_string = c_string.to_string_lossy().into_owned();
libc::free(receiver as *mut c_void);
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 create_processing_instruction(&mut self, name: &str, content: &str) -> Result<Node, ()> {
unsafe {
let c_name = CString::new(name).unwrap();
let c_content = CString::new(content).unwrap();
let node_ptr: xmlNodePtr = xmlNewDocPI(
self.doc_ptr(),
c_name.as_bytes().as_ptr(),
c_content.as_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(())
}
}