diff --git a/Cargo.toml b/Cargo.toml index eabd2f2..32d8aef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wmi" -version = "0.4.1" +version = "0.4.2" authors = ["Ohad Ravid "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/de/wbem_class_de.rs b/src/de/wbem_class_de.rs index f33ddcd..fa0664a 100644 --- a/src/de/wbem_class_de.rs +++ b/src/de/wbem_class_de.rs @@ -233,6 +233,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { mod tests { use super::*; use crate::datetime::WMIDateTime; + use crate::duration::WMIDuration; use crate::variant::Variant; use serde::Deserialize; use std::collections::HashMap; @@ -403,6 +404,18 @@ mod tests { ) } + #[test] + fn it_desr_duration() { + let wmi_con = wmi_con(); + + #[derive(Deserialize, Debug)] + pub struct Win32_NetworkLoginProfile { + pub PasswordAge: Option, + } + + let profiles: Vec = wmi_con.query().unwrap(); + } + #[test] fn it_can_desr_newtype() { // Values can return as Null / Empty from WMI. diff --git a/src/duration.rs b/src/duration.rs new file mode 100644 index 0000000..510670a --- /dev/null +++ b/src/duration.rs @@ -0,0 +1,89 @@ +use chrono::prelude::*; +use failure::{bail, Error}; +use serde::{de, ser}; +use std::fmt; +use std::str::FromStr; +use std::time::Duration; + +/// A wrapper type around Duration, which supports parsing from WMI-format strings. +/// +#[derive(Debug)] +pub struct WMIDuration(pub Duration); + +impl FromStr for WMIDuration { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s.len() != 25 { + bail!("Expected {:?} to be at 25 chars", s) + } + + let (seconds_part, reminder) = s.split_at(14); + let (micros_part, _) = reminder[1..].split_at(6); + + let seconds: u64 = seconds_part.parse()?; + let micros: u64 = micros_part.parse()?; + + let duration = Duration::from_secs(seconds) + Duration::from_micros(micros); + + Ok(Self(duration)) + } +} + +struct DurationVisitor; + +impl<'de> de::Visitor<'de> for DurationVisitor { + type Value = WMIDuration; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a timestamp in WMI format") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value.parse().map_err(|err| E::custom(format!("{}", err))) + } +} + +impl<'de> de::Deserialize<'de> for WMIDuration { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_u64(DurationVisitor) + } +} + +impl ser::Serialize for WMIDuration { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_u64(self.0.as_micros() as u64) + } +} + +#[cfg(test)] +mod tests { + use super::WMIDuration; + use serde_json; + + #[test] + fn it_works() { + let duration: WMIDuration = "00000005141436.100001:000".parse().unwrap(); + + assert_eq!(duration.0.as_micros(), 5141436100001); + assert_eq!(duration.0.as_millis(), 5141436100); + assert_eq!(duration.0.as_secs(), 5141436); + } + + #[test] + fn it_serializes_to_rfc() { + let duration: WMIDuration = "00000005141436.100001:000".parse().unwrap(); + + let v = serde_json::to_string(&duration).unwrap(); + assert_eq!(v, "5141436100001"); + } +} diff --git a/src/lib.rs b/src/lib.rs index e99be43..081dcff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,6 +99,7 @@ pub mod connection; pub mod datetime; pub mod de; +pub mod duration; pub mod error; pub mod query; pub mod result_enumerator; @@ -111,4 +112,5 @@ pub mod tests; pub use connection::{COMLibrary, WMIConnection}; pub use datetime::WMIDateTime; +pub use duration::WMIDuration; pub use variant::Variant;