#![allow(unsafe_code, clippy::missing_safety_doc)]
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use crate::ffi::{clear_last_error, set_last_error};
use crate::reader::{XmlNodeType, XmlReader};
pub struct FfiReader {
_input: Box<str>,
reader: XmlReader<'static>,
}
impl FfiReader {
fn new(input: String) -> Self {
let boxed: Box<str> = input.into_boxed_str();
let reader = unsafe {
let static_ref: &'static str = &*(std::ptr::from_ref::<str>(&boxed));
XmlReader::new(static_ref)
};
Self {
_input: boxed,
reader,
}
}
}
pub const XMLOXIDE_READER_NONE: i32 = 0;
pub const XMLOXIDE_READER_ELEMENT: i32 = 1;
pub const XMLOXIDE_READER_ATTRIBUTE: i32 = 2;
pub const XMLOXIDE_READER_TEXT: i32 = 3;
pub const XMLOXIDE_READER_CDATA: i32 = 4;
pub const XMLOXIDE_READER_PI: i32 = 7;
pub const XMLOXIDE_READER_COMMENT: i32 = 8;
pub const XMLOXIDE_READER_DOCUMENT_TYPE: i32 = 10;
pub const XMLOXIDE_READER_WHITESPACE: i32 = 13;
pub const XMLOXIDE_READER_END_ELEMENT: i32 = 15;
pub const XMLOXIDE_READER_XML_DECLARATION: i32 = 17;
pub const XMLOXIDE_READER_END_DOCUMENT: i32 = -1;
fn node_type_to_int(nt: XmlNodeType) -> i32 {
match nt {
XmlNodeType::None => XMLOXIDE_READER_NONE,
XmlNodeType::Element => XMLOXIDE_READER_ELEMENT,
XmlNodeType::EndElement => XMLOXIDE_READER_END_ELEMENT,
XmlNodeType::Text => XMLOXIDE_READER_TEXT,
XmlNodeType::CData => XMLOXIDE_READER_CDATA,
XmlNodeType::Comment => XMLOXIDE_READER_COMMENT,
XmlNodeType::ProcessingInstruction => XMLOXIDE_READER_PI,
XmlNodeType::XmlDeclaration => XMLOXIDE_READER_XML_DECLARATION,
XmlNodeType::DocumentType => XMLOXIDE_READER_DOCUMENT_TYPE,
XmlNodeType::Whitespace => XMLOXIDE_READER_WHITESPACE,
XmlNodeType::Attribute => XMLOXIDE_READER_ATTRIBUTE,
XmlNodeType::EndDocument => XMLOXIDE_READER_END_DOCUMENT,
}
}
fn to_c_string(s: &str) -> *mut c_char {
match CString::new(s) {
Ok(cs) => cs.into_raw(),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_new(input: *const c_char) -> *mut FfiReader {
if input.is_null() {
set_last_error("null input pointer");
return std::ptr::null_mut();
}
clear_last_error();
let c_str = CStr::from_ptr(input);
let Ok(s) = c_str.to_str() else {
set_last_error("input is not valid UTF-8");
return std::ptr::null_mut();
};
Box::into_raw(Box::new(FfiReader::new(s.to_string())))
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_read(reader: *mut FfiReader) -> i32 {
if reader.is_null() {
return -1;
}
match (*reader).reader.read() {
Ok(true) => 1,
Ok(false) => 0,
Err(e) => {
set_last_error(&e.to_string());
-1
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_node_type(reader: *const FfiReader) -> i32 {
if reader.is_null() {
return XMLOXIDE_READER_NONE;
}
node_type_to_int((*reader).reader.node_type())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_name(reader: *const FfiReader) -> *mut c_char {
if reader.is_null() {
return std::ptr::null_mut();
}
match (*reader).reader.name() {
Some(name) => to_c_string(name),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_local_name(reader: *const FfiReader) -> *mut c_char {
if reader.is_null() {
return std::ptr::null_mut();
}
match (*reader).reader.local_name() {
Some(name) => to_c_string(name),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_prefix(reader: *const FfiReader) -> *mut c_char {
if reader.is_null() {
return std::ptr::null_mut();
}
match (*reader).reader.prefix() {
Some(p) => to_c_string(p),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_namespace_uri(reader: *const FfiReader) -> *mut c_char {
if reader.is_null() {
return std::ptr::null_mut();
}
match (*reader).reader.namespace_uri() {
Some(ns) => to_c_string(ns),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_value(reader: *const FfiReader) -> *mut c_char {
if reader.is_null() {
return std::ptr::null_mut();
}
match (*reader).reader.value() {
Some(v) => to_c_string(v),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_depth(reader: *const FfiReader) -> u32 {
if reader.is_null() {
return 0;
}
(*reader).reader.depth()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_is_empty_element(reader: *const FfiReader) -> i32 {
if reader.is_null() {
return 0;
}
i32::from((*reader).reader.is_empty_element())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_has_value(reader: *const FfiReader) -> i32 {
if reader.is_null() {
return 0;
}
i32::from((*reader).reader.has_value())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_attribute_count(reader: *const FfiReader) -> usize {
if reader.is_null() {
return 0;
}
(*reader).reader.attribute_count()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_get_attribute(
reader: *const FfiReader,
name: *const c_char,
) -> *mut c_char {
if reader.is_null() || name.is_null() {
return std::ptr::null_mut();
}
let Ok(name) = CStr::from_ptr(name).to_str() else {
return std::ptr::null_mut();
};
match (*reader).reader.get_attribute(name) {
Some(v) => to_c_string(v),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_move_to_first_attribute(reader: *mut FfiReader) -> i32 {
if reader.is_null() {
return 0;
}
i32::from((*reader).reader.move_to_first_attribute())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_move_to_next_attribute(reader: *mut FfiReader) -> i32 {
if reader.is_null() {
return 0;
}
i32::from((*reader).reader.move_to_next_attribute())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_move_to_element(reader: *mut FfiReader) -> i32 {
if reader.is_null() {
return 0;
}
i32::from((*reader).reader.move_to_element())
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_reader_free(reader: *mut FfiReader) {
if reader.is_null() {
return;
}
drop(Box::from_raw(reader));
}