win-variant 0.3.0

This is a rust crate that aims to provide a more ergonomic way of working with variants in winapi based projects.
Documentation
//! # win-variant
//!
//! win-variant is a crate to improve the ergonomics of working with VARIANTS
//! from winapi.
//!
//! The crate uses enums and conversion traits to allow for modern rust usage
//! as well as provides utilities to easily free VARIANTS correctly.

/// Variant version of a true boolean as per win32 documentation.
///
/// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant#__variant_name_2__variant_name_3bool
const BOOL_TRUE: i16 = 0b1111_1111;
/// Variant version of a false boolean as per win32 documentation.
///
/// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant#__variant_name_2__variant_name_3bool
const BOOL_FALSE: i16 = 0b0000_0000;

mod arg;
mod errors;
mod result;
mod safearray;

use winapi::um::{oaidl::VARIANT, oleauto::VariantClear};

pub use arg::VariantArg;
pub use errors::{VariantArgError, VariantResultError};
pub use result::VariantResult;
pub use safearray::SafeArray;

/// free_variants is a utility to properly free the allocated resources of
/// variants.
pub fn free_variants(variants: &mut Vec<VARIANT>) {
    for variant in variants {
        unsafe {
            free_variant(variant);
        }
    }
}
/// # Safety
///
/// free_variant is a utility to properly free the allocated resources of a
/// variant.
pub unsafe fn free_variant(variant: &mut VARIANT) {
    VariantClear(variant);
}

#[cfg(test)]
mod tests {
    use crate::{free_variant, SafeArray, VariantArg, VariantResult};
    use log::trace;
    use std::convert::{TryFrom, TryInto};
    use winapi::um::oaidl::VARIANT;

    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    #[test]
    fn it_converts_to_result_from_bstr_arg() {
        init();
        let src = "Test Value";
        let _arg = VariantArg::try_from(src).unwrap();

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::String(v) = result {
            if !src.eq(&v) {
                panic!("expected {:?}, got {:?}", src, v);
            }
        } else {
            panic!("expected String, got: {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_int_arg() {
        init();
        let src: i32 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::Int(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected Int, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_uint_arg() {
        init();
        let src: u32 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::Uint(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected Int, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_f32_arg() {
        init();
        let src: f32 = 1234.1;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::F32(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected F32, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_f64_arg() {
        init();
        let src: f64 = 1234.1;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::F64(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected F64, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_i16_arg() {
        init();
        let src: i16 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::I16(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected i16, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_u16_arg() {
        init();
        let src: u16 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::U16(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected F64, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_i64_arg() {
        init();
        let src: i64 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::I64(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected I64, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_u64_arg() {
        init();
        let src: u64 = 1234;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::U64(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected U64, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_bool_arg() {
        init();
        let src = true;
        let _arg = VariantArg::from(src);

        let mut v: VARIANT = _arg.into();
        let result = VariantResult::try_from(v).unwrap();
        unsafe {
            free_variant(&mut v);
        }

        if let VariantResult::BOOL(v) = result {
            if src != v {
                panic!("expected {:}, got {:}", src, v);
            }
        } else {
            panic!("expected Bool, got {:?}", result);
        }
    }

    #[test]
    fn it_converts_to_result_from_safearray_arg() {
        init();

        let src = vec!["hello", "world"];
        let src_sa = SafeArray::try_from(&src).unwrap();
        let _arg = VariantArg::SafeArray(src_sa);
        let mut v: VARIANT = _arg.into();

        let result = VariantResult::try_from(v).unwrap();
        trace!("extracted result");
        if let VariantResult::SafeArray(sa) = result {
            let values: Vec<String> = sa.try_into().unwrap();
            unsafe { free_variant(&mut v) };
            if src[0] != values[0] {
                panic!("expected {:}, got {:}", src[0], values[0]);
            }
        } else {
            unsafe { free_variant(&mut v) };
            panic!("expected SafeArray, got {:?}", result);
        }
    }
}