#![allow(unsafe_code, clippy::missing_safety_doc)]
use std::ffi::CStr;
use std::os::raw::c_char;
use crate::tree::{Document, NodeId};
use crate::xpath::XPathValue;
use super::strings::to_c_string;
use super::{clear_last_error, set_last_error};
pub const XMLOXIDE_XPATH_NODESET: i32 = 1;
pub const XMLOXIDE_XPATH_BOOLEAN: i32 = 2;
pub const XMLOXIDE_XPATH_NUMBER: i32 = 3;
pub const XMLOXIDE_XPATH_STRING: i32 = 4;
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_eval(
doc: *const Document,
context_node: u32,
expr: *const c_char,
) -> *mut XPathValue {
clear_last_error();
if doc.is_null() || expr.is_null() {
set_last_error("null pointer argument");
return std::ptr::null_mut();
}
let doc = unsafe { &*doc };
let c_expr = unsafe { CStr::from_ptr(expr) };
let Ok(expr_str) = c_expr.to_str() else {
set_last_error("invalid UTF-8 in XPath expression");
return std::ptr::null_mut();
};
let ctx_node = if context_node == 0 {
doc.root()
} else if let Some(id) = NodeId::from_raw(context_node) {
id
} else {
set_last_error("invalid node id");
return std::ptr::null_mut();
};
match crate::xpath::evaluate(doc, ctx_node, expr_str) {
Ok(val) => Box::into_raw(Box::new(val)),
Err(e) => {
set_last_error(&format!("{e}"));
std::ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_result_type(result: *const XPathValue) -> i32 {
if result.is_null() {
return -1;
}
let val = unsafe { &*result };
match val {
XPathValue::NodeSet(_) => XMLOXIDE_XPATH_NODESET,
XPathValue::Boolean(_) => XMLOXIDE_XPATH_BOOLEAN,
XPathValue::Number(_) => XMLOXIDE_XPATH_NUMBER,
XPathValue::String(_) => XMLOXIDE_XPATH_STRING,
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_result_boolean(result: *const XPathValue) -> i32 {
if result.is_null() {
return 0;
}
let val = unsafe { &*result };
i32::from(val.to_boolean())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_result_number(result: *const XPathValue) -> f64 {
if result.is_null() {
return f64::NAN;
}
let val = unsafe { &*result };
val.to_number()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_result_string(result: *const XPathValue) -> *mut c_char {
if result.is_null() {
return std::ptr::null_mut();
}
let val = unsafe { &*result };
to_c_string(&val.to_xpath_string())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_nodeset_count(result: *const XPathValue) -> usize {
if result.is_null() {
return 0;
}
let val = unsafe { &*result };
match val {
XPathValue::NodeSet(nodes) => nodes.len(),
_ => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_nodeset_item(
result: *const XPathValue,
index: usize,
) -> u32 {
if result.is_null() {
return 0;
}
let val = unsafe { &*result };
match val {
XPathValue::NodeSet(nodes) => nodes.get(index).map_or(0, |id| id.into_raw()),
_ => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_xpath_free_result(result: *mut XPathValue) {
if !result.is_null() {
unsafe {
drop(Box::from_raw(result));
}
}
}