#![allow(unsafe_code, clippy::missing_safety_doc)]
use std::os::raw::c_char;
use crate::tree::{Document, NodeId, NodeKind};
use super::strings::to_c_string;
pub const XMLOXIDE_NODE_ELEMENT: i32 = 1;
pub const XMLOXIDE_NODE_TEXT: i32 = 3;
pub const XMLOXIDE_NODE_CDATA: i32 = 4;
pub const XMLOXIDE_NODE_ENTITY_REF: i32 = 5;
pub const XMLOXIDE_NODE_PI: i32 = 7;
pub const XMLOXIDE_NODE_COMMENT: i32 = 8;
pub const XMLOXIDE_NODE_DOCUMENT: i32 = 9;
pub const XMLOXIDE_NODE_DOCUMENT_TYPE: i32 = 10;
fn node_id_to_raw(id: Option<NodeId>) -> u32 {
id.map_or(0, NodeId::into_raw)
}
unsafe fn doc_and_node(doc: *const Document, raw_node: u32) -> Option<(&'static Document, NodeId)> {
if doc.is_null() {
return None;
}
let doc = unsafe { &*doc };
let node_id = NodeId::from_raw(raw_node)?;
Some((doc, node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_root(doc: *const Document) -> u32 {
if doc.is_null() {
return 0;
}
let doc = unsafe { &*doc };
doc.root().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_root_element(doc: *const Document) -> u32 {
if doc.is_null() {
return 0;
}
let doc = unsafe { &*doc };
node_id_to_raw(doc.root_element())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_parent(doc: *const Document, node: u32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
node_id_to_raw(doc.parent(node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_first_child(doc: *const Document, node: u32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
node_id_to_raw(doc.first_child(node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_last_child(doc: *const Document, node: u32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
node_id_to_raw(doc.last_child(node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_next_sibling(doc: *const Document, node: u32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
node_id_to_raw(doc.next_sibling(node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_prev_sibling(doc: *const Document, node: u32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
node_id_to_raw(doc.prev_sibling(node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_type(doc: *const Document, node: u32) -> i32 {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return -1;
};
match &doc.node(node_id).kind {
NodeKind::Document => XMLOXIDE_NODE_DOCUMENT,
NodeKind::Element { .. } => XMLOXIDE_NODE_ELEMENT,
NodeKind::Text { .. } => XMLOXIDE_NODE_TEXT,
NodeKind::CData { .. } => XMLOXIDE_NODE_CDATA,
NodeKind::Comment { .. } => XMLOXIDE_NODE_COMMENT,
NodeKind::ProcessingInstruction { .. } => XMLOXIDE_NODE_PI,
NodeKind::EntityRef { .. } => XMLOXIDE_NODE_ENTITY_REF,
NodeKind::DocumentType { .. } => XMLOXIDE_NODE_DOCUMENT_TYPE,
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_name(doc: *const Document, node: u32) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
match doc.node_name(node_id) {
Some(name) => to_c_string(name),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_text(doc: *const Document, node: u32) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
match doc.node_text(node_id) {
Some(text) => to_c_string(text),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_text_content(
doc: *const Document,
node: u32,
) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
let text = doc.text_content(node_id);
to_c_string(&text)
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_namespace(doc: *const Document, node: u32) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
match doc.node_namespace(node_id) {
Some(ns) => to_c_string(ns),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_attribute(
doc: *const Document,
node: u32,
name: *const c_char,
) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
if name.is_null() {
return std::ptr::null_mut();
}
let c_name = unsafe { std::ffi::CStr::from_ptr(name) };
let Ok(name_str) = c_name.to_str() else {
return std::ptr::null_mut();
};
match doc.attribute(node_id, name_str) {
Some(val) => to_c_string(val),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_attribute_count(doc: *const Document, node: u32) -> usize {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return 0;
};
doc.attributes(node_id).len()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_attribute_name_at(
doc: *const Document,
node: u32,
index: usize,
) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
let attrs = doc.attributes(node_id);
match attrs.get(index) {
Some(attr) => to_c_string(&attr.name),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_attribute_value_at(
doc: *const Document,
node: u32,
index: usize,
) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
let attrs = doc.attributes(node_id);
match attrs.get(index) {
Some(attr) => to_c_string(&attr.value),
None => std::ptr::null_mut(),
}
}
unsafe fn doc_and_node_mut(
doc: *mut Document,
raw_node: u32,
) -> Option<(&'static mut Document, NodeId)> {
if doc.is_null() {
return None;
}
let doc = unsafe { &mut *doc };
let node_id = NodeId::from_raw(raw_node)?;
Some((doc, node_id))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_create_element(doc: *mut Document, name: *const c_char) -> u32 {
if doc.is_null() || name.is_null() {
return 0;
}
let doc = unsafe { &mut *doc };
let c_name = unsafe { std::ffi::CStr::from_ptr(name) };
let Ok(name_str) = c_name.to_str() else {
return 0;
};
let node = doc.create_node(NodeKind::Element {
name: name_str.to_string(),
prefix: None,
namespace: None,
attributes: vec![],
});
node.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_create_text(doc: *mut Document, content: *const c_char) -> u32 {
if doc.is_null() || content.is_null() {
return 0;
}
let doc = unsafe { &mut *doc };
let c_content = unsafe { std::ffi::CStr::from_ptr(content) };
let Ok(text) = c_content.to_str() else {
return 0;
};
let node = doc.create_node(NodeKind::Text {
content: text.to_string(),
});
node.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_create_comment(
doc: *mut Document,
content: *const c_char,
) -> u32 {
if doc.is_null() || content.is_null() {
return 0;
}
let doc = unsafe { &mut *doc };
let c_content = unsafe { std::ffi::CStr::from_ptr(content) };
let Ok(text) = c_content.to_str() else {
return 0;
};
let node = doc.create_node(NodeKind::Comment {
content: text.to_string(),
});
node.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_append_child(doc: *mut Document, parent: u32, child: u32) -> i32 {
let Some((doc, parent_id)) = (unsafe { doc_and_node_mut(doc, parent) }) else {
return 0;
};
let Some(child_id) = NodeId::from_raw(child) else {
return 0;
};
doc.append_child(parent_id, child_id);
1
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_remove_node(doc: *mut Document, node: u32) -> i32 {
let Some((doc, node_id)) = (unsafe { doc_and_node_mut(doc, node) }) else {
return 0;
};
doc.remove_node(node_id);
1
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_clone_node(doc: *mut Document, node: u32, deep: i32) -> u32 {
let Some((doc, node_id)) = (unsafe { doc_and_node_mut(doc, node) }) else {
return 0;
};
let cloned = doc.clone_node(node_id, deep != 0);
cloned.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_set_text_content(
doc: *mut Document,
node: u32,
content: *const c_char,
) -> i32 {
if doc.is_null() || content.is_null() {
return 0;
}
let Some(node_id) = NodeId::from_raw(node) else {
return 0;
};
let doc = unsafe { &mut *doc };
let c_content = unsafe { std::ffi::CStr::from_ptr(content) };
let Ok(text) = c_content.to_str() else {
return 0;
};
i32::from(doc.set_text_content(node_id, text))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_set_attribute(
doc: *mut Document,
node: u32,
name: *const c_char,
value: *const c_char,
) -> i32 {
if doc.is_null() || name.is_null() || value.is_null() {
return 0;
}
let Some(node_id) = NodeId::from_raw(node) else {
return 0;
};
let doc = unsafe { &mut *doc };
let c_name = unsafe { std::ffi::CStr::from_ptr(name) };
let c_value = unsafe { std::ffi::CStr::from_ptr(value) };
let Ok(name_str) = c_name.to_str() else {
return 0;
};
let Ok(value_str) = c_value.to_str() else {
return 0;
};
i32::from(doc.set_attribute(node_id, name_str, value_str))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_remove_attribute(
doc: *mut Document,
node: u32,
name: *const c_char,
) -> i32 {
if doc.is_null() || name.is_null() {
return 0;
}
let Some(node_id) = NodeId::from_raw(node) else {
return 0;
};
let doc = unsafe { &mut *doc };
let c_name = unsafe { std::ffi::CStr::from_ptr(name) };
let Ok(name_str) = c_name.to_str() else {
return 0;
};
i32::from(doc.remove_attribute(node_id, name_str))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_insert_before(
doc: *mut Document,
reference: u32,
new_child: u32,
) -> i32 {
let Some((doc, ref_id)) = (unsafe { doc_and_node_mut(doc, reference) }) else {
return 0;
};
let Some(child_id) = NodeId::from_raw(new_child) else {
return 0;
};
doc.insert_before(ref_id, child_id);
1
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_element_by_id(doc: *const Document, id: *const c_char) -> u32 {
if doc.is_null() || id.is_null() {
return 0;
}
let doc = unsafe { &*doc };
let c_id = unsafe { std::ffi::CStr::from_ptr(id) };
let Ok(id_str) = c_id.to_str() else {
return 0;
};
node_id_to_raw(doc.element_by_id(id_str))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_insert_after(
doc: *mut Document,
reference: u32,
new_child: u32,
) -> i32 {
let Some((doc, ref_id)) = (unsafe { doc_and_node_mut(doc, reference) }) else {
return 0;
};
let Some(child_id) = NodeId::from_raw(new_child) else {
return 0;
};
doc.insert_after(ref_id, child_id);
1
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_replace_node(
doc: *mut Document,
old_node: u32,
new_node: u32,
) -> i32 {
let Some((doc, old_id)) = (unsafe { doc_and_node_mut(doc, old_node) }) else {
return 0;
};
let Some(new_id) = NodeId::from_raw(new_node) else {
return 0;
};
doc.replace_node(old_id, new_id);
1
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_create_pi(
doc: *mut Document,
target: *const c_char,
data: *const c_char,
) -> u32 {
if doc.is_null() || target.is_null() {
return 0;
}
let doc = unsafe { &mut *doc };
let c_target = unsafe { std::ffi::CStr::from_ptr(target) };
let Ok(target_str) = c_target.to_str() else {
return 0;
};
let data_str = if data.is_null() {
None
} else {
let c_data = unsafe { std::ffi::CStr::from_ptr(data) };
match c_data.to_str() {
Ok(s) => Some(s),
Err(_) => return 0,
}
};
let node = doc.create_processing_instruction(target_str, data_str);
node.into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_rename_element(
doc: *mut Document,
node: u32,
new_name: *const c_char,
) -> i32 {
if doc.is_null() || new_name.is_null() {
return 0;
}
let Some(node_id) = NodeId::from_raw(node) else {
return 0;
};
let doc = unsafe { &mut *doc };
let c_name = unsafe { std::ffi::CStr::from_ptr(new_name) };
let Ok(name_str) = c_name.to_str() else {
return 0;
};
i32::from(doc.rename_element(node_id, name_str))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_node_prefix(doc: *const Document, node: u32) -> *mut c_char {
let Some((doc, node_id)) = (unsafe { doc_and_node(doc, node) }) else {
return std::ptr::null_mut();
};
match doc.node_prefix(node_id) {
Some(prefix) => to_c_string(prefix),
None => std::ptr::null_mut(),
}
}