tools: make date_time_as_rfc3339 work for all TimeZones

and add a doc-test passing `cargo test`
(and formatting fixup)

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-06-18 12:10:45 +02:00
parent 099efb2da5
commit be6d9fb6ab
2 changed files with 52 additions and 23 deletions

View File

@ -23,3 +23,4 @@ valgrind = [ "valgrind_request" ]
# Docs should be able to reference the proxmox crate. # Docs should be able to reference the proxmox crate.
[dev-dependencies] [dev-dependencies]
proxmox = { path = "../proxmox" } proxmox = { path = "../proxmox" }
serde_json = "1.0"

View File

@ -1,51 +1,79 @@
//! Serialization helpers for serde //! Serialization helpers for serde
/// Sertialize DateTime<Local> as RFC3339 /// Serialize DateTime<Local> as RFC3339.
///
/// Usage example:
/// ```
/// # pub extern crate proxmox_tools;
/// # mod proxmox { pub use proxmox_tools as tools; }
///
/// use chrono::{DateTime, TimeZone, Utc};
/// use serde::{Deserialize, Serialize};
///
/// # #[derive(Debug)]
/// #[derive(Deserialize, PartialEq, Serialize)]
/// struct Foo {
/// #[serde(with = "proxmox::tools::serde::date_time_as_rfc3339")]
/// date: DateTime<Utc>,
/// }
///
/// let obj = Foo { date: Utc.timestamp_millis(86400000) }; // random test value
/// let json = serde_json::to_string(&obj).unwrap();
/// assert_eq!(json, r#"{"date":"1970-01-02T00:00:00+00:00"}"#);
///
/// let deserialized: Foo = serde_json::from_str(&json).unwrap();
/// assert_eq!(obj, deserialized);
/// ```
pub mod date_time_as_rfc3339 { pub mod date_time_as_rfc3339 {
use chrono::{DateTime, TimeZone};
use serde::{Deserialize, Deserializer, Serializer};
use chrono::{Local, DateTime}; pub fn serialize<S, Tz>(time: &DateTime<Tz>, serializer: S) -> Result<S::Ok, S::Error>
use serde::{Serializer, Deserializer, Deserialize}; where
S: Serializer,
pub fn serialize<S>(time: &DateTime<Local>, serializer: S) -> Result<S::Ok, S::Error> Tz: TimeZone,
where S: Serializer, Tz::Offset: std::fmt::Display,
{ {
serializer.serialize_str(&time.to_rfc3339()) serializer.serialize_str(&time.to_rfc3339())
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Local>, D::Error> pub fn deserialize<'de, D, Tz>(deserializer: D) -> Result<DateTime<Tz>, D::Error>
where D: Deserializer<'de> where
D: Deserializer<'de>,
Tz: TimeZone,
DateTime<Tz>: std::str::FromStr,
<DateTime<Tz> as std::str::FromStr>::Err: std::string::ToString,
{ {
use serde::de::Error; use serde::de::Error;
String::deserialize(deserializer) String::deserialize(deserializer).and_then(|string| {
.and_then(|string| { string
string.parse::<DateTime<Local>>() .parse::<DateTime<Tz>>()
.map_err(|err| Error::custom(err.to_string())) .map_err(|err| Error::custom(err.to_string()))
}) })
} }
} }
/// Serialize Vec<u8> as base64 encoded string. /// Serialize Vec<u8> as base64 encoded string.
pub mod bytes_as_base64 { pub mod bytes_as_base64 {
use base64; use base64;
use serde::{Serializer,Deserializer, Deserialize}; use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error> pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
where T: AsRef<[u8]>, where
S: Serializer, T: AsRef<[u8]>,
S: Serializer,
{ {
serializer.serialize_str(&base64::encode(data.as_ref())) serializer.serialize_str(&base64::encode(data.as_ref()))
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where D: Deserializer<'de> where
D: Deserializer<'de>,
{ {
use serde::de::Error; use serde::de::Error;
String::deserialize(deserializer) String::deserialize(deserializer).and_then(|string| {
.and_then(|string| { base64::decode(&string).map_err(|err| Error::custom(err.to_string()))
base64::decode(&string) })
.map_err(|err| Error::custom(err.to_string()))
})
} }
} }