mod cstr_ffi; mod last_error; mod object_pool; use object_pool::ObjectPool; use std::sync::{LazyLock, RwLock}; use thiserror::Error as TeError; use wfassoc::highlevel::{Program, Schema}; pub use cstr_ffi::CStyleString; pub use object_pool::Token; // region: Error /// 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), /// Error when operating Program. #[error("{0}")] Program(#[from] wfassoc::highlevel::ProgramError), /// Error when manipulating with C-style string. #[error("{0}")] CStrFfi(#[from] cstr_ffi::Error), /// Error when manipulating with object pool. #[error("{0}")] ObjectPool(#[from] object_pool::Error), /// Error when manipulating with poison RwLock #[error("RwLock is poisoning")] PoisonRwLock, } /// Result type used in this crate. type Result = std::result::Result; // endregion // region: Macros macro_rules! in_param_ty { ($t:ty) => { $t }; } macro_rules! out_param_ty { ($t:ty) => { *mut $t }; } macro_rules! set_out_param { ($lhs:expr, $rhs:expr) => { unsafe { *$lhs = $rhs }; }; } macro_rules! pull_reader { ($pool:expr) => { $pool.read().map_err(|_| Error::PoisonRwLock) }; } macro_rules! pull_writer { ($pool:expr) => { $pool.write().map_err(|_| Error::PoisonRwLock) }; } /// Macro to wrap inner function execution with standard error handling pattern. /// /// For functions with no output parameter and no input parameters: /// ```ignore /// cffi_wrapper!(|| { /// // inner function body returning Result<()> /// }); /// ``` /// /// For functions with no output parameter and with input parameters: /// ```ignore /// cffi_wrapper!(|param1: Type1, param2: Type2| { /// // inner function body using param1, param2 returning Result<()> /// }); /// ``` /// /// For functions with one output parameter and no input parameters: /// ```ignore /// cffi_wrapper!(|| -> (out_param, OutType) { /// // inner function body returning Result /// }); /// ``` /// /// For functions with one output parameter and with input parameters: /// ```ignore /// cffi_wrapper!(|param1: Type1| -> (out_param, OutType) { /// // inner function body using param1 returning Result /// }); /// ``` macro_rules! cffi_wrapper { // Case with output parameter and input parameters (|$($param:ident: $param_ty:ty),*| -> ($out_param:ident: $out_param_ty:ty) $inner_body:block) => {{ fn inner($($param: $param_ty),*) -> Result<$out_param_ty> $inner_body match inner($($param),*) { Ok(rv) => { set_out_param!($out_param, rv); last_error::clear_last_error(); true } Err(e) => { last_error::set_last_error(e.to_string().as_str()); false } } }}; // Case with output parameter and no input parameters (|| -> ($out_param:ident: $out_param_ty:ty) $inner_body:block) => {{ fn inner() -> Result<$out_param_ty> $inner_body match inner() { Ok(rv) => { set_out_param!($out_param, rv); last_error::clear_last_error(); true } Err(e) => { last_error::set_last_error(e.to_string().as_str()); false } } }}; // Case without output parameter but with input parameters (|$($param:ident: $param_ty:ty),*| $inner_body:block) => {{ fn inner($($param: $param_ty),*) -> Result<()> $inner_body match inner($($param),*) { Ok(_) => { last_error::clear_last_error(); true } Err(e) => { last_error::set_last_error(e.to_string().as_str()); false } } }}; // Case without output parameter and no input parameters ($inner_body:block) => {{ fn inner() -> Result<()> $inner_body match inner() { Ok(_) => { last_error::clear_last_error(); true } Err(e) => { last_error::set_last_error(e.to_string().as_str()); false } } }}; } // endregion // region: Exposed Functions // region: Facilities #[unsafe(no_mangle)] pub extern "C" fn WFStartup() -> bool { // TODO: Initialize all pool by fetching writer from them true } #[unsafe(no_mangle)] pub extern "C" fn WFShutdown() -> bool { // TODO: Free all pool stored objects true } #[unsafe(no_mangle)] pub extern "C" fn WFGetLastError() -> CStyleString { last_error::get_last_error() } #[unsafe(no_mangle)] pub extern "C" fn WFHasPrivilege() -> bool { wfassoc::win32::utilities::has_privilege() } // endregion // region: Schema static SCHEMA_POOL: LazyLock>> = LazyLock::new(|| RwLock::new(ObjectPool::new())); #[unsafe(no_mangle)] pub extern "C" fn WFSchemaCreate(out_schema: out_param_ty!(Token)) -> bool { cffi_wrapper!(|| -> (out_schema: Token) { let mut pool = pull_writer!(SCHEMA_POOL)?; Ok(pool.allocate(Schema::new())?) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaSetIdentifier( in_schema: in_param_ty!(Token), in_value: in_param_ty!(CStyleString), ) -> bool { cffi_wrapper!(|in_schema: Token, in_value: CStyleString| { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.get_mut(in_schema)?; schema.set_identifier(cstr_ffi::parse_ffi_string(in_value)?); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaIntoProgram( in_schema: in_param_ty!(Token), out_program: out_param_ty!(Token), ) -> bool { cffi_wrapper!(|in_schema: Token| -> (out_program: Token) { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.pop(in_schema)?; let mut pool = pull_writer!(PROGRAM_POOL)?; let program = schema.into_program()?; Ok(pool.allocate(program)?) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaDestroy(in_schema: in_param_ty!(Token)) -> bool { cffi_wrapper!(|in_schema: Token| { let mut pool = pull_writer!(SCHEMA_POOL)?; Ok(pool.free(in_schema)?) }) } // endregion // region: Program static PROGRAM_POOL: LazyLock>> = LazyLock::new(|| RwLock::new(ObjectPool::new())); // endregion // endregion