diff --git a/wfassoc-cdylib/src/cstr_ffi.rs b/wfassoc-cdylib/src/cstr_ffi.rs index ed5b2a7..d44e72f 100644 --- a/wfassoc-cdylib/src/cstr_ffi.rs +++ b/wfassoc-cdylib/src/cstr_ffi.rs @@ -1,7 +1,7 @@ //! When calling this dynamic library with outside programs, //! outer programs may usually need to fetch string resource produced by Rust code. //! However it is impossible pass Rust string directly to outer program. -//! +//! //! This module provide **thread independent** string cache for resolving this issue. //! When we need pass string to outer programs, we push that string into this string as C-like format, //! then return its pointer to outer program. @@ -9,21 +9,44 @@ //! The only thing that outer programs should note is that this string is volatile, //! once they get it, they must dupliate it immediately before any futher calling to this dynamic library. use std::cell::RefCell; -use std::ffi::{CString, CStr, c_char}; +use std::ffi::{CStr, CString, c_char}; +use thiserror::Error as TeError; -struct StringCache { +// region: Error + +/// Error occurs in this crate. +#[derive(Debug, TeError)] +pub enum Error { + #[error("unexpected NUL when parsing into C/C++ string")] + UnexpectedNul(#[from] std::ffi::NulError), + + #[error("given string pointer is nullptr when parsing from C/C++ string")] + NullPtr, + #[error("invalid UTF8 sequence when parsing from C/C++ string")] + InvalidEncoding(#[from] std::str::Utf8Error), +} + +/// Result type used in this crate. +type Result = std::result::Result; + +// endregion + +// region: FFI String + +struct FfiString { msg: CString, } -impl StringCache { +impl FfiString { fn new() -> Self { Self { msg: CString::new("").expect("empty string must be valid for CString"), } } - pub fn set_msg(&mut self, msg: &str) { - self.msg = CString::new(msg).expect("unexpected blank in string push into string cache"); + pub fn set_msg(&mut self, msg: &str) -> Result<()> { + self.msg = CString::new(msg)?; + Ok(()) } pub fn get_msg(&self) -> *const c_char { @@ -35,15 +58,19 @@ impl StringCache { } } +// endregion + +// region: Exposed Functions + thread_local! { - static STRING_CACHE: RefCell = RefCell::new(StringCache::new()); + static STRING_CACHE: RefCell = RefCell::new(FfiString::new()); } /// Set thread local string exposed for C code. -pub fn set_ffi_string(msg: &str) { +pub fn set_ffi_string(msg: &str) -> Result<()> { STRING_CACHE.with(|e| { - e.borrow_mut().set_msg(msg); - }); + e.borrow_mut().set_msg(msg) + }) } /// Get const pointer to thread local string exposed for C code. @@ -52,7 +79,7 @@ pub fn get_ffi_string() -> *const c_char { } /// Clear thread local string exposed for C code. -/// +/// /// This function usually should be called at the beginning of every exposed C functions. pub fn clear_ffi_string() { STRING_CACHE.with(|e| { @@ -61,6 +88,13 @@ pub fn clear_ffi_string() { } /// Parse string given by C code into Rust string. -pub fn parse_ffi_string<'a>(ptr: *const c_char) -> &'a CStr { - unsafe { CStr::from_ptr(ptr) } +pub fn parse_ffi_string<'a>(ptr: *const c_char) -> Result<&'a str> { + if ptr.is_null() { + Err(Error::NullPtr) + } else { + let c_str = unsafe { CStr::from_ptr(ptr) }; + Ok(c_str.to_str()?) + } } + +// endregion diff --git a/wfassoc-cdylib/src/lib.rs b/wfassoc-cdylib/src/lib.rs index dc20722..bd01077 100644 --- a/wfassoc-cdylib/src/lib.rs +++ b/wfassoc-cdylib/src/lib.rs @@ -12,6 +12,9 @@ mod wrapper; /// Error occurs in this crate. #[derive(Debug, TeError)] enum Error { + /// Error when operating Schema. + #[error("{0}")] + Schema(#[from] wfassoc::highlevel::SchemaError), /// Error when parsing Schema into Program. #[error("{0}")] ParseProgram(#[from] wfassoc::highlevel::ParseProgramError), @@ -19,10 +22,9 @@ enum Error { #[error("{0}")] Program(#[from] wfassoc::highlevel::ProgramError), - #[error("error occurs when parsing into C/C++ string")] - CastIntoCStr(#[from] std::ffi::NulError), - #[error("error occurs when parsing from C/C++ string")] - CastFromCStr(#[from] std::ffi::IntoStringError), + /// Error when manipulating with C-style string. + #[error("{0}")] + CStrFfi(#[from] cstr_ffi::Error), } /// Result type used in this crate. @@ -37,12 +39,12 @@ type Result = std::result::Result; #[unsafe(no_mangle)] pub extern "C" fn WFStartup() -> bool { - false + true } #[unsafe(no_mangle)] pub extern "C" fn WFShutdown() -> bool { - false + true } #[unsafe(no_mangle)]