From 2df0b8efb3dcd7370d6b54580ab17c75755bbd9d Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 25 Aug 2021 09:40:53 +0200 Subject: [PATCH] tools::serde: support Option in string_as_base64 This will make Updater derivations go more smoothly. Signed-off-by: Wolfgang Bumiller --- proxmox/src/tools/serde.rs | 62 +++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/proxmox/src/tools/serde.rs b/proxmox/src/tools/serde.rs index 60e6a00c..332774bd 100644 --- a/proxmox/src/tools/serde.rs +++ b/proxmox/src/tools/serde.rs @@ -88,7 +88,7 @@ pub mod bytes_as_base64 { } } -/// Serialize String as base64 encoded string. +/// Serialize `String` or `Option` as base64 encoded. /// /// Usage example: /// ``` @@ -111,28 +111,68 @@ pub mod bytes_as_base64 { pub mod string_as_base64 { use serde::{Deserialize, Deserializer, Serializer}; - pub fn serialize(data: &str, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&base64::encode(data.as_bytes())) + /// Private trait to enable `string_as_base64` for `Option` in addition to `String`. + #[doc(hidden)] + pub trait StrAsBase64: Sized { + fn ser(&self, serializer: S) -> Result; + fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result; } - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { + fn finish_deserializing<'de, D: Deserializer<'de>>(string: String) -> Result { use serde::de::Error; - let string = String::deserialize(deserializer)?; + let bytes = base64::decode(&string).map_err(|err| { let msg = format!("base64 decode: {}", err.to_string()); Error::custom(msg) })?; + String::from_utf8(bytes).map_err(|err| { let msg = format!("utf8 decode: {}", err.to_string()); Error::custom(msg) }) } + + impl StrAsBase64 for String { + fn ser(&self, serializer: S) -> Result { + serializer.serialize_str(&base64::encode(self.as_bytes())) + } + + fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + finish_deserializing::<'de, D>(String::deserialize(deserializer)?) + } + } + + impl StrAsBase64 for Option { + fn ser(&self, serializer: S) -> Result { + match self { + Some(s) => StrAsBase64::ser(s, serializer), + None => serializer.serialize_none(), + } + } + + fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + match Self::deserialize(deserializer)? { + Some(s) => Ok(Some(finish_deserializing::<'de, D>(s)?)), + None => Ok(None), + } + } + } + + pub fn serialize(data: &T, serializer: S) -> Result + where + S: Serializer, + T: StrAsBase64, + { + ::ser(data, serializer) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: StrAsBase64, + { + ::de::<'de, D>(deserializer) + } } /// Serialize Vec as base64url encoded string without padding.