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 OpenCC::from_config(config).map_or(OpenCCResult::CreationFailed, |instance| {
63 let handle = Box::new(OpenCCHandle {
64 instance,
65 is_destroyed: AtomicBool::new(false),
66 });
67 unsafe { *out_handle = Box::into_raw(handle) };
68 OpenCCResult::Success
69 })
70 }));
71 result.unwrap_or(OpenCCResult::InternalError)
72}
73
74#[unsafe(no_mangle)]
80pub unsafe extern "C" fn opencc_destroy(handle_ptr: *mut OpenCCHandle) {
81 if handle_ptr.is_null() {
82 return;
83 }
84
85 let result = catch_unwind(AssertUnwindSafe(|| {
86 let handle = unsafe { &*handle_ptr };
87
88 if handle
89 .is_destroyed
90 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
91 .is_ok()
92 {
93 unsafe { drop(Box::from_raw(handle_ptr)) };
94 }
95 }));
96
97 if result.is_err() {
98 eprintln!("opencc_destroy 内部发生 Panic!");
100 }
101}
102
103#[unsafe(no_mangle)]
121pub unsafe extern "C" fn opencc_convert(
122 handle_ptr: *const OpenCCHandle,
123 text: *const c_char,
124) -> *mut c_char {
125 let result = catch_unwind(AssertUnwindSafe(|| {
126 if handle_ptr.is_null() {
127 return std::ptr::null_mut();
128 }
129
130 let handle = unsafe { &*handle_ptr };
131
132 if handle.is_destroyed.load(Ordering::SeqCst) {
133 return std::ptr::null_mut();
134 }
135
136 if text.is_null() {
137 return std::ptr::null_mut();
138 }
139 let c_str = unsafe { CStr::from_ptr(text) };
140 let Ok(r_str) = c_str.to_str() else {
141 return std::ptr::null_mut();
142 };
143
144 let converted_string = handle.instance.convert(r_str);
145
146 CString::new(converted_string).map_or(std::ptr::null_mut(), CString::into_raw)
147 }));
148
149 result.unwrap_or_else(|_| {
150 eprintln!("opencc_convert 内部发生 Panic!");
152 std::ptr::null_mut()
153 })
154}
155
156#[unsafe(no_mangle)]
164pub unsafe extern "C" fn opencc_free_string(s_ptr: *mut c_char) {
165 if s_ptr.is_null() {
166 return;
167 }
168 let result = catch_unwind(AssertUnwindSafe(|| {
169 unsafe { drop(CString::from_raw(s_ptr)) };
170 }));
171
172 if result.is_err() {
173 eprintln!("opencc_free_string 内部发生 Panic!");
175 }
176}