mermaid_cli/utils/
serde_base64.rs1use base64::{Engine as _, engine::general_purpose};
13use serde::{Deserialize, Deserializer, Serializer};
14
15pub fn serialize<S: Serializer>(bytes: &[u8], ser: S) -> Result<S::Ok, S::Error> {
16 ser.serialize_str(&general_purpose::STANDARD.encode(bytes))
17}
18
19pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Vec<u8>, D::Error> {
20 let s = String::deserialize(de)?;
21 general_purpose::STANDARD
22 .decode(s.as_bytes())
23 .map_err(serde::de::Error::custom)
24}
25
26pub mod string {
29 use base64::{Engine as _, engine::general_purpose};
30 use serde::{Deserialize, Deserializer, Serializer};
31
32 pub fn serialize<S: Serializer>(value: &str, ser: S) -> Result<S::Ok, S::Error> {
33 ser.serialize_str(&general_purpose::STANDARD.encode(value.as_bytes()))
34 }
35
36 pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<String, D::Error> {
37 let encoded = String::deserialize(de)?;
38 let bytes = general_purpose::STANDARD
39 .decode(encoded.as_bytes())
40 .map_err(serde::de::Error::custom)?;
41 String::from_utf8(bytes).map_err(serde::de::Error::custom)
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use serde::{Deserialize, Serialize};
48
49 #[derive(Serialize, Deserialize, PartialEq, Debug)]
50 struct Blob {
51 #[serde(with = "super")]
52 data: Vec<u8>,
53 }
54
55 #[test]
56 fn round_trips_bytes_as_base64_string() {
57 let blob = Blob {
58 data: vec![0, 1, 2, 250, 255],
59 };
60 let json = serde_json::to_string(&blob).unwrap();
61 assert_eq!(json, r#"{"data":"AAEC+v8="}"#);
62 let back: Blob = serde_json::from_str(&json).unwrap();
63 assert_eq!(back, blob);
64 }
65
66 #[test]
67 fn rejects_invalid_base64() {
68 let err = serde_json::from_str::<Blob>(r#"{"data":"not base64!!"}"#);
69 assert!(err.is_err());
70 }
71
72 #[test]
73 fn opaque_string_round_trips_and_rejects_non_utf8() {
74 #[derive(Serialize, Deserialize, PartialEq, Debug)]
75 struct Opaque {
76 #[serde(with = "super::string")]
77 value: String,
78 }
79 let value = Opaque {
80 value: "eyJopaque.payload.signature".to_string(),
81 };
82 let json = serde_json::to_string(&value).unwrap();
83 assert!(!json.contains("eyJopaque.payload.signature"));
84 assert_eq!(serde_json::from_str::<Opaque>(&json).unwrap(), value);
85 assert!(serde_json::from_str::<Opaque>(r#"{"value":"/w=="}"#).is_err());
86 }
87}