Skip to main content

wmi/ser/
variant_ser.rs

1//! This module implements a custom serializer type, [`VariantStructSerializer`],
2//! to serialize a Rust struct into a HashMap mapping field name strings to [`Variant`] values
3use std::{any::type_name, fmt::Display};
4
5use crate::{Variant, WMIConnection, WMIError, result_enumerator::IWbemClassWrapper};
6use serde::{
7    Serialize, Serializer,
8    ser::{Impossible, SerializeSeq, SerializeStruct},
9};
10use thiserror::Error;
11
12macro_rules! serialize_variant_err_stub {
13    ($signature:ident, $type:ty) => {
14        fn $signature(self, _v: $type) -> Result<Self::Ok, Self::Error> {
15            Err(VariantSerializerError::UnsupportedVariantType(
16                type_name::<$type>().to_string(),
17            ))
18        }
19    };
20}
21
22macro_rules! serialize_variant {
23    ($signature:ident, $type:ty) => {
24        fn $signature(self, v: $type) -> Result<Self::Ok, Self::Error> {
25            Ok(Variant::from(v))
26        }
27    };
28}
29
30pub(crate) struct VariantSerializer<'a> {
31    pub(crate) wmi: &'a WMIConnection,
32    pub(crate) instance: Option<IWbemClassWrapper>,
33}
34
35impl<'a> Serializer for VariantSerializer<'a> {
36    type Ok = Variant;
37    type Error = VariantSerializerError;
38
39    type SerializeSeq = VariantSeqSerializer<'a>;
40    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
41    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
42    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
43    type SerializeMap = Impossible<Self::Ok, Self::Error>;
44    type SerializeStruct = VariantInstanceSerializer<'a>;
45    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
46
47    serialize_variant!(serialize_bool, bool);
48    serialize_variant!(serialize_i8, i8);
49    serialize_variant!(serialize_i16, i16);
50    serialize_variant!(serialize_i32, i32);
51    serialize_variant!(serialize_i64, i64);
52    serialize_variant!(serialize_u8, u8);
53    serialize_variant!(serialize_u16, u16);
54    serialize_variant!(serialize_u32, u32);
55    serialize_variant!(serialize_u64, u64);
56    serialize_variant!(serialize_f32, f32);
57    serialize_variant!(serialize_f64, f64);
58
59    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
60        // When starting from an instance, deserializing a unit means returning the original instance unmodified.
61        Ok(self.instance.map(Variant::from).unwrap_or(Variant::Empty))
62    }
63
64    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
65        Ok(Variant::from(v.to_string()))
66    }
67
68    fn serialize_newtype_variant<T>(
69        self,
70        name: &'static str,
71        _variant_index: u32,
72        variant: &'static str,
73        _value: &T,
74    ) -> Result<Self::Ok, Self::Error>
75    where
76        T: ?Sized + Serialize,
77    {
78        Err(VariantSerializerError::UnsupportedVariantType(format!(
79            "{variant}::{name}"
80        )))
81    }
82
83    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
84        let ser = self.serialize_struct(name, 0)?;
85
86        ser.end()
87    }
88
89    fn serialize_newtype_struct<T>(
90        self,
91        _name: &'static str,
92        value: &T,
93    ) -> Result<Self::Ok, Self::Error>
94    where
95        T: ?Sized + Serialize,
96    {
97        value.serialize(self)
98    }
99
100    fn serialize_unit_variant(
101        self,
102        _name: &'static str,
103        _variant_index: u32,
104        variant: &'static str,
105    ) -> Result<Self::Ok, Self::Error> {
106        Ok(Variant::from(variant.to_string()))
107    }
108
109    // Generic serializer code not relevant to this use case
110
111    serialize_variant_err_stub!(serialize_char, char);
112    serialize_variant_err_stub!(serialize_bytes, &[u8]);
113
114    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
115        // we serialize to VT_NULL (explicit NULL semantic)  rather than VT_EMPTY
116        // (default state or uninitialized semantic)
117        Ok(Variant::Null)
118    }
119
120    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
121    where
122        T: ?Sized + Serialize,
123    {
124        value.serialize(self)
125    }
126
127    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
128        Ok(VariantSeqSerializer {
129            seq: Vec::with_capacity(len.unwrap_or_default()),
130            wmi: self.wmi,
131        })
132    }
133
134    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
135        Err(VariantSerializerError::UnsupportedVariantType(
136            "Tuple".to_string(),
137        ))
138    }
139
140    fn serialize_tuple_struct(
141        self,
142        name: &'static str,
143        _len: usize,
144    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
145        Err(VariantSerializerError::UnsupportedVariantType(
146            name.to_string(),
147        ))
148    }
149
150    fn serialize_tuple_variant(
151        self,
152        name: &'static str,
153        _variant_index: u32,
154        variant: &'static str,
155        _len: usize,
156    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
157        Err(VariantSerializerError::UnsupportedVariantType(format!(
158            "{variant}::{name}"
159        )))
160    }
161
162    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
163        Err(VariantSerializerError::UnsupportedVariantType(
164            "Map".to_string(),
165        ))
166    }
167
168    fn serialize_struct(
169        self,
170        name: &'static str,
171        _len: usize,
172    ) -> Result<Self::SerializeStruct, Self::Error> {
173        // We are only given an initialized instance when called from `exec_method`,
174        // with the instance matching the method signature class.
175        // Otherwise, we use the name of the struct to create. See test for  `Win32_Process` with "Create" and `Win32_ProcessStartup`.
176        let instance = match self.instance {
177            Some(instance) => instance,
178            None => self.wmi.get_object(name)?.spawn_instance()?,
179        };
180
181        let ser = VariantInstanceSerializer {
182            wmi: self.wmi,
183            instance,
184        };
185
186        Ok(ser)
187    }
188
189    fn serialize_struct_variant(
190        self,
191        name: &'static str,
192        _variant_index: u32,
193        variant: &'static str,
194        _len: usize,
195    ) -> Result<Self::SerializeStructVariant, Self::Error> {
196        Err(VariantSerializerError::UnsupportedVariantType(format!(
197            "{variant}::{name}"
198        )))
199    }
200}
201
202#[derive(Debug, Error)]
203pub enum VariantSerializerError {
204    #[error("Unknown error while serializing struct:\n{0}")]
205    Unknown(String),
206    #[error("{0} cannot be serialized to a Variant.")]
207    UnsupportedVariantType(String),
208    #[error("WMI error while serializing struct: \n {0}")]
209    WMIError(#[from] WMIError),
210}
211
212impl serde::ser::Error for VariantSerializerError {
213    fn custom<T>(msg: T) -> Self
214    where
215        T: Display,
216    {
217        VariantSerializerError::Unknown(msg.to_string())
218    }
219}
220
221pub(crate) struct VariantInstanceSerializer<'a> {
222    instance: IWbemClassWrapper,
223    wmi: &'a WMIConnection,
224}
225
226impl<'a> SerializeStruct for VariantInstanceSerializer<'a> {
227    type Ok = Variant;
228
229    type Error = VariantSerializerError;
230
231    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
232    where
233        T: ?Sized + Serialize,
234    {
235        let variant = value.serialize(VariantSerializer {
236            wmi: self.wmi,
237            instance: None,
238        })?;
239
240        self.instance.put_property(key, variant)?;
241
242        Ok(())
243    }
244
245    fn end(self) -> Result<Self::Ok, Self::Error> {
246        Ok(Variant::Object(self.instance))
247    }
248}
249
250pub(crate) struct VariantSeqSerializer<'a> {
251    seq: Vec<Variant>,
252    wmi: &'a WMIConnection,
253}
254
255impl<'a> SerializeSeq for VariantSeqSerializer<'a> {
256    type Ok = Variant;
257    type Error = VariantSerializerError;
258
259    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
260    where
261        T: ?Sized + Serialize,
262    {
263        let variant = value.serialize(VariantSerializer {
264            wmi: self.wmi,
265            instance: None,
266        })?;
267
268        self.seq.push(variant);
269
270        Ok(())
271    }
272
273    fn end(self) -> Result<Self::Ok, Self::Error> {
274        Ok(Variant::Array(self.seq))
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use crate::tests::fixtures::wmi_con;
282    use serde::Serialize;
283    use std::ptr;
284    use windows::Win32::System::Wmi::{CIM_FLAG_ARRAY, CIM_SINT64, CIM_UINT64};
285    use windows::core::HSTRING;
286
287    #[test]
288    fn it_serialize_instance() {
289        let wmi_con = wmi_con();
290
291        #[derive(Serialize)]
292        struct GetBinaryValue {
293            sSubKeyName: String,
294            sValueName: String,
295        }
296
297        let in_params = GetBinaryValue {
298            sSubKeyName: r#"SYSTEM\CurrentControlSet\Control\Windows"#.to_string(),
299            sValueName: "FullProcessInformationSID".to_string(),
300        };
301
302        // Similar to how `exec_class_method` creates these objects.
303        let method_instance = wmi_con
304            .get_object("StdRegProv")
305            .unwrap()
306            .get_method("GetBinaryValue")
307            .unwrap()
308            .unwrap()
309            .spawn_instance()
310            .unwrap();
311
312        let instance_from_ser = in_params
313            .serialize(VariantSerializer {
314                wmi: &wmi_con,
315                instance: Some(method_instance),
316            })
317            .unwrap();
318
319        let instance_from_ser = match instance_from_ser {
320            Variant::Object(instance_from_ser) => instance_from_ser,
321            _ => panic!("Unexpected value {:?}", instance_from_ser),
322        };
323
324        let expected_instance = wmi_con
325            .get_object("StdRegProv")
326            .unwrap()
327            .get_method("GetBinaryValue")
328            .unwrap()
329            .unwrap()
330            .spawn_instance()
331            .unwrap();
332
333        assert_eq!(
334            instance_from_ser.class().unwrap(),
335            expected_instance.class().unwrap()
336        );
337
338        assert_eq!(
339            instance_from_ser.get_property("sSubKeyName").unwrap(),
340            Variant::String(in_params.sSubKeyName)
341        );
342    }
343
344    fn spawn_instance(name: &str) -> IWbemClassWrapper {
345        let wmi_con = wmi_con();
346        wmi_con.get_object(&name).unwrap().spawn_instance().unwrap()
347    }
348
349    #[test]
350    fn it_can_get_and_put_strings() {
351        let prop = spawn_instance("Win32_PnPDevicePropertyString");
352        let test_value = Variant::String("Some Title".to_string());
353
354        prop.put_property("Data", test_value.clone()).unwrap();
355        assert_eq!(prop.get_property("Data").unwrap(), test_value,);
356
357        let prop = spawn_instance("Win32_PnPDevicePropertyStringArray");
358        let test_value = Variant::Array(vec![
359            Variant::String("X".to_string()),
360            Variant::String("a".to_string()),
361        ]);
362        prop.put_property("Data", test_value.clone()).unwrap();
363        assert_eq!(prop.get_property("Data").unwrap(), test_value,);
364    }
365
366    #[test]
367    fn it_can_get_and_put_numbers_and_bool() {
368        let prop = spawn_instance("Win32_PnPDevicePropertyBoolean");
369        prop.put_property("Data", true).unwrap();
370        assert_eq!(prop.get_property("Data").unwrap(), Variant::Bool(true));
371
372        let prop = spawn_instance("Win32_PnPDevicePropertyUint8");
373        prop.put_property("Data", u8::MAX).unwrap();
374        assert_eq!(prop.get_property("Data").unwrap(), Variant::UI1(u8::MAX));
375
376        let prop = spawn_instance("Win32_PnPDevicePropertyUint16");
377        prop.put_property("Data", u16::MAX).unwrap();
378        assert_eq!(prop.get_property("Data").unwrap(), Variant::UI2(u16::MAX));
379
380        let prop = spawn_instance("Win32_PnPDevicePropertyUint32");
381        prop.put_property("Data", u32::MAX).unwrap();
382        assert_eq!(prop.get_property("Data").unwrap(), Variant::UI4(u32::MAX));
383
384        let prop = spawn_instance("Win32_PnPDevicePropertyUint64");
385        prop.put_property("Data", u64::MAX).unwrap();
386        assert_eq!(prop.get_property("Data").unwrap(), Variant::UI8(u64::MAX));
387
388        let prop = spawn_instance("Win32_PnPDevicePropertySint8");
389        prop.put_property("Data", i8::MAX).unwrap();
390        assert_eq!(prop.get_property("Data").unwrap(), Variant::I1(i8::MAX));
391        prop.put_property("Data", i8::MIN).unwrap();
392        assert_eq!(prop.get_property("Data").unwrap(), Variant::I1(i8::MIN));
393
394        let prop = spawn_instance("Win32_PnPDevicePropertySint16");
395        prop.put_property("Data", i16::MAX).unwrap();
396        assert_eq!(prop.get_property("Data").unwrap(), Variant::I2(i16::MAX));
397        prop.put_property("Data", i16::MIN).unwrap();
398        assert_eq!(prop.get_property("Data").unwrap(), Variant::I2(i16::MIN));
399
400        let prop = spawn_instance("Win32_PnPDevicePropertySint32");
401        prop.put_property("Data", i32::MAX).unwrap();
402        assert_eq!(prop.get_property("Data").unwrap(), Variant::I4(i32::MAX));
403        prop.put_property("Data", i32::MIN).unwrap();
404        assert_eq!(prop.get_property("Data").unwrap(), Variant::I4(i32::MIN));
405
406        let prop = spawn_instance("Win32_PnPDevicePropertySint64");
407        prop.put_property("Data", i64::MAX).unwrap();
408        assert_eq!(prop.get_property("Data").unwrap(), Variant::I8(i64::MAX));
409        prop.put_property("Data", i64::MIN).unwrap();
410        assert_eq!(prop.get_property("Data").unwrap(), Variant::I8(i64::MIN));
411
412        let prop = spawn_instance("Win32_PnPDevicePropertyReal32");
413        prop.put_property("Data", 1.0f32).unwrap();
414        assert_eq!(prop.get_property("Data").unwrap(), Variant::R4(1.0));
415
416        let prop = spawn_instance("Win32_PnPDevicePropertyReal64");
417        prop.put_property("Data", 1.0f64).unwrap();
418        assert_eq!(prop.get_property("Data").unwrap(), Variant::R8(1.0));
419    }
420
421    #[test]
422    fn it_can_get_and_put_arrays() {
423        let prop = spawn_instance("Win32_PnPDevicePropertyBooleanArray");
424        let test_value = vec![true, false, true];
425        prop.put_property("Data", test_value.clone()).unwrap();
426        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
427
428        // Test with an empty array as well.
429        let prop = spawn_instance("Win32_PnPDevicePropertyBooleanArray");
430        let test_value = Variant::Array(vec![]);
431        prop.put_property("Data", test_value.clone()).unwrap();
432        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
433
434        let prop = spawn_instance("Win32_PnPDevicePropertyBinary");
435        let test_value = vec![1u8, 2, u8::MAX];
436        prop.put_property("Data", test_value.clone()).unwrap();
437        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
438
439        let prop = spawn_instance("Win32_PnPDevicePropertyUint16Array");
440        let test_value = vec![1u16, 2, u16::MAX];
441        prop.put_property("Data", test_value.clone()).unwrap();
442        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
443
444        let prop = spawn_instance("Win32_PnPDevicePropertyUint32Array");
445        let test_value = vec![1u32, 2, u32::MAX];
446        prop.put_property("Data", test_value.clone()).unwrap();
447        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
448
449        let prop = spawn_instance("Win32_PnPDevicePropertySint8Array");
450        let test_value = vec![1i8, i8::MIN, i8::MAX];
451        prop.put_property("Data", test_value.clone()).unwrap();
452        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
453
454        let prop = spawn_instance("Win32_PnPDevicePropertySint16Array");
455        let test_value = vec![1i16, i16::MIN, i16::MAX];
456        prop.put_property("Data", test_value.clone()).unwrap();
457        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
458
459        let prop = spawn_instance("Win32_PnPDevicePropertySint32Array");
460        let test_value = vec![1i32, i32::MIN, i32::MAX];
461        prop.put_property("Data", test_value.clone()).unwrap();
462        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
463
464        let prop = spawn_instance("Win32_PnPDevicePropertyReal32Array");
465        let test_value = vec![1.0f32, 2.0, -1.0];
466        prop.put_property("Data", test_value.clone()).unwrap();
467        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
468
469        let prop = spawn_instance("Win32_PnPDevicePropertyReal64Array");
470        let test_value = vec![1.0f64, 2.0, -1.0];
471        prop.put_property("Data", test_value.clone()).unwrap();
472        assert_eq!(prop.get_property("Data").unwrap(), test_value.into());
473    }
474
475    #[test]
476    fn it_can_get_and_put_u64_i64_arrays() {
477        // Since `Win32_PnPDeviceProperty{Uint64,Sint64}Array` are missing (documented, but do not exist in practice),
478        // we create a new class and set custom properties with the needed array types.
479
480        let wmi_con = wmi_con();
481        let new_cls_obj = wmi_con.get_object("").unwrap();
482
483        unsafe {
484            new_cls_obj
485                .inner
486                .Put(
487                    &HSTRING::from("uValue"),
488                    0,
489                    ptr::null(),
490                    CIM_UINT64.0 | CIM_FLAG_ARRAY.0,
491                )
492                .unwrap()
493        };
494
495        let test_value = vec![1u64, 2, u64::MAX];
496        new_cls_obj
497            .put_property("uValue", test_value.clone())
498            .unwrap();
499        assert_eq!(
500            new_cls_obj.get_property("uValue").unwrap(),
501            test_value.into()
502        );
503
504        unsafe {
505            new_cls_obj
506                .inner
507                .Put(
508                    &HSTRING::from("iValue"),
509                    0,
510                    ptr::null(),
511                    CIM_SINT64.0 | CIM_FLAG_ARRAY.0,
512                )
513                .unwrap()
514        };
515
516        let test_value = vec![1i64, i64::MIN, i64::MAX];
517        new_cls_obj
518            .put_property("iValue", test_value.clone())
519            .unwrap();
520        assert_eq!(
521            new_cls_obj.get_property("iValue").unwrap(),
522            test_value.into()
523        );
524    }
525
526    #[test]
527    fn it_serialize_instance_nested() {
528        let wmi_con = wmi_con();
529
530        #[derive(Debug, Serialize, Default)]
531        pub struct Win32_ProcessStartup {
532            pub Title: String,
533            pub ShowWindow: Option<u16>,
534            pub CreateFlags: Option<u32>,
535        }
536
537        #[derive(Serialize)]
538        struct CreateInput {
539            CommandLine: String,
540            ProcessStartupInformation: Win32_ProcessStartup,
541        }
542
543        // Verify that `Win32_ProcessStartup` can be serialized.
544        let startup_info = Win32_ProcessStartup {
545            Title: "Pong".to_string(),
546            ShowWindow: Some(3),
547            CreateFlags: None,
548        };
549
550        let startup_info_instance = startup_info
551            .serialize(VariantSerializer {
552                wmi: &wmi_con,
553                instance: None,
554            })
555            .unwrap();
556
557        let startup_info_instance = match startup_info_instance {
558            Variant::Object(startup_info_instance) => startup_info_instance,
559            _ => panic!("Unexpected value {:?}", startup_info_instance),
560        };
561
562        assert_eq!(
563            startup_info_instance.class().unwrap(),
564            "Win32_ProcessStartup"
565        );
566        assert_eq!(
567            startup_info_instance.get_property("Title").unwrap(),
568            Variant::String(startup_info.Title.clone())
569        );
570
571        assert_eq!(
572            startup_info_instance.get_property("ShowWindow").unwrap(),
573            Variant::UI2(3)
574        );
575        assert_eq!(
576            startup_info_instance.get_property("CreateFlags").unwrap(),
577            Variant::Null
578        );
579
580        let create_params = CreateInput {
581            CommandLine: r#"ping -n 3 127.0.0.1"#.to_string(),
582            ProcessStartupInformation: startup_info,
583        };
584
585        // Similar to how `exec_class_method` creates these objects.
586        let (method_in, method_out) = wmi_con
587            .get_object("Win32_Process")
588            .unwrap()
589            .get_method_in_out("Create")
590            .unwrap();
591
592        let method_in = method_in.unwrap().spawn_instance().unwrap();
593        let method_out = method_out.unwrap().spawn_instance().unwrap();
594
595        let instance_from_ser = create_params
596            .serialize(VariantSerializer {
597                wmi: &wmi_con,
598                instance: Some(method_in),
599            })
600            .unwrap();
601
602        let instance_from_ser = match instance_from_ser {
603            Variant::Object(instance_from_ser) => instance_from_ser,
604            _ => panic!("Unexpected value {:?}", instance_from_ser),
605        };
606
607        assert_eq!(
608            instance_from_ser.get_property("CommandLine").unwrap(),
609            Variant::String(create_params.CommandLine)
610        );
611
612        assert!(matches!(
613            instance_from_ser
614                .get_property("ProcessStartupInformation")
615                .unwrap(),
616            Variant::Object(_)
617        ));
618
619        assert_eq!(
620            method_out.get_property("ReturnValue").unwrap(),
621            Variant::Null
622        );
623    }
624}