1use std::{borrow::Cow, ptr};
2
3use encoding_rs::GB18030;
4
5pub fn check_make_dir(dir: &str) {
7 match std::fs::create_dir_all(dir) {
8 Ok(_) => (),
9 Err(e) => {
10 if e.kind() == std::io::ErrorKind::AlreadyExists {
11 } else {
12 panic!("create dir {} failed: {}", dir, e);
13 }
14 }
15 }
16}
17pub fn copy_str_to_i8_array_with_truncation<const N: usize>(
18 buffer: &mut [i8; N],
19 text: &str,
20) -> Result<(), &'static str> {
21 if N == 0 {
22 return Err("Buffer size is zero, cannot copy string");
23 }
24
25 let text_bytes = text.as_bytes();
27
28 let copy_len = std::cmp::min(text_bytes.len(), N - 1);
30
31 unsafe {
33 ptr::copy_nonoverlapping(
35 text_bytes.as_ptr() as *const i8, buffer.as_mut_ptr(), copy_len, );
39 }
40
41 buffer[copy_len] = 0;
43
44 Ok(())
45}
46
47pub fn copy_str_to_i8_array(dst: &mut [i8], src: &str) {
48 if dst.is_empty() {
49 return;
50 }
51 let bytes = src.as_bytes();
52 let len = usize::min(bytes.len(), dst.len() - 1);
53
54 unsafe {
55 ptr::copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr() as *mut u8, len);
56 }
57 dst[len] = 0;
58}
59
60#[macro_export]
61macro_rules! print_rsp_info {
62 ($p:expr) => {
63 if let Some(p) = $p {
64 println!(
65 "ErrorID[{}] Message[{}]",
66 p.ErrorID,
67 gb18030_cstr_i8_to_str(&p.ErrorMsg).unwrap().to_string()
68 );
69 }
70 };
71}
72
73pub fn gb18030_cstr_i8_to_str<'a>(
78 c_chars: &'a [std::os::raw::c_char],
79) -> Result<Cow<'a, str>, String> {
80 let len = c_chars
81 .iter()
82 .position(|&c| c == 0)
83 .unwrap_or(c_chars.len());
84 let bytes = &c_chars[..len];
85
86 let bytes = unsafe { &*(bytes as *const [std::os::raw::c_char] as *const [u8]) };
87
88 if bytes.is_ascii() {
89 return std::str::from_utf8(bytes)
90 .map(Cow::Borrowed)
91 .map_err(|e| format!("Invalid UTF-8: {}", e));
92 }
93
94 let (decoded, _, had_errors) = GB18030.decode(bytes);
96 if had_errors {
97 return Err("Failed to decode GB18030 string".to_string());
98 }
99 Ok(Cow::Owned(decoded.into_owned()))
100}
101
102pub trait AssignFromString {
103 fn assign_from_str(&mut self, s: &str);
106}
107
108impl<const N: usize> AssignFromString for [i8; N] {
109 fn assign_from_str(&mut self, s: &str) {
110 copy_str_to_i8_array(self, s);
111 }
112}
113
114impl<const N: usize> AssignFromString for &mut [i8; N] {
115 fn assign_from_str(&mut self, s: &str) {
116 copy_str_to_i8_array(*self, s);
117 }
118}
119
120pub trait SetString {
121 fn set_str(&mut self, s: &str);
122}
123
124impl<const N: usize> SetString for [i8; N] {
125 fn set_str(&mut self, s: &str) {
126 copy_str_to_i8_array(self, s);
127 }
128}
129
130impl<const N: usize> SetString for &mut [i8; N] {
131 fn set_str(&mut self, s: &str) {
132 copy_str_to_i8_array(*self, s);
133 }
134}
135
136pub trait WrapToString {
137 fn to_string(&self) -> String;
138 fn try_to_string(&self) -> Result<String, String>;
139}
140
141impl<const N: usize> WrapToString for [i8; N] {
142 fn to_string(&self) -> String {
143 let str_ = gb18030_cstr_i8_to_str(self);
144 str_.unwrap().to_string()
145 }
146
147 fn try_to_string(&self) -> Result<String, String> {
148 gb18030_cstr_i8_to_str(self).map(|cow| cow.to_string())
149 }
150}
151
152impl<const N: usize> WrapToString for &[i8; N] {
153 fn to_string(&self) -> String {
154 let str_ = gb18030_cstr_i8_to_str(*self);
155 str_.unwrap().to_string()
156 }
157
158 fn try_to_string(&self) -> Result<String, String> {
159 gb18030_cstr_i8_to_str(*self).map(|cow| cow.to_string())
160 }
161}
162
163pub trait DecodeString {
167 fn decode(&self) -> String;
169 fn try_decode(&self) -> Result<String, String>;
171}
172
173impl<const N: usize> DecodeString for [i8; N] {
174 fn decode(&self) -> String {
175 gb18030_cstr_i8_to_str(self).unwrap().into_owned()
176 }
177
178 fn try_decode(&self) -> Result<String, String> {
179 gb18030_cstr_i8_to_str(self).map(|cow| cow.into_owned())
180 }
181}
182
183impl<const N: usize> DecodeString for &[i8; N] {
184 fn decode(&self) -> String {
185 gb18030_cstr_i8_to_str(*self).unwrap().into_owned()
186 }
187
188 fn try_decode(&self) -> Result<String, String> {
189 gb18030_cstr_i8_to_str(*self).map(|cow| cow.into_owned())
190 }
191}
192
193#[derive(Debug, Clone)]
194pub struct WrapString(pub String); impl<const N: usize> From<[i8; N]> for WrapString {
197 fn from(value: [i8; N]) -> Self {
198 let str_ = gb18030_cstr_i8_to_str(&value).expect("failed to decode");
199 WrapString(str_.into())
200 }
201}
202
203impl<const N: usize> From<&[i8; N]> for WrapString {
204 fn from(value: &[i8; N]) -> Self {
205 let str_ = gb18030_cstr_i8_to_str(value).expect("failed to decode");
206 WrapString(str_.into())
207 }
208}
209
210pub trait WrapFrom<T> {
211 fn wrap_from(value: T) -> Self;
212}
213
214pub trait WrapInto<T>: Sized {
216 fn wrap_into(self) -> T;
217}
218
219impl<T, U> WrapInto<U> for T
221where
222 U: WrapFrom<T>,
223{
224 fn wrap_into(self) -> U {
225 U::wrap_from(self)
226 }
227}
228
229impl WrapFrom<&[i8]> for String {
230 fn wrap_from(bytes: &[i8]) -> Self {
231 let u8_bytes: Vec<u8> = bytes.iter().map(|&b| b as u8).collect();
232 String::from_utf8(u8_bytes).expect("Invalid UTF-8 sequence")
233 }
234}
235
236impl<const N: usize> WrapFrom<[i8; N]> for String {
237 fn wrap_from(value: [i8; N]) -> Self {
238 let str_ = gb18030_cstr_i8_to_str(&value).expect("failed to decode");
239 str_.into()
240 }
241}
242
243impl<const N: usize> WrapFrom<&[i8; N]> for String {
244 fn wrap_from(value: &[i8; N]) -> Self {
245 let str_ = gb18030_cstr_i8_to_str(value).expect("failed to decode");
246 str_.into()
247 }
248}
249
250#[derive(Debug, Clone, Copy)]
252pub enum DynLibKind {
253 MdApi,
255 TraderApi,
257}
258
259pub fn resolve_dynlib_path<P: AsRef<std::path::Path>>(
272 dir: P,
273 kind: DynLibKind,
274) -> std::path::PathBuf {
275 let dir = dir.as_ref();
276 let lib_name = match kind {
277 DynLibKind::MdApi => "thostmduserapi_se",
278 DynLibKind::TraderApi => "thosttraderapi_se",
279 };
280
281 #[cfg(target_os = "linux")]
282 {
283 dir.join(format!("{}.so", lib_name))
284 }
285
286 #[cfg(target_os = "windows")]
287 {
288 dir.join(format!("{}.dll", lib_name))
289 }
290
291 #[cfg(target_os = "macos")]
292 {
293 let framework_path = dir.join(format!(
295 "{}.framework/{}",
296 lib_name, lib_name
297 ));
298 if framework_path.exists() {
299 return framework_path;
300 }
301 dir.join(format!("{}.dylib", lib_name))
303 }
304}