use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::{Document, DocumentRef, DocumentWeak, Node};
use libc;
use libc::{c_char, c_void, size_t};
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::rc::Rc;
use std::str;
pub(crate) type ContextRef = Rc<RefCell<_Context>>;
#[derive(Debug)]
pub(crate) struct _Context(pub(crate) xmlXPathContextPtr);
impl Drop for _Context {
fn drop(&mut self) {
unsafe {
xmlXPathFreeContext(self.0);
}
}
}
#[derive(Clone)]
pub struct Context {
pub(crate) context_ptr: ContextRef,
pub(crate) document: DocumentWeak,
}
pub struct Object {
pub ptr: xmlXPathObjectPtr,
document: DocumentWeak,
}
impl Context {
pub fn new(doc: &Document) -> Result<Context, ()> {
let ctxtptr = unsafe { xmlXPathNewContext(doc.doc_ptr()) };
if ctxtptr.is_null() {
Err(())
} else {
Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(&doc.0),
})
}
}
pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
if ctxtptr.is_null() {
Err(())
} else {
Ok(Context {
context_ptr: Rc::new(RefCell::new(_Context(ctxtptr))),
document: Rc::downgrade(&docref),
})
}
}
pub fn as_ptr(&self) -> xmlXPathContextPtr {
self.context_ptr.borrow().0
}
pub fn from_node(node: &Node) -> Result<Context, ()> {
let docref = node.get_docref().upgrade().unwrap();
Context::new_ptr(&docref)
}
pub fn register_namespace(&self, prefix: &str, href: &str) -> Result<(), ()> {
let c_prefix = CString::new(prefix).unwrap();
let c_href = CString::new(href).unwrap();
unsafe {
let result = xmlXPathRegisterNs(
self.as_ptr(),
c_prefix.as_bytes().as_ptr(),
c_href.as_bytes().as_ptr(),
);
if result != 0 {
Err(())
} else {
Ok(())
}
}
}
pub fn evaluate(&self, xpath: &str) -> Result<Object, ()> {
let c_xpath = CString::new(xpath).unwrap();
let ptr = unsafe { xmlXPathEvalExpression(c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
if ptr.is_null() {
Err(())
} else {
Ok(Object {
ptr,
document: self.document.clone(),
})
}
}
pub fn node_evaluate(&self, xpath: &str, node: &Node) -> Result<Object, ()> {
let c_xpath = CString::new(xpath).unwrap();
let ptr =
unsafe { xmlXPathNodeEval(node.node_ptr(), c_xpath.as_bytes().as_ptr(), self.as_ptr()) };
if ptr.is_null() {
Err(())
} else {
Ok(Object {
ptr,
document: self.document.clone(),
})
}
}
pub fn set_context_node(&mut self, node: &Node) -> Result<(), ()> {
unsafe {
let result = xmlXPathSetContextNode(node.node_ptr(), self.as_ptr());
if result != 0 {
return Err(());
}
}
Ok(())
}
pub fn findnodes(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<Vec<Node>, ()> {
let evaluated;
if let Some(node) = node_opt {
evaluated = self.node_evaluate(xpath, node)?;
} else {
evaluated = self.evaluate(xpath)?;
}
Ok(evaluated.get_nodes_as_vec())
}
pub fn findvalue(&mut self, xpath: &str, node_opt: Option<&Node>) -> Result<String, ()> {
let evaluated;
if let Some(node) = node_opt {
evaluated = self.node_evaluate(xpath, node)?;
} else {
evaluated = self.evaluate(xpath)?;
}
Ok(evaluated.to_string())
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe {
xmlXPathFreeObject(self.ptr);
}
}
}
impl Object {
pub fn get_number_of_nodes(&self) -> usize {
let v = xmlXPathObjectNumberOfNodes(self.ptr);
if v == -1 {
panic!("rust-libxml: xpath: Passed in null pointer!");
}
if v == -2 {
return 0;
}
if v < -2 {
panic!("rust-libxml: xpath: expected non-negative number of result nodes");
}
v as usize
}
pub fn get_nodes_as_vec(&self) -> Vec<Node> {
let n = self.get_number_of_nodes();
let mut vec: Vec<Node> = Vec::with_capacity(n);
let slice = if n > 0 {
xmlXPathObjectGetNodes(self.ptr, n as size_t)
} else {
Vec::new()
};
for ptr in slice {
if ptr.is_null() {
panic!("rust-libxml: xpath: found null pointer result set");
}
let node = Node::wrap(ptr, &self.document.upgrade().unwrap());
vec.push(node);
}
vec
}
pub fn to_string(&self) -> String {
unsafe {
let receiver = xmlXPathCastToString(self.ptr);
let c_string = CStr::from_ptr(receiver as *const c_char);
let rust_string = str::from_utf8(c_string.to_bytes()).unwrap().to_owned();
libc::free(receiver as *mut c_void);
rust_string
}
}
}