variant_rs/
lib.rs

1#![allow(unused_unsafe)] // rustc bug #94912
2#![doc = include_str!("../README.md")]
3
4use crate::com_types::bool::ComBool;
5use crate::com_types::ptr_wrapper::PtrWrapper;
6pub use crate::variant::*;
7
8pub use windows::Win32::System::Variant::{VARENUM, VARIANT};
9
10pub mod com_types;
11pub mod convert;
12pub mod dispatch;
13pub mod variant;
14
15#[doc(hidden)]
16#[macro_export]
17macro_rules! variant {
18    ( $type: expr ) => {
19        unsafe {
20            let mut variant: VARIANT = std::mem::zeroed();
21            (*variant.Anonymous.Anonymous).vt = VARENUM($type as u16);
22            variant
23        }
24    };
25
26    ( $type: expr, $field: ident, $val: expr ) => {
27        unsafe {
28            let mut variant: VARIANT = variant!($type);
29            (*variant.Anonymous.Anonymous).Anonymous.$field = $val;
30            variant
31        }
32    };
33
34    ( $type: expr, ($field: ident), $val: expr ) => {
35        unsafe {
36            let mut variant: VARIANT = variant!($type);
37            variant.Anonymous.$field = $val;
38            variant
39        }
40    };
41}
42
43#[cfg(test)]
44mod tests {
45    use crate::{ToVariant, Variant, VariantType};
46    use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
47    use rust_decimal_macros::dec;
48
49    use std::mem::ManuallyDrop;
50    use windows::core::BSTR;
51    use windows::Win32::Foundation::{DECIMAL, DECIMAL_0, DECIMAL_0_0, DECIMAL_1, VARIANT_BOOL};
52    use windows::Win32::System::Com::CY;
53    use windows::Win32::System::Variant::{VARENUM, VARIANT};
54
55    macro_rules! roundtrip
56    {
57        ( ($t: ident $(, $($tts:tt)*)?), $b: expr ) =>
58        {
59            #[test]
60            #[allow(non_snake_case)]
61            fn $t()
62            {
63                let conv = variant!(VariantType::$t $(, $($tts)*)?).try_into();
64                assert_eq!(conv, Ok($b), "COM to Rust");
65                let cv: VARIANT = $b.try_into().unwrap();
66                assert_eq!(cv.try_into(), Ok($b), "Rust to COM");
67            }
68        }
69    }
70
71    roundtrip!((VT_EMPTY), Variant::Empty);
72    roundtrip!((VT_NULL), Variant::Null);
73
74    roundtrip!((VT_BOOL, boolVal, VARIANT_BOOL(!0i16)), Variant::Bool(true));
75
76    roundtrip!((VT_I1, cVal, 0x55), Variant::I8(0x55));
77    roundtrip!((VT_I2, iVal, 0x55aa), Variant::I16(0x55aa));
78    roundtrip!((VT_I4, lVal, 0x55aa55aa), Variant::I32(0x55aa55aa));
79    roundtrip!(
80        (VT_I8, llVal, 0x55aa55aa55aa55aa),
81        Variant::I64(0x55aa55aa55aa55aa)
82    );
83
84    roundtrip!((VT_UI1, bVal, 0x55), Variant::U8(0x55));
85    roundtrip!((VT_UI2, uiVal, 0x55aa), Variant::U16(0x55aa));
86    roundtrip!((VT_UI4, ulVal, 0x55aa55aa), Variant::U32(0x55aa55aa));
87    roundtrip!(
88        (VT_UI8, ullVal, 0x55aa55aa55aa55aa),
89        Variant::U64(0x55aa55aa55aa55aa)
90    );
91
92    roundtrip!((VT_R4, fltVal, 0.5f32), Variant::F32(0.5f32));
93    roundtrip!((VT_R8, dblVal, 0.5f64), Variant::F64(0.5f64));
94
95    roundtrip!(
96        (VT_CY, cyVal, CY { int64: 123456 }),
97        Variant::Currency(dec!(12.3456).into())
98    );
99
100    roundtrip!(
101        (
102            VT_DECIMAL,
103            (decVal),
104            DECIMAL {
105                wReserved: VariantType::VT_DECIMAL as u16,
106                Anonymous1: DECIMAL_0 {
107                    Anonymous: DECIMAL_0_0 { scale: 4, sign: 0 }
108                },
109                Hi32: 0,
110                Anonymous2: DECIMAL_1 { Lo64: 123456 }
111            }
112        ),
113        Variant::Decimal(dec!(12.3456))
114    );
115
116    roundtrip!(
117        (VT_DATE, date, 5.25),
118        Variant::Date(NaiveDateTime::new(
119            NaiveDate::from_ymd_opt(1900, 1, 4).unwrap(),
120            NaiveTime::from_hms_opt(6, 0, 0).unwrap()
121        ))
122    );
123
124    roundtrip!(
125        (
126            VT_BSTR,
127            bstrVal,
128            ManuallyDrop::new(BSTR::from("Hello, world!"))
129        ),
130        Variant::String(BSTR::from("Hello, world!"))
131    );
132
133    #[test]
134    fn main() {
135        let v1 = Variant::I32(123); // manual instanciation
136        let v2 = 123.to_variant(); // ToVariant trait
137        let v3 = 123.into(); // From / Into traits
138        assert_eq!(v1, v2);
139        assert_eq!(v1, v3);
140
141        let bstr: Variant = "Hello, world!".into();
142        let ptr: VARIANT = bstr.clone().try_into().unwrap(); // convert to COM VARIANT
143        let back: Variant = ptr.try_into().unwrap(); // convert back
144        assert_eq!(bstr, back);
145    }
146}