1use crate::OpenCC;
4use crate::config::BuiltinConfig;
5use std::ffi::{CStr, CString, c_char};
6use std::panic::{AssertUnwindSafe, catch_unwind};
7use std::sync::atomic::{AtomicBool, Ordering};
8
9#[repr(i32)]
11#[derive(Debug, PartialEq, Eq)]
12pub enum OpenCCResult {
13 Success = 0,
15
16 InvalidHandle = 1,
18
19 InvalidArgument = 2,
21
22 CreationFailed = 3,
24
25 InternalError = 4,
27}
28
29pub struct OpenCCHandle {
31 instance: OpenCC,
33
34 is_destroyed: AtomicBool,
36}
37
38#[unsafe(no_mangle)]
52pub unsafe extern "C" fn opencc_create(
53 config: BuiltinConfig,
54 out_handle: *mut *mut OpenCCHandle,
55) -> OpenCCResult {
56 let result = catch_unwind(AssertUnwindSafe(|| {
57 if out_handle.is_null() {
58 return OpenCCResult::InvalidArgument;
59 }
60 unsafe { *out_handle = std::ptr::null_mut() };
61
62 match OpenCC::from_config(config) {
63 Ok(instance) => {
64 let handle = Box::new(OpenCCHandle {
65 instance,
66 is_destroyed: AtomicBool::new(false),
67 });
68 unsafe { *out_handle = Box::into_raw(handle) };
69 OpenCCResult::Success
70 }
71 Err(_) => OpenCCResult::CreationFailed,
72 }
73 }));
74 result.unwrap_or(OpenCCResult::InternalError)
75}
76
77#[unsafe(no_mangle)]
83pub unsafe extern "C" fn opencc_destroy(handle_ptr: *mut OpenCCHandle) {
84 if handle_ptr.is_null() {
85 return;
86 }
87
88 let result = catch_unwind(AssertUnwindSafe(|| {
89 let handle = unsafe { &*handle_ptr };
90
91 if handle
92 .is_destroyed
93 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
94 .is_ok()
95 {
96 unsafe { drop(Box::from_raw(handle_ptr)) };
97 }
98 }));
99
100 if result.is_err() {
101 eprintln!("opencc_destroy 内部发生 Panic!");
103 }
104}
105
106#[unsafe(no_mangle)]
124pub unsafe extern "C" fn opencc_convert(
125 handle_ptr: *const OpenCCHandle,
126 text: *const c_char,
127) -> *mut c_char {
128 let result = catch_unwind(AssertUnwindSafe(|| {
129 if handle_ptr.is_null() {
130 return std::ptr::null_mut();
131 }
132
133 let handle = unsafe { &*handle_ptr };
134
135 if handle.is_destroyed.load(Ordering::SeqCst) {
136 return std::ptr::null_mut();
137 }
138
139 if text.is_null() {
140 return std::ptr::null_mut();
141 }
142 let c_str = unsafe { CStr::from_ptr(text) };
143 let r_str = match c_str.to_str() {
144 Ok(s) => s,
145 Err(_) => return std::ptr::null_mut(),
146 };
147
148 let converted_string = handle.instance.convert(r_str);
149
150 match CString::new(converted_string) {
151 Ok(c_string) => c_string.into_raw(),
152 Err(_) => std::ptr::null_mut(),
153 }
154 }));
155
156 result.unwrap_or_else(|_| {
157 eprintln!("opencc_convert 内部发生 Panic!");
159 std::ptr::null_mut()
160 })
161}
162
163#[unsafe(no_mangle)]
171pub unsafe extern "C" fn opencc_free_string(s_ptr: *mut c_char) {
172 if s_ptr.is_null() {
173 return;
174 }
175 let result = catch_unwind(AssertUnwindSafe(|| {
176 unsafe { drop(CString::from_raw(s_ptr)) };
177 }));
178
179 if result.is_err() {
180 eprintln!("opencc_free_string 内部发生 Panic!");
182 }
183}