#![allow(dead_code)]
use base64::{Engine, engine::general_purpose::STANDARD};
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize_base64_opt<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(v) => serializer.serialize_str(&STANDARD.encode(v.as_bytes())),
None => serializer.serialize_none(),
}
}
pub fn deserialize_base64_opt<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(encoded) => {
let bytes = STANDARD
.decode(&encoded)
.map_err(serde::de::Error::custom)?;
let s = String::from_utf8(bytes).map_err(serde::de::Error::custom)?;
Ok(Some(s))
}
None => Ok(None),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestStruct {
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_base64_opt",
deserialize_with = "deserialize_base64_opt"
)]
body: Option<String>,
}
#[test]
fn serialize_none_omits_field() {
let t = TestStruct { body: None };
let json = serde_json::to_string(&t).unwrap();
assert_eq!(json, "{}");
}
#[test]
fn serialize_some_base64_encodes() {
let t = TestStruct {
body: Some(r#"{"key":"value"}"#.into()),
};
let json = serde_json::to_string(&t).unwrap();
let expected_b64 = STANDARD.encode(r#"{"key":"value"}"#.as_bytes());
assert_eq!(json, format!(r#"{{"body":"{}"}}"#, expected_b64));
}
#[test]
fn deserialize_base64_decodes() {
let encoded = STANDARD.encode(r#"{"key":"value"}"#.as_bytes());
let json = format!(r#"{{"body":"{}"}}"#, encoded);
let t: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(t.body, Some(r#"{"key":"value"}"#.into()));
}
#[test]
fn roundtrip_preserves_value() {
let original = TestStruct {
body: Some("hello world".into()),
};
let json = serde_json::to_string(&original).unwrap();
let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn deserialize_missing_field_gives_none() {
let json = "{}";
let t: TestStruct = serde_json::from_str(json).unwrap();
assert_eq!(t.body, None);
}
#[test]
fn deserialize_invalid_base64_errors() {
let json = r#"{"body":"not-valid-base64!!!"}"#;
let result: Result<TestStruct, _> = serde_json::from_str(json);
assert!(result.is_err());
}
}