use failure::format_err;
use serde::de::{
self, DeserializeOwned, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, Unexpected,
VariantAccess, Visitor,
};
use serde::forward_to_deserialize_any;
use std::{iter::Peekable, ptr};
use winapi::um::oleauto::VariantClear;
use crate::error::Error;
use crate::result_enumerator::IWbemClassWrapper;
pub struct Deserializer<'a> {
pub wbem_class_obj: &'a IWbemClassWrapper,
}
impl<'a> Deserializer<'a> {
pub fn from_wbem_class_obj(wbem_class_obj: &'a IWbemClassWrapper) -> Self {
Deserializer { wbem_class_obj }
}
}
pub fn from_wbem_class_obj<T>(wbem_class_obj: &IWbemClassWrapper) -> Result<T, Error>
where
T: DeserializeOwned,
{
let mut deserializer = Deserializer::from_wbem_class_obj(wbem_class_obj);
let t = T::deserialize(&mut deserializer)?;
Ok(t)
}
struct WMIEnum<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
}
impl<'a, 'de> WMIEnum<'a, 'de> {
pub fn new(de: &'a mut Deserializer<'de>) -> Self {
Self { de }
}
}
impl<'de, 'a> EnumAccess<'de> for WMIEnum<'a, 'de> {
type Error = Error;
type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>,
{
let val = seed.deserialize(&mut *self.de)?;
Ok((val, self))
}
}
impl<'de, 'a> VariantAccess<'de> for WMIEnum<'a, 'de> {
type Error = Error;
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
seed.deserialize(self.de)
}
fn unit_variant(self) -> Result<(), Self::Error> {
let unexp = Unexpected::UnitVariant;
Err(de::Error::invalid_type(unexp, &"newtype variant"))
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let unexp = Unexpected::TupleVariant;
Err(de::Error::invalid_type(unexp, &"newtype variant"))
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let unexp = Unexpected::StructVariant;
Err(de::Error::invalid_type(unexp, &"newtype variant"))
}
}
struct WMIMapAccess<'a, 'de, S, I>
where
S: AsRef<str>,
I: Iterator<Item = S>,
{
fields: Peekable<I>,
de: &'a Deserializer<'de>,
}
impl<'a, 'de, S, I> WMIMapAccess<'a, 'de, S, I>
where
S: AsRef<str>,
I: Iterator<Item = S>,
{
pub fn new(fields: I, de: &'a Deserializer<'de>) -> Self {
Self {
fields: fields.peekable(),
de,
}
}
}
impl<'de, 'a, S, I> MapAccess<'de> for WMIMapAccess<'a, 'de, S, I>
where
S: AsRef<str>,
I: Iterator<Item = S>,
{
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'de>,
{
if let Some(field) = self.fields.peek() {
seed.deserialize(field.as_ref().into_deserializer())
.map(Some)
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'de>,
{
let current_field = self
.fields
.next()
.ok_or(format_err!("Expected current field to not be None"))?;
let property_value = self
.de
.wbem_class_obj
.get_property(current_field.as_ref())
.map_err(Error::from_err)?;
seed.deserialize(property_value)
}
}
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(Error::from_err(format_err!(
"Only structs and maps can be deserialized from WMI objects"
)))
}
fn deserialize_enum<V>(
mut self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_enum(WMIEnum::new(&mut self))
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let class_name = self.wbem_class_obj.class()?;
visitor.visit_string(class_name)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let fields = self.wbem_class_obj.list_properties()?;
visitor.visit_map(WMIMapAccess::new(fields.iter(), &self))
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(WMIMapAccess::new(fields.iter(), &self))
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf option unit unit_struct seq tuple
tuple_struct ignored_any
}
}
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[cfg(test)]
mod tests {
use super::*;
use crate::datetime::WMIDateTime;
use crate::variant::Variant;
use serde::Deserialize;
use std::collections::HashMap;
use crate::tests::fixtures::*;
#[test]
fn it_works() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
struct Win32_OperatingSystem {
Caption: String,
Name: String,
CurrentTimeZone: i16,
Debug: bool,
EncryptionLevel: u32,
ForegroundApplicationBoost: u8,
LastBootUpTime: WMIDateTime,
}
let enumerator = wmi_con
.exec_query_native_wrapper("SELECT * FROM Win32_OperatingSystem")
.unwrap();
for res in enumerator {
let w = res.unwrap();
let w: Win32_OperatingSystem = from_wbem_class_obj(&w).unwrap();
assert_eq!(w.Caption, "Microsoft Windows 10 Pro");
assert_eq!(
w.Name,
"Microsoft Windows 10 Pro|C:\\WINDOWS|\\Device\\Harddisk0\\Partition3"
);
assert_eq!(w.CurrentTimeZone, 120);
assert_eq!(w.Debug, false);
assert_eq!(w.EncryptionLevel, 256);
assert_eq!(w.ForegroundApplicationBoost, 2);
assert_eq!(
w.LastBootUpTime.0.timezone().local_minus_utc() / 60,
w.CurrentTimeZone as i32
);
}
}
#[test]
fn it_desr_into_map() {
let wmi_con = wmi_con();
let enumerator = wmi_con
.exec_query_native_wrapper("SELECT * FROM Win32_OperatingSystem")
.unwrap();
for res in enumerator {
let w = res.unwrap();
let w: HashMap<String, Variant> = from_wbem_class_obj(&w).unwrap();
assert_eq!(
*w.get("Caption").unwrap(),
Variant::String("Microsoft Windows 10 Pro".into())
);
assert_eq!(*w.get("Debug").unwrap(), Variant::Bool(false));
assert_eq!(
*w.get("MUILanguages").unwrap(),
Variant::Array(vec![Variant::String("en-US".into())])
);
}
}
#[test]
fn it_desr_into_map_with_selected_fields() {
let wmi_con = wmi_con();
let enumerator = wmi_con
.exec_query_native_wrapper("SELECT Caption FROM Win32_OperatingSystem")
.unwrap();
for res in enumerator {
let w = res.unwrap();
let w: HashMap<String, Variant> = from_wbem_class_obj(&w).unwrap();
assert_eq!(
*w.get("Caption").unwrap(),
Variant::String("Microsoft Windows 10 Pro".into())
);
assert_eq!(w.get("Debug"), None);
}
}
#[test]
fn it_desr_array() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
struct Win32_ComputerSystem {
BootStatus: Vec<i32>,
Roles: Vec<String>,
}
let results: Vec<Win32_ComputerSystem> = wmi_con.query().unwrap();
for res in results {
assert_eq!(res.BootStatus, [0, 0, 0, 33, 31, 158, 0, 3, 2, 2]);
assert_eq!(res.Roles, ["LM_Workstation", "LM_Server", "NT"]);
}
}
#[test]
fn it_desr_option_string() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
pub struct Win32_Service {
pub Name: String,
pub PathName: Option<String>,
}
let results: Vec<Win32_Service> = wmi_con.query().unwrap();
let lsm_service = results
.iter()
.find(|&service| service.Name == "LSM")
.unwrap();
assert_eq!(lsm_service.PathName, None);
let lmhosts_service = results
.iter()
.find(|&service| service.Name == "lmhosts")
.unwrap();
assert!(lmhosts_service.PathName.is_some());
}
#[test]
fn it_fail_to_desr_null_to_string() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
pub struct Win32_Service {
pub Name: String,
pub PathName: String,
}
let res: Result<Vec<Win32_Service>, _> = wmi_con.query();
let err = res.err().unwrap();
assert_eq!(
format!("{}", err),
"invalid type: Option value, expected a string"
)
}
#[test]
fn it_can_desr_newtype() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
pub struct Win32_Service {
pub Name: String,
pub PathName: Option<String>,
}
#[derive(Deserialize, Debug)]
struct Wrapper(Win32_Service);
let wrapped_service: Wrapper = wmi_con.get().unwrap();
assert_ne!(&wrapped_service.0.Name, "")
}
#[test]
fn it_can_desr_newtype_enum() {
let wmi_con = wmi_con();
#[derive(Deserialize, Debug)]
pub struct Win32_UserAccount {
pub __Path: String,
pub Name: String,
}
#[derive(Deserialize, Debug)]
pub struct Win32_SystemAccount {
pub Name: String,
}
#[derive(Deserialize, Debug)]
enum User {
#[serde(rename = "Win32_SystemAccount")]
System(Win32_SystemAccount),
#[serde(rename = "Win32_UserAccount")]
User(Win32_UserAccount),
};
let user: Win32_UserAccount = wmi_con.get().unwrap();
let user_enum: User = wmi_con.get_by_path(&user.__Path).unwrap();
match user_enum {
User::System(_) => assert!(false),
User::User(_) => assert!(true),
};
}
}