//! BMap wrapper used marshaler for string and string array. use crate::bmap::{CKSTRING, PCKSTRING}; use std::ffi::{CStr, CString}; use thiserror::Error as TeError; // region: Error and Result Types /// Any possible error occurs in this module. #[derive(Debug, TeError, Clone)] pub enum Error { #[error("can not parse from native string")] FromNative(#[from] std::str::Utf8Error), #[error("can not format into native string")] ToNative(#[from] std::ffi::NulError), } /// The result type used in this module. pub type Result = std::result::Result; // endregion pub unsafe fn from_native_string(ptr: CKSTRING) -> Result { let s = unsafe { CStr::from_ptr(ptr) }; Ok(String::from(s.to_str()?)) } pub unsafe fn to_native_string(words: T) -> Result where T: Into>, { BMString::new(words) } pub struct BMString { inner: CString, } impl BMString { fn new(words: T) -> Result where T: Into>, { Ok(Self { inner: CString::new(words)?, }) } pub unsafe fn as_raw(&self) -> CKSTRING { self.inner.as_ptr() as CKSTRING } } pub unsafe fn from_native_string_array(ptr: PCKSTRING) -> Result> { let mut rv = Vec::new(); loop { let item_ptr = unsafe { *ptr } as CKSTRING; if item_ptr.is_null() { break; } let item = unsafe { from_native_string(item_ptr)? }; rv.push(item); } Ok(rv) } pub unsafe fn to_native_string_array(words: I) -> Result where I: Iterator, T: Into>, { BMStringArray::new(words) } pub struct BMStringArray { array_items: Vec, array_body: Vec, } impl BMStringArray { fn new(words: I) -> Result where I: Iterator, T: Into>, { // Build array items let array_items = words .map(|word| CString::new(word)) .collect::, std::ffi::NulError>>()?; // Build array body. // In theory, move operation will not affect data allocated on heap. // So we can simply fetch the address of this new generated array. let array_body = array_items .iter() .map(|i| i.as_ptr() as CKSTRING) .chain(std::iter::once(std::ptr::null_mut() as CKSTRING)) .collect::>(); // Return value Ok(Self { array_items, array_body }) } pub fn len(&self) -> usize { self.array_items.len() } pub unsafe fn as_raw(&self) -> PCKSTRING { self.array_body.as_ptr() as PCKSTRING } }