ctp-dyn 0.1.3

ctp rust binding with dynlib libloading support
Documentation
use std::ptr;

use encoding_rs::GB18030;
use std::borrow::Cow;

pub fn copy_str_to_i8_array(dst: &mut [i8], src: &str) {
    // 确保目标数组至少有一个元素用于放置 '\0'
    if dst.is_empty() {
        return;
    }

    // 获取源字符串的字节表示
    let bytes = src.as_bytes();

    // 计算要复制的长度,取源字符串长度和目标缓冲区长度减一的最小值
    let len = usize::min(bytes.len(), dst.len() - 1);

    unsafe {
        ptr::copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr() as *mut u8, len);
    }

    // 使用 '\0' 截断
    dst[len] = 0;

    // 如果目标缓冲区比源字符串长,则剩余部分保持未初始化或保持原值。
}

#[macro_export]
macro_rules! print_rsp_info {
    ($p:expr) => {
        if let Some(p) = $p {
            println!(
                "ErrorID[{}] Message[{}]",
                p.ErrorID,
                gb18030_cstr_i8_to_str(&p.ErrorMsg).unwrap().to_string()
            );
        }
    };
}

/// 将 `&[std::os::raw::c_char]` 转换为 `Cow<str>`
/// - 如果输入为 ASCII,返回 `Cow::Borrowed`。
/// - 如果输入为 GB18030,解码后返回 `Cow::Owned`。
/// - 如果解码失败,返回 `Err`。
pub fn gb18030_cstr_i8_to_str<'a>(
    c_chars: &'a [std::os::raw::c_char],
) -> Result<Cow<'a, str>, String> {
    // 找到 '\0' 的位置以截断
    let len = c_chars
        .iter()
        .position(|&c| c == 0)
        .unwrap_or(c_chars.len());
    let bytes = &c_chars[..len];

    // 转换为 &[u8]
    let bytes = unsafe { &*(bytes as *const [std::os::raw::c_char] as *const [u8]) };

    // 快速路径:检测是否是 ASCII
    if bytes.is_ascii() {
        return std::str::from_utf8(bytes)
            .map(Cow::Borrowed)
            .map_err(|e| format!("Invalid UTF-8: {}", e));
    }

    // 非 ASCII 的情况:使用 GB18030 解码
    let (decoded, _, had_errors) = GB18030.decode(bytes);
    if had_errors {
        return Err("Failed to decode GB18030 string".to_string());
    }

    Ok(Cow::Owned(decoded.into_owned()))
}

pub trait AssignFromString {
    /// 将 `&str` 的内容写入到 `[i8; N]` 数组中
    /// 超出数组大小的部分将被截断,未被覆盖的部分保留原值或清零。
    fn assign_from_str(&mut self, s: &str);
}

impl<const N: usize> AssignFromString for [i8; N] {
    fn assign_from_str(&mut self, s: &str) {
        copy_str_to_i8_array(self, s);
    }
}

impl<const N: usize> AssignFromString for &mut [i8; N] {
    fn assign_from_str(&mut self, s: &str) {
        copy_str_to_i8_array(*self, s);
    }
}

pub trait WrapToString {
    fn to_string(&self) -> String;
}

impl<const N: usize> WrapToString for [i8; N] {
    fn to_string(&self) -> String {
        let str_ = gb18030_cstr_i8_to_str(self);
        str_.unwrap().to_string()
    }
}

impl<const N: usize> WrapToString for &[i8; N] {
    fn to_string(&self) -> String {
        let str_ = gb18030_cstr_i8_to_str(*self);
        str_.unwrap().to_string()
    }
}

#[derive(Debug, Clone)]
pub struct WrapString(pub String); // 包装 String

impl<const N: usize> From<[i8; N]> for WrapString {
    fn from(value: [i8; N]) -> Self {
        let str_ = gb18030_cstr_i8_to_str(&value).expect("failed to decode");
        WrapString(str_.into())
    }
}

impl<const N: usize> From<&[i8; N]> for WrapString {
    fn from(value: &[i8; N]) -> Self {
        let str_ = gb18030_cstr_i8_to_str(value).expect("failed to decode");
        WrapString(str_.into())
    }
}

pub trait WrapFrom<T> {
    fn wrap_from(value: T) -> Self;
}

// 自定义 MyInto trait
pub trait WrapInto<T>: Sized {
    fn wrap_into(self) -> T;
}

// 为 MyFrom 创建对应的 MyInto 实现
impl<T, U> WrapInto<U> for T
where
    U: WrapFrom<T>,
{
    fn wrap_into(self) -> U {
        U::wrap_from(self)
    }
}

impl WrapFrom<&[i8]> for String {
    fn wrap_from(bytes: &[i8]) -> Self {
        let u8_bytes: Vec<u8> = bytes.iter().map(|&b| b as u8).collect();
        String::from_utf8(u8_bytes).expect("Invalid UTF-8 sequence")
    }
}

impl<const N: usize> WrapFrom<[i8; N]> for String {
    fn wrap_from(value: [i8; N]) -> Self {
        let str_ = gb18030_cstr_i8_to_str(&value).expect("failed to decode");
        str_.into()
    }
}

impl<const N: usize> WrapFrom<&[i8; N]> for String {
    fn wrap_from(value: &[i8; N]) -> Self {
        let str_ = gb18030_cstr_i8_to_str(value).expect("failed to decode");
        str_.into()
    }
}