#![allow(unsafe_code, clippy::missing_safety_doc)]
use std::ffi::CStr;
use std::os::raw::c_char;
use crate::tree::Document;
use crate::error::ErrorSeverity;
use super::strings::to_c_string;
use super::{
clear_last_error, set_last_error, set_last_error_structured, XMLOXIDE_ERR_ERROR,
XMLOXIDE_ERR_FATAL, XMLOXIDE_ERR_WARNING,
};
#[no_mangle]
pub unsafe extern "C" fn xmloxide_parse_str(input: *const c_char) -> *mut Document {
clear_last_error();
if input.is_null() {
set_last_error("null input pointer");
return std::ptr::null_mut();
}
let c_str = unsafe { CStr::from_ptr(input) };
let s = match c_str.to_str() {
Ok(s) => s,
Err(e) => {
set_last_error(&format!("invalid UTF-8: {e}"));
return std::ptr::null_mut();
}
};
match Document::parse_str(s) {
Ok(doc) => Box::into_raw(Box::new(doc)),
Err(e) => {
set_last_error_structured(
&e.message,
e.location.line,
e.location.column,
XMLOXIDE_ERR_FATAL,
);
std::ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_parse_bytes(data: *const u8, len: usize) -> *mut Document {
clear_last_error();
if data.is_null() {
set_last_error("null data pointer");
return std::ptr::null_mut();
}
let bytes = unsafe { std::slice::from_raw_parts(data, len) };
match Document::parse_bytes(bytes) {
Ok(doc) => Box::into_raw(Box::new(doc)),
Err(e) => {
set_last_error_structured(
&e.message,
e.location.line,
e.location.column,
XMLOXIDE_ERR_FATAL,
);
std::ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_free_doc(doc: *mut Document) {
if !doc.is_null() {
unsafe {
drop(Box::from_raw(doc));
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_version(doc: *const Document) -> *mut c_char {
if doc.is_null() {
return std::ptr::null_mut();
}
let doc = unsafe { &*doc };
match &doc.version {
Some(v) => to_c_string(v),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_encoding(doc: *const Document) -> *mut c_char {
if doc.is_null() {
return std::ptr::null_mut();
}
let doc = unsafe { &*doc };
match &doc.encoding {
Some(e) => to_c_string(e),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_parse_html(input: *const c_char) -> *mut Document {
clear_last_error();
if input.is_null() {
set_last_error("null input pointer");
return std::ptr::null_mut();
}
let c_str = unsafe { CStr::from_ptr(input) };
let s = match c_str.to_str() {
Ok(s) => s,
Err(e) => {
set_last_error(&format!("invalid UTF-8: {e}"));
return std::ptr::null_mut();
}
};
match crate::html::parse_html(s) {
Ok(doc) => Box::into_raw(Box::new(doc)),
Err(e) => {
set_last_error_structured(
&e.message,
e.location.line,
e.location.column,
XMLOXIDE_ERR_FATAL,
);
std::ptr::null_mut()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_parse_file(path: *const c_char) -> *mut Document {
clear_last_error();
if path.is_null() {
set_last_error("null path pointer");
return std::ptr::null_mut();
}
let c_str = unsafe { CStr::from_ptr(path) };
let s = match c_str.to_str() {
Ok(s) => s,
Err(e) => {
set_last_error(&format!("invalid UTF-8 in path: {e}"));
return std::ptr::null_mut();
}
};
match Document::parse_file(s) {
Ok(doc) => Box::into_raw(Box::new(doc)),
Err(e) => {
set_last_error_structured(
&e.message,
e.location.line,
e.location.column,
XMLOXIDE_ERR_FATAL,
);
std::ptr::null_mut()
}
}
}
fn severity_to_ffi(s: ErrorSeverity) -> i32 {
match s {
ErrorSeverity::Warning => XMLOXIDE_ERR_WARNING,
ErrorSeverity::Error => XMLOXIDE_ERR_ERROR,
ErrorSeverity::Fatal => XMLOXIDE_ERR_FATAL,
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_diagnostic_count(doc: *const Document) -> usize {
if doc.is_null() {
return 0;
}
let doc = unsafe { &*doc };
doc.diagnostics.len()
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_diagnostic_message(
doc: *const Document,
index: usize,
) -> *mut c_char {
if doc.is_null() {
return std::ptr::null_mut();
}
let doc = unsafe { &*doc };
match doc.diagnostics.get(index) {
Some(d) => to_c_string(&d.message),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_diagnostic_line(doc: *const Document, index: usize) -> u32 {
if doc.is_null() {
return 0;
}
let doc = unsafe { &*doc };
doc.diagnostics.get(index).map_or(0, |d| d.location.line)
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_diagnostic_column(doc: *const Document, index: usize) -> u32 {
if doc.is_null() {
return 0;
}
let doc = unsafe { &*doc };
doc.diagnostics.get(index).map_or(0, |d| d.location.column)
}
#[no_mangle]
pub unsafe extern "C" fn xmloxide_doc_diagnostic_severity(
doc: *const Document,
index: usize,
) -> i32 {
if doc.is_null() {
return -1;
}
let doc = unsafe { &*doc };
doc.diagnostics
.get(index)
.map_or(-1, |d| severity_to_ffi(d.severity))
}