catia 0.1.1

Rust COM bindings and helpers for CATIA V5
Documentation
use windows::Win32::System::Variant::{
    VariantInit, VariantChangeType, VariantClear, VAR_CHANGE_FLAGS, VARENUM,
    VT_BSTR, VT_I4, VT_BOOL, VT_DISPATCH,
};
use windows::core::VARIANT;
use windows::Win32::System::Com::IDispatch;
use windows::core::{Result as WinResult, BSTR, Interface};
// no need for SysStringLen; use BSTR::from_raw

pub fn variant_empty() -> VARIANT {
    unsafe { VariantInit() }
}

pub fn variant_bstr(s: &str) -> VARIANT {
    VARIANT::from(s)
}

pub fn variant_i4(i: i32) -> VARIANT {
    VARIANT::from(i)
}

pub fn variant_r8(f: f64) -> VARIANT {
    VARIANT::from(f)
}

pub fn variant_bool(val: bool) -> VARIANT {
    VARIANT::from(val)
}

// Construct VARIANT holding SAFEARRAY of BSTR from &[&str]
// NOTE: Placeholder implementation; full SAFEARRAY wiring will follow.
pub fn variant_bstr_array(items: &[&str]) -> VARIANT {
    let _ = items;
    variant_empty()
}


// Reading helpers
fn coerce(var: &VARIANT, vt: VARENUM) -> WinResult<VARIANT> {
    let mut dest = unsafe { VariantInit() };
    unsafe { VariantChangeType(&mut dest as *mut VARIANT, var as *const VARIANT, VAR_CHANGE_FLAGS(0), vt)?; }
    Ok(dest)
}

pub fn variant_to_string(var: &VARIANT) -> WinResult<Option<String>> {
    // Try coerce to BSTR
    let mut coerced = coerce(var, VT_BSTR)?;
    // SAFETY: after VariantChangeType vt==VT_BSTR and bstrVal contains valid BSTR
    let ptr = unsafe { (*coerced.as_raw()).Anonymous.Anonymous.Anonymous.bstrVal };
    let out = if ptr.is_null() {
        None
    } else {
        // SAFETY: wrap raw BSTR pointer, convert to String, then forget to avoid double free
        let b = unsafe { BSTR::from_raw(ptr as *mut u16) };
        let s = b.to_string();
        std::mem::forget(b);
        Some(s)
    };
    unsafe { VariantClear(&mut coerced as *mut VARIANT)?; }
    Ok(out)
}

pub fn variant_to_i32(var: &VARIANT) -> WinResult<Option<i32>> {
    let mut coerced = coerce(var, VT_I4)?;
    let val = unsafe { (*coerced.as_raw()).Anonymous.Anonymous.Anonymous.lVal };
    unsafe { VariantClear(&mut coerced as *mut VARIANT)?; }
    Ok(Some(val))
}

pub fn variant_to_bool(var: &VARIANT) -> WinResult<Option<bool>> {
    let mut coerced = coerce(var, VT_BOOL)?;
    let vbool = unsafe { (*coerced.as_raw()).Anonymous.Anonymous.Anonymous.boolVal };
    unsafe { VariantClear(&mut coerced as *mut VARIANT)?; }
    Ok(Some(vbool != 0))
}

pub fn variant_to_dispatch(var: &VARIANT) -> WinResult<Option<IDispatch>> {
    let mut coerced = coerce(var, VT_DISPATCH)?;
    let ptr = unsafe { (*coerced.as_raw()).Anonymous.Anonymous.Anonymous.pdispVal };
    if ptr.is_null() {
        unsafe { VariantClear(&mut coerced as *mut VARIANT)?; }
        return Ok(None);
    }
    // Construct interface, clone to AddRef, then forget original to avoid double release
    let disp = unsafe { IDispatch::from_raw(ptr.cast()) };
    let keep = disp.clone();
    std::mem::forget(disp);
    unsafe { VariantClear(&mut coerced as *mut VARIANT)?; }
    Ok(Some(keep))
}