use std::{
ffi::{
CStr,
CString,
c_char,
},
panic::{
AssertUnwindSafe,
catch_unwind,
},
sync::atomic::{
AtomicBool,
Ordering,
},
};
use crate::{
OpenCC,
config::BuiltinConfig,
};
#[repr(i32)]
#[derive(Debug, PartialEq, Eq)]
pub enum OpenCCResult {
Success = 0,
InvalidHandle = 1,
InvalidArgument = 2,
CreationFailed = 3,
InternalError = 4,
}
pub struct OpenCCHandle {
instance: OpenCC,
is_destroyed: AtomicBool,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opencc_create(
config: BuiltinConfig,
out_handle: *mut *mut OpenCCHandle,
) -> OpenCCResult {
let result = catch_unwind(AssertUnwindSafe(|| {
if out_handle.is_null() {
return OpenCCResult::InvalidArgument;
}
unsafe { *out_handle = std::ptr::null_mut() };
OpenCC::from_config(config).map_or(OpenCCResult::CreationFailed, |instance| {
let handle = Box::new(OpenCCHandle {
instance,
is_destroyed: AtomicBool::new(false),
});
unsafe { *out_handle = Box::into_raw(handle) };
OpenCCResult::Success
})
}));
result.unwrap_or(OpenCCResult::InternalError)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opencc_destroy(handle_ptr: *mut OpenCCHandle) {
if handle_ptr.is_null() {
return;
}
let result = catch_unwind(AssertUnwindSafe(|| {
let handle = unsafe { &*handle_ptr };
if handle
.is_destroyed
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
unsafe { drop(Box::from_raw(handle_ptr)) };
}
}));
if result.is_err() {
eprintln!("Panic occurred inside opencc_destroy!");
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opencc_convert(
handle_ptr: *const OpenCCHandle,
text: *const c_char,
) -> *mut c_char {
let result = catch_unwind(AssertUnwindSafe(|| {
if handle_ptr.is_null() {
return std::ptr::null_mut();
}
let handle = unsafe { &*handle_ptr };
if handle.is_destroyed.load(Ordering::SeqCst) {
return std::ptr::null_mut();
}
if text.is_null() {
return std::ptr::null_mut();
}
let c_str = unsafe { CStr::from_ptr(text) };
let Ok(r_str) = c_str.to_str() else {
return std::ptr::null_mut();
};
let converted_string = handle.instance.convert(r_str);
CString::new(converted_string).map_or(std::ptr::null_mut(), CString::into_raw)
}));
result.unwrap_or_else(|_| {
eprintln!("Panic occurred inside opencc_convert!");
std::ptr::null_mut()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opencc_free_string(s_ptr: *mut c_char) {
if s_ptr.is_null() {
return;
}
let result = catch_unwind(AssertUnwindSafe(|| {
unsafe { drop(CString::from_raw(s_ptr)) };
}));
if result.is_err() {
eprintln!("Panic occurred inside opencc_free_string!");
}
}