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 { // Initialize all pool by fetching writer from them cffi_wrapper!(|| { let _pool = pull_writer!(SCHEMA_POOL)?; let _pool = pull_writer!(PROGRAM_POOL)?; Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFShutdown() -> bool { // Free all pool stored objects cffi_wrapper!(|| { let mut pool = pull_writer!(SCHEMA_POOL)?; pool.clear(); let mut pool = pull_writer!(PROGRAM_POOL)?; pool.clear(); Ok(()) }) } #[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 WFSchemaSetPath( 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_path(cstr_ffi::parse_ffi_string(in_value)?); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaSetClsid( 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_clsid(cstr_ffi::parse_ffi_string(in_value)?); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaSetName( 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)?; let name = if in_value.is_null() { None } else { Some(cstr_ffi::parse_ffi_string(in_value)?) }; schema.set_name(name); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaSetIcon( 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)?; let icon = if in_value.is_null() { None } else { Some(cstr_ffi::parse_ffi_string(in_value)?) }; schema.set_icon(icon); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaSetBehavior( 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)?; let behavior = if in_value.is_null() { None } else { Some(cstr_ffi::parse_ffi_string(in_value)?) }; schema.set_behavior(behavior); Ok(()) }) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaAddStr( in_schema: in_param_ty!(Token), in_name: in_param_ty!(CStyleString), in_value: in_param_ty!(CStyleString), ) -> bool { cffi_wrapper!( |in_schema: Token, in_name: CStyleString, in_value: CStyleString| { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.get_mut(in_schema)?; schema.add_str( cstr_ffi::parse_ffi_string(in_name)?, cstr_ffi::parse_ffi_string(in_value)?, )?; Ok(()) } ) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaAddIcon( in_schema: in_param_ty!(Token), in_name: in_param_ty!(CStyleString), in_value: in_param_ty!(CStyleString), ) -> bool { cffi_wrapper!( |in_schema: Token, in_name: CStyleString, in_value: CStyleString| { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.get_mut(in_schema)?; schema.add_icon( cstr_ffi::parse_ffi_string(in_name)?, cstr_ffi::parse_ffi_string(in_value)?, )?; Ok(()) } ) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaAddBehavior( in_schema: in_param_ty!(Token), in_name: in_param_ty!(CStyleString), in_value: in_param_ty!(CStyleString), ) -> bool { cffi_wrapper!( |in_schema: Token, in_name: CStyleString, in_value: CStyleString| { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.get_mut(in_schema)?; schema.add_behavior( cstr_ffi::parse_ffi_string(in_name)?, cstr_ffi::parse_ffi_string(in_value)?, )?; Ok(()) } ) } #[unsafe(no_mangle)] pub extern "C" fn WFSchemaAddExt( in_schema: in_param_ty!(Token), in_ext: in_param_ty!(CStyleString), in_ext_name: in_param_ty!(CStyleString), in_ext_icon: in_param_ty!(CStyleString), in_ext_behavior: in_param_ty!(CStyleString), ) -> bool { cffi_wrapper!(|in_schema: Token, in_ext: CStyleString, in_ext_name: CStyleString, in_ext_icon: CStyleString, in_ext_behavior: CStyleString| { let mut pool = pull_writer!(SCHEMA_POOL)?; let schema = pool.get_mut(in_schema)?; schema.add_ext( cstr_ffi::parse_ffi_string(in_ext)?, cstr_ffi::parse_ffi_string(in_ext_name)?, cstr_ffi::parse_ffi_string(in_ext_icon)?, cstr_ffi::parse_ffi_string(in_ext_behavior)?, )?; Ok(()) }) } #[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())); #[unsafe(no_mangle)] pub extern "C" fn WFProgramCreate( 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 = Program::new(schema)?; Ok(pool.allocate(program)?) }) } #[unsafe(no_mangle)] pub extern "C" fn WFProgramDestroy(in_program: in_param_ty!(Token)) -> bool { cffi_wrapper!(|in_program: Token| { let mut pool = pull_writer!(PROGRAM_POOL)?; Ok(pool.free(in_program)?) }) } // endregion // endregion