Skip to main content

wmi/
variant.rs

1use crate::safearray::SafeArrayAccessor;
2use crate::{
3    WMIError, WMIResult, result_enumerator::IWbemClassWrapper, safearray::safe_array_to_vec,
4};
5use serde::Serialize;
6use std::convert::TryFrom;
7use std::ptr::NonNull;
8use windows::Win32::Foundation::{VARIANT_FALSE, VARIANT_TRUE};
9use windows::Win32::System::Ole::SafeArrayCreateVector;
10use windows::Win32::System::Variant::*;
11use windows::Win32::System::Variant::{VARIANT, VT_NULL};
12use windows::Win32::System::Wmi::{self, CIMTYPE_ENUMERATION, IWbemClassObject};
13use windows::core::{IUnknown, Interface, PCWSTR};
14
15fn variant_from_string_array(array: &[String]) -> WMIResult<VARIANT> {
16    // Convert the strings to null terminated vectors of `u16`s.
17    let v: Vec<Vec<u16>> = array
18        .iter()
19        .map(|s| s.encode_utf16().chain([0]).collect())
20        .collect();
21
22    // Safety: each pointer points to a null terminated slice of `u16`s.
23    let v: Vec<_> = v
24        .iter()
25        .map(|b| unsafe { PCWSTR::from_raw(b.as_ptr()) })
26        .collect();
27
28    // The new variant will allocate new `BSTR`s for the array,
29    // so it is safe to free `v` after this call.
30    let variant = unsafe { InitVariantFromStringArray(&v) }?;
31
32    Ok(variant)
33}
34
35fn set_variant_type(variant: &mut VARIANT, new_type: VARENUM) {
36    // Safety: it's always valid to access the `vt` field.
37    unsafe {
38        (&mut variant.Anonymous.Anonymous).vt = new_type;
39    }
40}
41
42#[derive(Debug, PartialEq, Serialize, Clone)]
43#[serde(untagged)]
44pub enum Variant {
45    Empty,
46    Null,
47
48    String(String),
49
50    I1(i8),
51    I2(i16),
52    I4(i32),
53    I8(i64),
54
55    R4(f32),
56    R8(f64),
57
58    Bool(bool),
59
60    UI1(u8),
61    UI2(u16),
62    UI4(u32),
63    UI8(u64),
64
65    Array(Vec<Variant>),
66
67    /// Temporary variant used internally
68    Unknown(IUnknownWrapper),
69    Object(IWbemClassWrapper),
70}
71
72// The `cast_num` macro is used to convert a numerical variable to a variant of the given CIMTYPE.
73macro_rules! cast_num {
74    ($var:ident, $cim_type: ident) => {
75        if $cim_type == Wmi::CIM_UINT8 {
76            Ok(Variant::UI1($var as u8))
77        } else if $cim_type == Wmi::CIM_UINT16 {
78            Ok(Variant::UI2($var as u16))
79        } else if $cim_type == Wmi::CIM_UINT32 {
80            Ok(Variant::UI4($var as u32))
81        } else if $cim_type == Wmi::CIM_UINT64 {
82            Ok(Variant::UI8($var as u64))
83        } else if $cim_type == Wmi::CIM_SINT8 {
84            Ok(Variant::I1($var as i8))
85        } else if $cim_type == Wmi::CIM_SINT16 {
86            Ok(Variant::I2($var as i16))
87        } else if $cim_type == Wmi::CIM_SINT32 {
88            Ok(Variant::I4($var as i32))
89        } else if $cim_type == Wmi::CIM_SINT64 {
90            Ok(Variant::I8($var as i64))
91        } else if $cim_type == Wmi::CIM_REAL32 {
92            Ok(Variant::R4($var as f32))
93        } else if $cim_type == Wmi::CIM_REAL64 {
94            Ok(Variant::R8($var as f64))
95        } else if $cim_type == Wmi::CIM_CHAR16 {
96            Ok(Variant::String(String::from_utf16(&[$var as u16])?))
97        } else {
98            Err(WMIError::ConvertVariantError(format!(
99                "Value {:?} cannot be turned into a CIMTYPE {:?}",
100                $var, $cim_type,
101            )))
102        }
103    };
104}
105
106impl Variant {
107    /// Create a `Variant` instance from a raw `VARIANT`.
108    ///
109    /// Note: this function is safe since manipulating a `VARIANT` by hand is an *unsafe* operation,
110    /// so we can assume that the `VARIANT` is valid.
111    pub fn from_variant(vt: &VARIANT) -> WMIResult<Variant> {
112        let variant_type = unsafe { vt.Anonymous.Anonymous.vt };
113
114        // variant_type has two 'forms':
115        // 1. A simple type like `VT_BSTR` .
116        // 2. An array of certain type like `VT_ARRAY | VT_BSTR`.
117        if variant_type & VT_ARRAY == VT_ARRAY {
118            let array = NonNull::new(unsafe { vt.Anonymous.Anonymous.Anonymous.parray })
119                .ok_or(WMIError::NullPointerResult)?;
120
121            let item_type = variant_type & VT_TYPEMASK;
122
123            return Ok(Variant::Array(unsafe {
124                safe_array_to_vec(array, item_type)?
125            }));
126        }
127
128        // See https://msdn.microsoft.com/en-us/library/cc237865.aspx for more info.
129        // Rust can infer the return type of `vt.*Val()` calls,
130        // but it's easier to read when the type is named explicitly.
131        let variant_value = match variant_type {
132            VT_BSTR => {
133                let bstr_ptr = unsafe { &vt.Anonymous.Anonymous.Anonymous.bstrVal };
134                let bstr_as_str = bstr_ptr.to_string();
135                Variant::String(bstr_as_str)
136            }
137            VT_I1 => {
138                let num = unsafe { vt.Anonymous.Anonymous.Anonymous.cVal };
139
140                Variant::I1(num as _)
141            }
142            VT_I2 => {
143                let num: i16 = unsafe { vt.Anonymous.Anonymous.Anonymous.iVal };
144
145                Variant::I2(num)
146            }
147            VT_I4 => {
148                let num: i32 = unsafe { vt.Anonymous.Anonymous.Anonymous.lVal };
149
150                Variant::I4(num)
151            }
152            VT_I8 => {
153                let num: i64 = unsafe { vt.Anonymous.Anonymous.Anonymous.llVal };
154
155                Variant::I8(num)
156            }
157            VT_R4 => {
158                let num: f32 = unsafe { vt.Anonymous.Anonymous.Anonymous.fltVal };
159
160                Variant::R4(num)
161            }
162            VT_R8 => {
163                let num: f64 = unsafe { vt.Anonymous.Anonymous.Anonymous.dblVal };
164
165                Variant::R8(num)
166            }
167            VT_BOOL => {
168                let value = unsafe { vt.Anonymous.Anonymous.Anonymous.boolVal };
169
170                match value {
171                    VARIANT_FALSE => Variant::Bool(false),
172                    VARIANT_TRUE => Variant::Bool(true),
173                    _ => return Err(WMIError::ConvertBoolError(value.0)),
174                }
175            }
176            VT_UI1 => {
177                let num: u8 = unsafe { vt.Anonymous.Anonymous.Anonymous.bVal };
178
179                Variant::UI1(num)
180            }
181            VT_UI2 => {
182                let num: u16 = unsafe { vt.Anonymous.Anonymous.Anonymous.uiVal };
183
184                Variant::UI2(num)
185            }
186            VT_UI4 => {
187                let num: u32 = unsafe { vt.Anonymous.Anonymous.Anonymous.ulVal };
188
189                Variant::UI4(num)
190            }
191            VT_UI8 => {
192                let num: u64 = unsafe { vt.Anonymous.Anonymous.Anonymous.ullVal };
193
194                Variant::UI8(num)
195            }
196            VT_EMPTY => Variant::Empty,
197            VT_NULL => Variant::Null,
198            VT_UNKNOWN => {
199                let ptr = unsafe { vt.Anonymous.Anonymous.Anonymous.punkVal.as_ref() };
200                let ptr = ptr.cloned().ok_or(WMIError::NullPointerResult)?;
201                Variant::Unknown(IUnknownWrapper::new(ptr))
202            }
203            _ => return Err(WMIError::ConvertError(variant_type.0)),
204        };
205
206        Ok(variant_value)
207    }
208
209    /// Convert the variant it to a specific type.
210    pub fn convert_into_cim_type(self, cim_type: CIMTYPE_ENUMERATION) -> WMIResult<Self> {
211        if cim_type == Wmi::CIM_EMPTY {
212            return Ok(Variant::Null);
213        }
214
215        if (Wmi::CIM_FLAG_ARRAY.0 & cim_type.0) != 0 {
216            /*
217            "If the type is actually an array type,
218            the CimBaseType MUST be combined by using the bitwise OR operation with the CimArrayFlag value (0x2000)
219            that results in the most significant octet containing 0x20
220            and the lower octet containing the value of the CimBaseType."
221            */
222            return match self {
223                // If we got an array, we just need to convert it's elements.
224                Variant::Array(arr) => Variant::Array(arr)
225                    .convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type.0 & 0xff)),
226                Variant::Empty | Variant::Null => Ok(Variant::Array(vec![])),
227                // If we didn't get an array, we need to convert the element, but also wrap it in an array.
228                not_array => {
229                    Ok(Variant::Array(vec![not_array.convert_into_cim_type(
230                        CIMTYPE_ENUMERATION(cim_type.0 & 0xff),
231                    )?]))
232                }
233            };
234        }
235
236        // The `convert_into_cim_type` function is used to convert a `Variant` into a CIM-type.
237        // we cannot use `try_into` because we need to support i8 to u8 conversion.
238        let converted_variant = match self {
239            Variant::Empty => Variant::Empty,
240            Variant::Null => Variant::Null,
241            Variant::I1(n) => cast_num!(n, cim_type)?,
242            Variant::I2(n) => cast_num!(n, cim_type)?,
243            Variant::I4(n) => cast_num!(n, cim_type)?,
244            Variant::I8(n) => cast_num!(n, cim_type)?,
245            Variant::R4(f) => cast_num!(f, cim_type)?,
246            Variant::R8(f) => cast_num!(f, cim_type)?,
247            Variant::UI1(n) => cast_num!(n, cim_type)?,
248            Variant::UI2(n) => cast_num!(n, cim_type)?,
249            Variant::UI4(n) => cast_num!(n, cim_type)?,
250            Variant::UI8(n) => cast_num!(n, cim_type)?,
251            Variant::Bool(b) => {
252                if cim_type == Wmi::CIM_BOOLEAN {
253                    Variant::Bool(b)
254                } else {
255                    return Err(WMIError::ConvertVariantError(format!(
256                        "A boolean Variant cannot be turned into a CIMTYPE {:?}",
257                        cim_type,
258                    )));
259                }
260            }
261            Variant::String(s) => {
262                match cim_type {
263                    Wmi::CIM_STRING | Wmi::CIM_CHAR16 => Variant::String(s),
264                    Wmi::CIM_REAL64 => Variant::R8(s.parse()?),
265                    Wmi::CIM_REAL32 => Variant::R4(s.parse()?),
266                    Wmi::CIM_UINT64 => Variant::UI8(s.parse()?),
267                    Wmi::CIM_SINT64 => Variant::I8(s.parse()?),
268                    Wmi::CIM_UINT32 => Variant::UI4(s.parse()?),
269                    Wmi::CIM_SINT32 => Variant::I4(s.parse()?),
270                    Wmi::CIM_UINT16 => Variant::UI2(s.parse()?),
271                    Wmi::CIM_SINT16 => Variant::I2(s.parse()?),
272                    Wmi::CIM_UINT8 => Variant::UI1(s.parse()?),
273                    Wmi::CIM_SINT8 => Variant::I1(s.parse()?),
274                    // Since Variant cannot natively represent a CIM_DATETIME or a CIM_REFERENCE (or any other), we keep it as a string.
275                    _ => Variant::String(s),
276                }
277            }
278            Variant::Array(variants) => {
279                let converted_variants = variants
280                    .into_iter()
281                    .map(|variant| variant.convert_into_cim_type(cim_type))
282                    .collect::<Result<Vec<_>, WMIError>>()?;
283
284                Variant::Array(converted_variants)
285            }
286            Variant::Unknown(u) => {
287                if cim_type == Wmi::CIM_OBJECT {
288                    Variant::Object(u.to_wbem_class_obj()?)
289                } else {
290                    return Err(WMIError::ConvertVariantError(format!(
291                        "A unknown Variant cannot be turned into a CIMTYPE {:?}",
292                        cim_type,
293                    )));
294                }
295            }
296            Variant::Object(o) => Variant::Object(o),
297        };
298
299        Ok(converted_variant)
300    }
301}
302
303impl TryFrom<Variant> for VARIANT {
304    type Error = WMIError;
305
306    fn try_from(value: Variant) -> WMIResult<VARIANT> {
307        // Some rules are special cased by https://learn.microsoft.com/en-us/windows/win32/wmisdk/numbers.
308        // For int64, we also must use decimal and not hexadecimal:
309        // https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nf-wbemcli-iwbemclassobject-put#examples.
310        match value {
311            Variant::Empty => Ok(VARIANT::default()),
312
313            Variant::String(string) => Ok(VARIANT::from(string.as_str())),
314
315            // sint8 uses VT_I2.
316            Variant::I1(int8) => Ok(VARIANT::from(int8 as i16)),
317            Variant::I2(int16) => Ok(VARIANT::from(int16)),
318            Variant::I4(int32) => Ok(VARIANT::from(int32)),
319
320            // Signed 64-bit integer in string form
321            Variant::I8(int64) => Ok(VARIANT::from(int64.to_string().as_str())),
322
323            Variant::R4(float32) => Ok(VARIANT::from(float32)),
324            Variant::R8(float64) => Ok(VARIANT::from(float64)),
325
326            Variant::Bool(b) => Ok(VARIANT::from(b)),
327
328            Variant::UI1(uint8) => Ok(VARIANT::from(uint8)),
329
330            // uint16 uses VT_I4.
331            Variant::UI2(uint16) => Ok(VARIANT::from(uint16 as i32)),
332            // uint32 uses VT_I4.
333            Variant::UI4(uint32) => Ok(VARIANT::from(uint32 as i32)),
334
335            // Signed 64-bit integer in string form.
336            Variant::UI8(uint64) => Ok(VARIANT::from(uint64.to_string().as_str())),
337
338            Variant::Object(instance) => Ok(VARIANT::from(IUnknown::from(instance.inner))),
339            Variant::Unknown(unknown) => Ok(VARIANT::from(unknown.inner)),
340
341            Variant::Null => {
342                let mut variant = VARIANT::default();
343                set_variant_type(&mut variant, VT_NULL);
344                Ok(variant)
345            }
346            Variant::Array(array) => {
347                // Variant arrays can only contain a single type, and we only support types that have utility functions in the `windows` crate.
348                match array.first() {
349                    // The "Empty" (default) variant is not a valid array.
350                    None => Ok(Variant::Null.try_into()?),
351                    Some(Variant::UI1(_)) => {
352                        let v: Vec<u8> = Variant::Array(array).try_into()?;
353
354                        //  "Creates a VT_ARRAY | VT_UI1 variant".
355                        let variant =
356                            unsafe { InitVariantFromBuffer(v.as_ptr() as _, v.len() as _) }?;
357                        Ok(variant)
358                    }
359                    Some(Variant::UI2(_)) => {
360                        let v: Vec<u16> = Variant::Array(array).try_into()?;
361
362                        // uint16 uses VT_I4.
363                        let v: Vec<i32> = v.into_iter().map(i32::from).collect();
364
365                        let variant = unsafe { InitVariantFromInt32Array(&v) }?;
366                        Ok(variant)
367                    }
368                    Some(Variant::UI4(_)) => {
369                        let v: Vec<u32> = Variant::Array(array).try_into()?;
370
371                        // uint32 uses VT_I4.
372                        let v: Vec<i32> = v.into_iter().map(|i| i as _).collect();
373
374                        let variant = unsafe { InitVariantFromInt32Array(&v) }?;
375                        Ok(variant)
376                    }
377                    Some(Variant::UI8(_)) => {
378                        let v: Vec<u64> = Variant::Array(array).try_into()?;
379
380                        // Unsigned 64-bit integer in string form.
381                        let v: Vec<String> = v.into_iter().map(|i| i.to_string()).collect();
382
383                        Ok(variant_from_string_array(&v)?)
384                    }
385                    Some(Variant::I1(_)) => {
386                        let v: Vec<i8> = Variant::Array(array).try_into()?;
387
388                        // sint8 uses VT_I2.
389                        let v: Vec<i16> = v.into_iter().map(i16::from).collect();
390
391                        let variant = unsafe { InitVariantFromInt16Array(&v) }?;
392                        Ok(variant)
393                    }
394                    Some(Variant::I2(_)) => {
395                        let v: Vec<i16> = Variant::Array(array).try_into()?;
396
397                        let variant = unsafe { InitVariantFromInt16Array(&v) }?;
398                        Ok(variant)
399                    }
400                    Some(Variant::I4(_)) => {
401                        let v: Vec<i32> = Variant::Array(array).try_into()?;
402
403                        let variant = unsafe { InitVariantFromInt32Array(&v) }?;
404                        Ok(variant)
405                    }
406                    Some(Variant::I8(_)) => {
407                        let v: Vec<i64> = Variant::Array(array).try_into()?;
408
409                        // Signed 64-bit integer in string form.
410                        let v: Vec<String> = v.into_iter().map(|i| i.to_string()).collect();
411
412                        Ok(variant_from_string_array(&v)?)
413                    }
414                    Some(Variant::R4(_)) => {
415                        let v: Vec<f32> = Variant::Array(array).try_into()?;
416
417                        let safe_arr =
418                            NonNull::new(unsafe { SafeArrayCreateVector(VT_R4, 0, v.len() as _) })
419                                .ok_or(WMIError::NullPointerResult)?;
420
421                        let mut accessor = unsafe { SafeArrayAccessor::new(safe_arr) }?;
422
423                        for (src, dst) in v.into_iter().zip(accessor.iter_mut()) {
424                            *dst = src;
425                        }
426
427                        drop(accessor);
428
429                        let mut variant = VARIANT::default();
430                        set_variant_type(&mut variant, VT_ARRAY | VT_R4);
431
432                        //  According to https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-variantclear:
433                        // "If the vt field has the VT_ARRAY bit set, the array is freed."
434                        // Therefore, we must not destroy the array ourselves, as the ownership is transferred to the variant.
435                        unsafe {
436                            (&mut variant.Anonymous.Anonymous).Anonymous.parray = safe_arr.as_ptr();
437                        }
438
439                        Ok(variant)
440                    }
441                    Some(Variant::R8(_)) => {
442                        let v: Vec<f64> = Variant::Array(array).try_into()?;
443
444                        let variant = unsafe { InitVariantFromDoubleArray(&v) }?;
445                        Ok(variant)
446                    }
447                    Some(Variant::Bool(_)) => {
448                        let v: Vec<bool> = Variant::Array(array).try_into()?;
449                        let v: Vec<_> = v.into_iter().map(Into::into).collect();
450
451                        let variant = unsafe { InitVariantFromBooleanArray(&v) }?;
452                        Ok(variant)
453                    }
454                    Some(Variant::String(_)) => {
455                        let v: Vec<String> = Variant::Array(array).try_into()?;
456
457                        Ok(variant_from_string_array(&v)?)
458                    }
459                    other => Err(WMIError::ConvertVariantError(format!(
460                        "Cannot convert {other:?} to a Windows VARIANT"
461                    ))),
462                }
463            }
464        }
465    }
466}
467
468macro_rules! impl_try_from_variant {
469    ($target_type:ty, $variant_type:ident) => {
470        impl TryFrom<Variant> for $target_type {
471            type Error = WMIError;
472
473            fn try_from(value: Variant) -> Result<$target_type, Self::Error> {
474                match value {
475                    Variant::$variant_type(item) => Ok(item),
476                    other => Err(WMIError::ConvertVariantError(format!(
477                        "Variant {:?} cannot be turned into a {}",
478                        &other,
479                        stringify!($target_type)
480                    ))),
481                }
482            }
483        }
484    };
485}
486
487/// Infallible conversion from a Rust type into a Variant wrapper for that type
488macro_rules! impl_wrap_type {
489    ($target_type:ty, $variant_type:ident) => {
490        impl From<$target_type> for Variant {
491            fn from(value: $target_type) -> Self {
492                Variant::$variant_type(value)
493            }
494        }
495    };
496}
497
498macro_rules! impl_try_vec_from_variant {
499    ($target_type:ty, $variant_type:ident) => {
500        impl TryFrom<Variant> for Vec<$target_type> {
501            type Error = WMIError;
502
503            fn try_from(value: Variant) -> Result<Vec<$target_type>, Self::Error> {
504                let array = match value {
505                    Variant::Array(array) => array,
506                    _ => {
507                        return Err(WMIError::ConvertVariantError(format!(
508                            "Cannot convert a non Variant::Array {:?} to Vec",
509                            value
510                        )));
511                    }
512                };
513
514                let mut output_vec = Vec::with_capacity(array.len());
515
516                for item in array {
517                    let item = item.try_into()?;
518                    output_vec.push(item);
519                }
520
521                Ok(output_vec)
522            }
523        }
524    };
525}
526
527/// Infallible conversion from a Rust type into a Variant wrapper for that type
528macro_rules! impl_wrap_vec_type {
529    ($target_type:ty, $variant_type:ident) => {
530        impl From<Vec<$target_type>> for Variant {
531            fn from(value: Vec<$target_type>) -> Self {
532                Variant::Array(value.into_iter().map(Variant::$variant_type).collect())
533            }
534        }
535    };
536}
537
538/// Add conversions from a Rust type to its Variant form and vice versa
539macro_rules! bidirectional_variant_convert {
540    ($target_type:ty, $variant_type:ident) => {
541        impl_try_from_variant!($target_type, $variant_type);
542        impl_try_vec_from_variant!($target_type, $variant_type);
543        impl_wrap_type!($target_type, $variant_type);
544        impl_wrap_vec_type!($target_type, $variant_type);
545    };
546}
547
548bidirectional_variant_convert!(String, String);
549bidirectional_variant_convert!(i8, I1);
550bidirectional_variant_convert!(i16, I2);
551bidirectional_variant_convert!(i32, I4);
552bidirectional_variant_convert!(i64, I8);
553bidirectional_variant_convert!(u8, UI1);
554bidirectional_variant_convert!(u16, UI2);
555bidirectional_variant_convert!(u32, UI4);
556bidirectional_variant_convert!(u64, UI8);
557bidirectional_variant_convert!(f32, R4);
558bidirectional_variant_convert!(f64, R8);
559bidirectional_variant_convert!(bool, Bool);
560bidirectional_variant_convert!(IWbemClassWrapper, Object);
561
562impl From<()> for Variant {
563    fn from(_value: ()) -> Self {
564        Variant::Empty
565    }
566}
567
568impl From<&str> for Variant {
569    fn from(value: &str) -> Self {
570        Variant::String(value.to_string())
571    }
572}
573
574impl TryFrom<Variant> for () {
575    type Error = WMIError;
576
577    fn try_from(value: Variant) -> Result<(), Self::Error> {
578        match value {
579            Variant::Empty => Ok(()),
580            other => Err(WMIError::ConvertVariantError(format!(
581                "Variant {:?} cannot be turned into a {}",
582                &other,
583                stringify!(())
584            ))),
585        }
586    }
587}
588
589/// A wrapper around the [`IUnknown`] interface. \
590/// Used to retrieve [`IWbemClassObject`][winapi::um::Wmi::IWbemClassObject]
591///
592#[repr(transparent)]
593#[derive(Debug, PartialEq, Eq, Clone)]
594pub struct IUnknownWrapper {
595    inner: IUnknown,
596}
597
598impl IUnknownWrapper {
599    /// Wraps a non-null pointer to IUnknown
600    ///
601    pub fn new(ptr: IUnknown) -> Self {
602        IUnknownWrapper { inner: ptr }
603    }
604
605    pub fn to_wbem_class_obj(&self) -> WMIResult<IWbemClassWrapper> {
606        Ok(IWbemClassWrapper {
607            inner: self.inner.cast::<IWbemClassObject>()?,
608        })
609    }
610}
611
612impl Serialize for IUnknownWrapper {
613    /// IUnknownWrapper serializaes to `()`, since it should have been converted into [Variant::Object]
614    ///
615    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
616    where
617        S: serde::Serializer,
618    {
619        serializer.serialize_unit()
620    }
621}
622
623#[cfg(test)]
624mod tests {
625    use windows::Win32::System::Wmi::{CIM_SINT8, CIM_SINT64, CIM_UINT16, CIM_UINT32, CIM_UINT64};
626
627    use super::*;
628
629    #[test]
630    fn it_convert_into_cim_type_sint8() {
631        let cim_type = Wmi::CIM_SINT8;
632        let variant = Variant::I1(1);
633        let converted = variant.convert_into_cim_type(cim_type).unwrap();
634        assert_eq!(converted, Variant::I1(1));
635
636        let variant = Variant::UI1(1);
637        let converted = variant.convert_into_cim_type(cim_type).unwrap();
638        assert_eq!(converted, Variant::I1(1));
639    }
640
641    #[test]
642    fn it_convert_into_cim_type_uint8() {
643        let cim_type = Wmi::CIM_UINT8;
644        let variant = Variant::UI1(1);
645        let converted = variant.convert_into_cim_type(cim_type).unwrap();
646        assert_eq!(converted, Variant::UI1(1));
647
648        let variant = Variant::I1(1);
649        let converted = variant.convert_into_cim_type(cim_type).unwrap();
650        assert_eq!(converted, Variant::UI1(1));
651    }
652
653    #[test]
654    fn it_convert_into_cim_type_sint16() {
655        let cim_type = Wmi::CIM_UINT16;
656        let variant = Variant::I2(1);
657        let converted = variant.convert_into_cim_type(cim_type).unwrap();
658        assert_eq!(converted, Variant::UI2(1));
659
660        let variant = Variant::UI2(1);
661        let converted = variant.convert_into_cim_type(cim_type).unwrap();
662        assert_eq!(converted, Variant::UI2(1));
663
664        let variant = Variant::I1(1);
665        let converted = variant.convert_into_cim_type(cim_type).unwrap();
666        assert_eq!(converted, Variant::UI2(1));
667    }
668
669    #[test]
670    fn it_convert_into_cim_type_uint32() {
671        let cim_type = Wmi::CIM_UINT32;
672        let variant = Variant::I8(1);
673        let converted = variant.convert_into_cim_type(cim_type).unwrap();
674        assert_eq!(converted, Variant::UI4(1));
675
676        let variant = Variant::UI8(1);
677        let converted = variant.convert_into_cim_type(cim_type).unwrap();
678        assert_eq!(converted, Variant::UI4(1));
679    }
680
681    #[test]
682    fn it_convert_into_cim_type_sint32() {
683        let cim_type = Wmi::CIM_SINT32;
684        let variant = Variant::I8(1);
685        let converted = variant.convert_into_cim_type(cim_type).unwrap();
686        assert_eq!(converted, Variant::I4(1));
687
688        let variant = Variant::UI8(1);
689        let converted = variant.convert_into_cim_type(cim_type).unwrap();
690        assert_eq!(converted, Variant::I4(1));
691
692        let variant = Variant::String("1".to_string());
693        let converted = variant.convert_into_cim_type(cim_type).unwrap();
694        assert_eq!(converted, Variant::I4(1));
695    }
696
697    #[test]
698    fn it_convert_into_cim_type_uint64() {
699        let cim_type = Wmi::CIM_UINT64;
700        let variant = Variant::I8(1);
701        let converted = variant.convert_into_cim_type(cim_type).unwrap();
702        assert_eq!(converted, Variant::UI8(1));
703
704        let variant = Variant::UI8(1);
705        let converted = variant.convert_into_cim_type(cim_type).unwrap();
706        assert_eq!(converted, Variant::UI8(1));
707
708        let variant = Variant::String("1".to_string());
709        let converted = variant.convert_into_cim_type(cim_type).unwrap();
710        assert_eq!(converted, Variant::UI8(1));
711    }
712
713    #[test]
714    fn it_convert_into_cim_type_sint64() {
715        let cim_type = Wmi::CIM_SINT64;
716        let variant = Variant::I8(1);
717        let converted = variant.convert_into_cim_type(cim_type).unwrap();
718        assert_eq!(converted, Variant::I8(1));
719
720        let variant = Variant::UI8(1);
721        let converted = variant.convert_into_cim_type(cim_type).unwrap();
722        assert_eq!(converted, Variant::I8(1));
723
724        let variant = Variant::String("1".to_string());
725        let converted = variant.convert_into_cim_type(cim_type).unwrap();
726        assert_eq!(converted, Variant::I8(1));
727    }
728
729    #[test]
730    fn it_convert_into_cim_type_real32() {
731        let cim_type = Wmi::CIM_REAL32;
732        let variant = Variant::I8(1);
733        let converted = variant.convert_into_cim_type(cim_type).unwrap();
734        assert_eq!(converted, Variant::R4(1.0));
735
736        let variant = Variant::UI8(1);
737        let converted = variant.convert_into_cim_type(cim_type).unwrap();
738        assert_eq!(converted, Variant::R4(1.0));
739
740        let variant = Variant::String("1".to_string());
741        let converted = variant.convert_into_cim_type(cim_type).unwrap();
742        assert_eq!(converted, Variant::R4(1.0));
743
744        let variant = Variant::String("1.0".to_string());
745        let converted = variant.convert_into_cim_type(cim_type).unwrap();
746        assert_eq!(converted, Variant::R4(1.0));
747    }
748
749    #[test]
750    fn it_convert_into_cim_type_real64() {
751        let cim_type = Wmi::CIM_REAL64;
752        let variant = Variant::I8(1);
753        let converted = variant.convert_into_cim_type(cim_type).unwrap();
754        assert_eq!(converted, Variant::R8(1.0));
755
756        let variant = Variant::UI8(1);
757        let converted = variant.convert_into_cim_type(cim_type).unwrap();
758        assert_eq!(converted, Variant::R8(1.0));
759
760        let variant = Variant::String("1".to_string());
761        let converted = variant.convert_into_cim_type(cim_type).unwrap();
762        assert_eq!(converted, Variant::R8(1.0));
763
764        let variant = Variant::String("1.0".to_string());
765        let converted = variant.convert_into_cim_type(cim_type).unwrap();
766        assert_eq!(converted, Variant::R8(1.0));
767    }
768
769    #[test]
770    fn it_convert_into_cim_char16() {
771        let cim_type = Wmi::CIM_CHAR16;
772        let variant = Variant::UI2(67);
773        let converted = variant.convert_into_cim_type(cim_type).unwrap();
774        assert_eq!(converted, Variant::String("C".to_string()));
775    }
776
777    #[test]
778    fn it_convert_into_cim_type_datetime() {
779        let cim_type = Wmi::CIM_DATETIME;
780        let datetime = "19980401135809.000000+000";
781        let variant = Variant::String(datetime.to_string());
782        let converted = variant.convert_into_cim_type(cim_type).unwrap();
783        assert_eq!(converted, Variant::String(datetime.to_string()));
784    }
785
786    #[test]
787    fn it_convert_into_cim_type_reference() {
788        let cim_type = Wmi::CIM_REFERENCE;
789        let datetime =
790            r#"\\\\PC\\root\\cimv2:Win32_DiskDrive.DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE0\""#;
791        let variant = Variant::String(datetime.to_string());
792        let converted = variant.convert_into_cim_type(cim_type).unwrap();
793        assert_eq!(converted, Variant::String(datetime.to_string()));
794    }
795
796    #[test]
797    fn it_convert_an_array_into_cim_type_array() {
798        let cim_type = CIMTYPE_ENUMERATION(Wmi::CIM_UINT64.0 | Wmi::CIM_FLAG_ARRAY.0);
799        let variant = Variant::Array(vec![Variant::String("1".to_string())]);
800        let converted = variant.convert_into_cim_type(cim_type).unwrap();
801        assert_eq!(converted, Variant::Array(vec![Variant::UI8(1)]));
802
803        let cim_type = CIMTYPE_ENUMERATION(Wmi::CIM_UINT8.0 | Wmi::CIM_FLAG_ARRAY.0);
804        let variant = Variant::Array(vec![Variant::UI1(1)]);
805        let converted = variant.convert_into_cim_type(cim_type).unwrap();
806        assert_eq!(converted, Variant::Array(vec![Variant::UI1(1)]));
807    }
808
809    #[test]
810    fn it_convert_a_single_value_into_cim_type_array() {
811        let cim_type = CIMTYPE_ENUMERATION(Wmi::CIM_UINT64.0 | Wmi::CIM_FLAG_ARRAY.0);
812        let variant = Variant::String("1".to_string());
813        let converted = variant.convert_into_cim_type(cim_type).unwrap();
814        assert_eq!(converted, Variant::Array(vec![Variant::UI8(1)]));
815
816        let cim_type = CIMTYPE_ENUMERATION(Wmi::CIM_UINT8.0 | Wmi::CIM_FLAG_ARRAY.0);
817        let variant = Variant::UI1(1);
818        let converted = variant.convert_into_cim_type(cim_type).unwrap();
819        assert_eq!(converted, Variant::Array(vec![Variant::UI1(1)]));
820    }
821
822    #[test]
823    fn it_convert_an_empty_into_cim_type_array() {
824        let cim_type = CIMTYPE_ENUMERATION(Wmi::CIM_STRING.0 | Wmi::CIM_FLAG_ARRAY.0);
825        let variant = Variant::Null;
826        let converted = variant.convert_into_cim_type(cim_type).unwrap();
827        assert_eq!(converted, Variant::Array(vec![]));
828
829        let variant = Variant::Empty;
830        let converted = variant.convert_into_cim_type(cim_type).unwrap();
831        assert_eq!(converted, Variant::Array(vec![]));
832    }
833
834    #[test]
835    fn it_bidirectional_string_convert() {
836        let string = "Test String".to_string();
837        let variant = Variant::from(string.clone());
838        assert_eq!(variant.try_into().ok(), Some(string.clone()));
839
840        let variant = Variant::from(string.clone());
841        let ms_variant = VARIANT::try_from(variant).unwrap();
842        let variant = Variant::from(string.clone());
843        assert_eq!(Variant::from_variant(&ms_variant).unwrap(), variant);
844    }
845
846    #[test]
847    fn it_bidirectional_empty_convert() {
848        let variant = Variant::from(());
849        assert_eq!(variant.try_into().ok(), Some(()));
850
851        let variant = Variant::from(());
852        let ms_variant = VARIANT::try_from(variant).unwrap();
853        let variant = Variant::from(());
854        assert_eq!(Variant::from_variant(&ms_variant).unwrap(), variant);
855    }
856
857    #[test]
858    fn it_bidirectional_r8_convert() {
859        let num = 0.123456789;
860        let variant = Variant::from(num);
861        assert_eq!(variant.try_into().ok(), Some(num));
862
863        let variant = Variant::from(num);
864        let ms_variant = VARIANT::try_from(variant).unwrap();
865        let variant = Variant::from(num);
866        assert_eq!(Variant::from_variant(&ms_variant).unwrap(), variant);
867    }
868
869    #[test]
870    fn it_convert_array_to_vec() {
871        let v: Vec<u8> = Variant::Array(vec![Variant::UI1(1), Variant::UI1(2)])
872            .try_into()
873            .unwrap();
874
875        assert_eq!(v, vec![1, 2]);
876
877        let _v: Vec<u16> = Variant::Array(vec![Variant::UI2(1)]).try_into().unwrap();
878        let _v: Vec<u32> = Variant::Array(vec![Variant::UI4(1)]).try_into().unwrap();
879        let _v: Vec<u64> = Variant::Array(vec![Variant::UI8(1)]).try_into().unwrap();
880
881        let _v: Vec<i8> = Variant::Array(vec![Variant::I1(1)]).try_into().unwrap();
882        let _v: Vec<i16> = Variant::Array(vec![Variant::I2(1)]).try_into().unwrap();
883        let _v: Vec<i32> = Variant::Array(vec![Variant::I4(1)]).try_into().unwrap();
884        let _v: Vec<i64> = Variant::Array(vec![Variant::I8(1)]).try_into().unwrap();
885
886        let _v: Vec<f32> = Variant::Array(vec![Variant::R4(1.)]).try_into().unwrap();
887        let _v: Vec<f64> = Variant::Array(vec![Variant::R8(1.)]).try_into().unwrap();
888
889        let _v: Vec<String> = Variant::Array(vec![Variant::String("s".to_string())])
890            .try_into()
891            .unwrap();
892        let _v: Vec<bool> = Variant::Array(vec![Variant::Bool(true)])
893            .try_into()
894            .unwrap();
895    }
896
897    #[test]
898    fn it_convert_array_to_ms_variant() {
899        let variant = Variant::Array(vec![Variant::UI1(1), Variant::UI1(2)]);
900        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
901        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
902
903        assert_eq!(variant, converted_back_variant);
904
905        let variant = Variant::Array(vec![Variant::UI2(1), Variant::UI2(2)]);
906        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
907        let converted_back_variant = Variant::from_variant(&ms_variant)
908            .unwrap()
909            .convert_into_cim_type(CIM_UINT16)
910            .unwrap();
911
912        assert_eq!(variant, converted_back_variant);
913
914        let variant = Variant::Array(vec![Variant::UI4(1), Variant::UI4(2)]);
915        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
916        let converted_back_variant = Variant::from_variant(&ms_variant)
917            .unwrap()
918            .convert_into_cim_type(CIM_UINT32)
919            .unwrap();
920
921        assert_eq!(variant, converted_back_variant);
922
923        let variant = Variant::Array(vec![Variant::UI8(1), Variant::UI8(2)]);
924        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
925        let converted_back_variant = Variant::from_variant(&ms_variant)
926            .unwrap()
927            .convert_into_cim_type(CIM_UINT64)
928            .unwrap();
929
930        assert_eq!(variant, converted_back_variant);
931
932        let variant = Variant::Array(vec![Variant::I2(1), Variant::I2(2)]);
933        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
934        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
935
936        assert_eq!(variant, converted_back_variant);
937
938        let variant = Variant::Array(vec![Variant::I4(1), Variant::I4(2)]);
939        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
940        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
941
942        assert_eq!(variant, converted_back_variant);
943
944        let variant = Variant::Array(vec![Variant::I8(1), Variant::I8(2)]);
945        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
946        let converted_back_variant = Variant::from_variant(&ms_variant)
947            .unwrap()
948            .convert_into_cim_type(CIM_SINT64)
949            .unwrap();
950
951        assert_eq!(variant, converted_back_variant);
952
953        let variant = Variant::Array(vec![Variant::R8(1.), Variant::R8(2.)]);
954        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
955        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
956
957        assert_eq!(variant, converted_back_variant);
958
959        let variant = Variant::Array(vec![Variant::Bool(true), Variant::Bool(false)]);
960        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
961        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
962
963        assert_eq!(variant, converted_back_variant);
964
965        let variant = Variant::Array(vec![
966            Variant::String("a".to_string()),
967            Variant::String("b".to_string()),
968        ]);
969        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
970        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
971
972        assert_eq!(variant, converted_back_variant);
973
974        // Empty arrays are converted to empty variants.
975        let variant = Variant::Array(vec![]);
976        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
977        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
978
979        assert_eq!(converted_back_variant, Variant::Null);
980
981        let variant = Variant::Array(vec![Variant::I1(0), Variant::I1(1)]);
982        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
983        let converted_back_variant = Variant::from_variant(&ms_variant)
984            .unwrap()
985            .convert_into_cim_type(CIM_SINT8)
986            .unwrap();
987        assert_eq!(variant, converted_back_variant);
988
989        let variant = Variant::Array(vec![Variant::R4(0.), Variant::R4(1.)]);
990        let ms_variant = VARIANT::try_from(variant.clone()).unwrap();
991        let converted_back_variant = Variant::from_variant(&ms_variant).unwrap();
992        assert_eq!(variant, converted_back_variant);
993    }
994
995    #[test]
996    fn it_does_not_convert_array_to_unsupported_ms_variant() {
997        let variant = Variant::Array(vec![Variant::String("a".to_string()), Variant::I8(0)]);
998        assert!(
999            VARIANT::try_from(variant.clone()).is_err(),
1000            "Mixed arrays are not supported"
1001        );
1002    }
1003}