1#![allow(unused_unsafe)] #![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); let v2 = 123.to_variant(); let v3 = 123.into(); 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(); let back: Variant = ptr.try_into().unwrap(); assert_eq!(bstr, back);
145 }
146}