#![allow(clippy::cast_possible_wrap)]
#![warn(missing_docs)]
mod types;
pub use types::{TX_NULL_NODE, TxDocument, TxError, TxNodeId, TxNodeType, TxPrinter};
use std::ffi::{CStr, CString, c_char, c_double, c_int};
use std::ptr;
unsafe fn cstr_to_str<'a>(s: *const c_char) -> Option<&'a str> {
if s.is_null() {
return None;
}
unsafe { CStr::from_ptr(s) }.to_str().ok()
}
fn cache_str(doc: &mut TxDocument, s: &str) -> *const c_char {
match CString::new(s) {
Ok(cs) => {
let ptr = cs.as_ptr();
doc.string_cache.push(cs);
ptr
}
Err(_) => ptr::null(),
}
}
macro_rules! ffi_catch {
($default:expr, $body:expr) => {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $body)) {
Ok(val) => val,
Err(_) => $default,
}
};
}
#[unsafe(no_mangle)]
pub extern "C" fn tx_document_new() -> *mut TxDocument {
ffi_catch!(ptr::null_mut(), {
Box::into_raw(Box::new(TxDocument::new()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_free(doc: *mut TxDocument) {
if doc.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
drop(unsafe { Box::from_raw(doc) });
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_clear(doc: *mut TxDocument) {
if doc.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
doc.doc.clear();
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_parse(doc: *mut TxDocument, xml: *const c_char) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
let Some(xml_str) = (unsafe { cstr_to_str(xml) }) else {
return TxError::TxErrorEmptyDocument;
};
doc.invalidate_caches();
TxError::from_result(&doc.doc.parse_str(xml_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_load_file(
doc: *mut TxDocument,
path: *const c_char,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
let Some(path_str) = (unsafe { cstr_to_str(path) }) else {
return TxError::TxErrorFileNotFound;
};
doc.invalidate_caches();
TxError::from_result(&doc.doc.load_file_mut(path_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_save_file(
doc: *const TxDocument,
path: *const c_char,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &*doc };
let Some(path_str) = (unsafe { cstr_to_str(path) }) else {
return TxError::TxErrorFileNotFound;
};
TxError::from_result(&doc.doc.save_file(path_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_to_string(doc: *mut TxDocument) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let s = doc.doc.to_string();
match CString::new(s) {
Ok(cs) => {
let ptr = cs.as_ptr();
doc.cached_to_string = Some(cs);
ptr
}
Err(_) => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_to_string_compact(doc: *mut TxDocument) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let s = doc.doc.to_string_compact();
match CString::new(s) {
Ok(cs) => {
let ptr = cs.as_ptr();
doc.cached_to_string_compact = Some(cs);
ptr
}
Err(_) => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_error(doc: *const TxDocument) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &*doc };
match doc.doc.error() {
Some(e) => TxError::from_xml_error(&e),
None => TxError::TxSuccess,
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_error_line(doc: *const TxDocument) -> c_int {
if doc.is_null() {
return 0;
}
ffi_catch!(0, {
let doc = unsafe { &*doc };
doc.doc.error_line().unwrap_or(0) as c_int
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_document_error_name(doc: *mut TxDocument) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
match doc.doc.error() {
Some(e) => {
let name = e.name();
match CString::new(name) {
Ok(cs) => {
let ptr = cs.as_ptr();
doc.cached_error_name = Some(cs);
ptr
}
Err(_) => ptr::null(),
}
}
None => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_new_element(doc: *mut TxDocument, name: *const c_char) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &mut *doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TX_NULL_NODE;
};
doc.invalidate_caches();
TxNodeId::from_node_id(doc.doc.new_element(name_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_new_text(doc: *mut TxDocument, text: *const c_char) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &mut *doc };
let Some(text_str) = (unsafe { cstr_to_str(text) }) else {
return TX_NULL_NODE;
};
doc.invalidate_caches();
TxNodeId::from_node_id(doc.doc.new_text(text_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_new_comment(doc: *mut TxDocument, text: *const c_char) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &mut *doc };
let Some(text_str) = (unsafe { cstr_to_str(text) }) else {
return TX_NULL_NODE;
};
doc.invalidate_caches();
TxNodeId::from_node_id(doc.doc.new_comment(text_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_new_declaration(doc: *mut TxDocument, text: *const c_char) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &mut *doc };
let Some(text_str) = (unsafe { cstr_to_str(text) }) else {
return TX_NULL_NODE;
};
doc.invalidate_caches();
TxNodeId::from_node_id(doc.doc.new_declaration(text_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_new_unknown(doc: *mut TxDocument, text: *const c_char) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &mut *doc };
let Some(text_str) = (unsafe { cstr_to_str(text) }) else {
return TX_NULL_NODE;
};
doc.invalidate_caches();
TxNodeId::from_node_id(doc.doc.new_unknown(text_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_insert_end_child(
doc: *mut TxDocument,
parent: TxNodeId,
child: TxNodeId,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
let result = doc
.doc
.insert_end_child(parent.to_node_id(), child.to_node_id());
TxError::from_result(&result)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_insert_first_child(
doc: *mut TxDocument,
parent: TxNodeId,
child: TxNodeId,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
let result = doc
.doc
.insert_first_child(parent.to_node_id(), child.to_node_id());
TxError::from_result(&result)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_insert_after_child(
doc: *mut TxDocument,
after: TxNodeId,
child: TxNodeId,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
let result = doc
.doc
.insert_after_child(after.to_node_id(), child.to_node_id());
TxError::from_result(&result)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_delete_child(
doc: *mut TxDocument,
parent: TxNodeId,
child: TxNodeId,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
let result = doc
.doc
.delete_child(parent.to_node_id(), child.to_node_id());
TxError::from_result(&result)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_delete_children(doc: *mut TxDocument, parent: TxNodeId) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
TxError::from_result(&doc.doc.delete_children(parent.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_delete_node(doc: *mut TxDocument, node: TxNodeId) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
doc.invalidate_caches();
TxError::from_result(&doc.doc.delete_node(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_parent(doc: *const TxDocument, node: TxNodeId) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.parent(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_first_child(doc: *const TxDocument, node: TxNodeId) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.first_child(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_last_child(doc: *const TxDocument, node: TxNodeId) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.last_child(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_prev_sibling(doc: *const TxDocument, node: TxNodeId) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.prev_sibling(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_next_sibling(doc: *const TxDocument, node: TxNodeId) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.next_sibling(node.to_node_id()))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_first_child_element(
doc: *const TxDocument,
node: TxNodeId,
name: *const c_char,
) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
let name_opt = unsafe { cstr_to_str(name) };
TxNodeId::from_option(doc.doc.first_child_element(node.to_node_id(), name_opt))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_next_sibling_element(
doc: *const TxDocument,
node: TxNodeId,
name: *const c_char,
) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
let name_opt = unsafe { cstr_to_str(name) };
TxNodeId::from_option(doc.doc.next_sibling_element(node.to_node_id(), name_opt))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_root_element(doc: *const TxDocument) -> TxNodeId {
if doc.is_null() {
return TX_NULL_NODE;
}
ffi_catch!(TX_NULL_NODE, {
let doc = unsafe { &*doc };
TxNodeId::from_option(doc.doc.root_element())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_name(doc: *mut TxDocument, element: TxNodeId) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let val = doc
.doc
.node_ref(element.to_node_id())
.map(|nr| nr.value().to_owned());
match val {
Some(s) => cache_str(doc, &s),
None => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_attribute(
doc: *mut TxDocument,
el: TxNodeId,
name: *const c_char,
) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return ptr::null();
};
let val = doc
.doc
.attribute(el.to_node_id(), name_str)
.map(str::to_owned);
match val {
Some(s) => cache_str(doc, &s),
None => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_set_attribute(
doc: *mut TxDocument,
el: TxNodeId,
name: *const c_char,
value: *const c_char,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TxError::TxErrorNoAttribute;
};
let Some(val_str) = (unsafe { cstr_to_str(value) }) else {
return TxError::TxErrorNoAttribute;
};
doc.invalidate_caches();
TxError::from_result(&doc.doc.set_attribute(el.to_node_id(), name_str, val_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_delete_attribute(
doc: *mut TxDocument,
el: TxNodeId,
name: *const c_char,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TxError::TxErrorNoAttribute;
};
doc.invalidate_caches();
TxError::from_result(&doc.doc.delete_attribute(el.to_node_id(), name_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_get_text(
doc: *mut TxDocument,
element: TxNodeId,
) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let val = doc.doc.get_text(element.to_node_id()).map(str::to_owned);
match val {
Some(s) => cache_str(doc, &s),
None => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_element_set_text(
doc: *mut TxDocument,
element: TxNodeId,
text: *const c_char,
) -> TxError {
if doc.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &mut *doc };
let Some(text_str) = (unsafe { cstr_to_str(text) }) else {
return TxError::TxErrorNoTextNode;
};
doc.invalidate_caches();
TxError::from_result(&doc.doc.set_text(element.to_node_id(), text_str))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_query_int_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
value: *mut c_int,
) -> TxError {
if doc.is_null() || value.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TxError::TxErrorNoAttribute;
};
match doc.doc.query_int_attribute(el.to_node_id(), name_str) {
Ok(v) => {
unsafe { *value = v as c_int };
TxError::TxSuccess
}
Err(e) => TxError::from_xml_error(&e),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_query_bool_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
value: *mut bool,
) -> TxError {
if doc.is_null() || value.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TxError::TxErrorNoAttribute;
};
match doc.doc.query_bool_attribute(el.to_node_id(), name_str) {
Ok(v) => {
unsafe { *value = v };
TxError::TxSuccess
}
Err(e) => TxError::from_xml_error(&e),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_query_double_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
value: *mut c_double,
) -> TxError {
if doc.is_null() || value.is_null() {
return TxError::TxErrorInvalidNodeId;
}
ffi_catch!(TxError::TxErrorInvalidNodeId, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return TxError::TxErrorNoAttribute;
};
match doc.doc.query_double_attribute(el.to_node_id(), name_str) {
Ok(v) => {
unsafe { *value = v };
TxError::TxSuccess
}
Err(e) => TxError::from_xml_error(&e),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_int_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
default_val: c_int,
) -> c_int {
if doc.is_null() {
return default_val;
}
ffi_catch!(default_val, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return default_val;
};
doc.doc
.int_attribute(el.to_node_id(), name_str, default_val)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_bool_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
default_val: bool,
) -> bool {
if doc.is_null() {
return default_val;
}
ffi_catch!(default_val, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return default_val;
};
doc.doc
.bool_attribute(el.to_node_id(), name_str, default_val)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_double_attribute(
doc: *const TxDocument,
el: TxNodeId,
name: *const c_char,
default_val: c_double,
) -> c_double {
if doc.is_null() {
return default_val;
}
ffi_catch!(default_val, {
let doc = unsafe { &*doc };
let Some(name_str) = (unsafe { cstr_to_str(name) }) else {
return default_val;
};
doc.doc
.double_attribute(el.to_node_id(), name_str, default_val)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn tx_printer_new() -> *mut TxPrinter {
ffi_catch!(ptr::null_mut(), {
Box::into_raw(Box::new(TxPrinter::new(false)))
})
}
#[unsafe(no_mangle)]
pub extern "C" fn tx_printer_new_compact() -> *mut TxPrinter {
ffi_catch!(ptr::null_mut(), {
Box::into_raw(Box::new(TxPrinter::new(true)))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_free(printer: *mut TxPrinter) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
drop(unsafe { Box::from_raw(printer) });
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_open_element(printer: *mut TxPrinter, name: *const c_char) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
if let Some(name_str) = unsafe { cstr_to_str(name) } {
p.cached_result = None;
p.printer.open_element(name_str);
}
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_push_attribute(
printer: *mut TxPrinter,
name: *const c_char,
value: *const c_char,
) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
if let (Some(n), Some(v)) = (unsafe { cstr_to_str(name) }, unsafe { cstr_to_str(value) }) {
p.cached_result = None;
p.printer.push_attribute(n, v);
}
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_close_element(printer: *mut TxPrinter) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
p.cached_result = None;
p.printer.close_element();
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_push_text(printer: *mut TxPrinter, text: *const c_char) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
if let Some(text_str) = unsafe { cstr_to_str(text) } {
p.cached_result = None;
p.printer.push_text(text_str);
}
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_push_comment(printer: *mut TxPrinter, text: *const c_char) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
if let Some(text_str) = unsafe { cstr_to_str(text) } {
p.cached_result = None;
p.printer.push_comment(text_str);
}
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_result(printer: *mut TxPrinter) -> *const c_char {
if printer.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let p = unsafe { &mut *printer };
let s = p.printer.result();
match CString::new(s) {
Ok(cs) => {
let ptr = cs.as_ptr();
p.cached_result = Some(cs);
ptr
}
Err(_) => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_printer_clear(printer: *mut TxPrinter) {
if printer.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let p = unsafe { &mut *printer };
p.cached_result = None;
p.printer.clear();
}));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_node_type(doc: *const TxDocument, node: TxNodeId) -> TxNodeType {
if doc.is_null() {
return TxNodeType::TxNodeUnknown;
}
ffi_catch!(TxNodeType::TxNodeUnknown, {
let doc = unsafe { &*doc };
match doc.doc.node_kind(node.to_node_id()) {
Some(kind) => TxNodeType::from_node_kind(kind),
None => TxNodeType::TxNodeUnknown,
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn tx_node_is_null(node: TxNodeId) -> bool {
node.is_null()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_node_value(doc: *mut TxDocument, node: TxNodeId) -> *const c_char {
if doc.is_null() {
return ptr::null();
}
ffi_catch!(ptr::null(), {
let doc = unsafe { &mut *doc };
let val = doc
.doc
.node_ref(node.to_node_id())
.map(|nr| nr.value().to_owned());
match val {
Some(s) => cache_str(doc, &s),
None => ptr::null(),
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tx_node_line(doc: *const TxDocument, node: TxNodeId) -> c_int {
if doc.is_null() {
return 0;
}
ffi_catch!(0, {
let doc = unsafe { &*doc };
doc.doc.line_num(node.to_node_id()).unwrap_or(0) as c_int
})
}