1pub mod async_md_api;
6pub mod async_trader_api;
7pub mod md_api;
8pub mod trader_api;
9
10pub use async_md_api::AsyncMdApi;
11pub use async_trader_api::AsyncTraderApi;
12pub use md_api::{MdApi, MdSpiHandler};
13pub use trader_api::{TraderApi, TraderSpiHandler};
14
15use crate::error::{CtpError, CtpResult};
16use std::ffi::CString;
17
18pub trait CtpApi {
20 fn get_version() -> CtpResult<String>;
22
23 fn init(&mut self) -> CtpResult<()>;
25
26 fn release(&mut self);
28
29 fn get_trading_day(&self) -> CtpResult<String>;
31
32 fn register_front(&mut self, front_address: &str) -> CtpResult<()>;
34
35 fn join(&self) -> CtpResult<i32>;
37}
38
39pub(crate) fn to_cstring(s: &str) -> CtpResult<CString> {
41 CString::new(s).map_err(|e| CtpError::InvalidParameterError(format!("字符串转换失败: {}", e)))
42}
43
44#[allow(dead_code)]
46pub(crate) fn check_null_ptr<T>(ptr: *const T, name: &str) -> CtpResult<()> {
47 if ptr.is_null() {
48 return Err(CtpError::FfiError(format!("{} 指针为空", name)));
49 }
50 Ok(())
51}
52
53pub(crate) fn safe_cstr_to_string(ptr: *const i8) -> CtpResult<String> {
55 if ptr.is_null() {
56 return Ok(String::new());
57 }
58
59 unsafe { crate::encoding::GbkConverter::cstring_to_utf8(ptr) }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_to_cstring() {
68 let result = to_cstring("test");
69 assert!(result.is_ok());
70
71 let result = to_cstring("测试");
72 assert!(result.is_ok());
73 }
74
75 #[test]
76 fn test_check_null_ptr() {
77 let ptr: *const i32 = std::ptr::null();
78 assert!(check_null_ptr(ptr, "test").is_err());
79
80 let value = 42i32;
81 let ptr = &value as *const i32;
82 assert!(check_null_ptr(ptr, "test").is_ok());
83 }
84}