diff --git a/wfassoc_dylib/src/last_error.rs b/wfassoc_dylib/src/last_error.rs new file mode 100644 index 0000000..6f1997b --- /dev/null +++ b/wfassoc_dylib/src/last_error.rs @@ -0,0 +1,58 @@ +//! When calling function with dynamic library, +//! function return value indicates whether function has been successfully executed. +//! When function return `false`, programmer may want to know which error occurs. +//! +//! This module provide **thread independent** error message string storage, +//! which is more like Win32 `GetLastError()` but return error message instead of error code. +//! These module provided functions will be called when executing main module functions. + +use std::cell::RefCell; +use std::ffi::{CString, c_char}; + +struct LastError { + msg: CString, +} + +impl LastError { + 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 error message output"); + } + + pub fn get_msg(&self) -> *const c_char { + self.msg.as_ptr() + } + + pub fn clear_msg(&mut self) { + self.msg = CString::new("").expect("empty string must be valid for CString"); + } +} + +thread_local! { + static LAST_ERROR: RefCell = RefCell::new(LastError::new()); +} + +/// Set thread local error message. +pub fn set_last_error(msg: &str) { + LAST_ERROR.with(|e| { + e.borrow_mut().set_msg(msg); + }); +} + +/// Get const pointer to thread local error message string. +/// If there is no error, return pointer will point to empty string. +pub fn get_last_error() -> *const c_char { + LAST_ERROR.with(|e| e.borrow().get_msg()) +} + +/// Clear thread local error message (reset to empty string). +pub fn clear_last_error() { + LAST_ERROR.with(|e| { + e.borrow_mut().clear_msg(); + }); +} diff --git a/wfassoc_dylib/src/lib.rs b/wfassoc_dylib/src/lib.rs index 3f57957..a1a8cec 100644 --- a/wfassoc_dylib/src/lib.rs +++ b/wfassoc_dylib/src/lib.rs @@ -2,6 +2,9 @@ use std::cell::RefCell; use std::ffi::{CString, c_char}; use thiserror::Error as TeError; +mod last_error; +mod string_cache; + // region: Error /// Error occurs in this crate. @@ -22,54 +25,6 @@ type Result = std::result::Result; // endregion -// region: Last Error - -struct LastError { - msg: CString, -} - -impl LastError { - 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 error message output"); - } - - pub fn get_msg(&self) -> *const c_char { - self.msg.as_ptr() - } - - pub fn clear_msg(&mut self) { - self.msg = CString::new("").expect("empty string must be valid for CString"); - } -} - -thread_local! { - static LAST_ERROR: RefCell = RefCell::new(LastError::new()); -} - -fn set_last_error(msg: &str) { - LAST_ERROR.with(|e| { - e.borrow_mut().set_msg(msg); - }); -} - -fn get_last_error() -> *const c_char { - LAST_ERROR.with(|e| e.borrow().get_msg()) -} - -fn clear_last_error() { - LAST_ERROR.with(|e| { - e.borrow_mut().clear_msg(); - }); -} - -// endregion - // region: Macros @@ -87,7 +42,7 @@ pub extern "C" fn WFShutdown() -> bool { #[unsafe(no_mangle)] pub extern "C" fn WFGetLastError() -> *const c_char { - get_last_error() + last_error::get_last_error() } #[unsafe(no_mangle)] diff --git a/wfassoc_dylib/src/string_cache.rs b/wfassoc_dylib/src/string_cache.rs new file mode 100644 index 0000000..81737fe --- /dev/null +++ b/wfassoc_dylib/src/string_cache.rs @@ -0,0 +1,52 @@ +//! 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. +//! So that outside program can utilize it like calling C/C++ library. +//! 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, c_char}; + +struct StringCache { + msg: CString, +} + +impl StringCache { + 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 get_msg(&self) -> *const c_char { + self.msg.as_ptr() + } + + pub fn clear_msg(&mut self) { + self.msg = CString::new("").expect("empty string must be valid for CString"); + } +} + +thread_local! { + static STRING_CACHE: RefCell = RefCell::new(StringCache::new()); +} + +/// Set thread local string cache. +pub fn set_string_cache(msg: &str) { + STRING_CACHE.with(|e| { + e.borrow_mut().set_msg(msg); + }); +} + +/// Get const pointer to thread local string cache for outside program visiting. +pub fn get_string_cache() -> *const c_char { + STRING_CACHE.with(|e| e.borrow().get_msg()) +}