A better Rust ATProto crate
at main 3.4 kB view raw
1//! Custom serde helpers for bytes::Bytes using serde_bytes 2 3use base64::{ 4 Engine, 5 prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD, BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD}, 6}; 7use bytes::Bytes; 8use core::fmt; 9use serde::{ 10 Deserializer, Serializer, 11 de::{self, MapAccess, Visitor}, 12}; 13 14/// Serialize Bytes as a CBOR byte string 15pub fn serialize<S>(bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error> 16where 17 S: Serializer, 18{ 19 if let Some(bytes) = bytes { 20 if serializer.is_human_readable() { 21 // JSON: {"$bytes": "base64 string"} 22 use serde::ser::SerializeMap; 23 let mut map = serializer.serialize_map(Some(1))?; 24 map.serialize_entry("$bytes", &BASE64_STANDARD.encode(bytes))?; 25 map.end() 26 } else { 27 // CBOR: raw bytes 28 serde_bytes::serialize(bytes.as_ref(), serializer) 29 } 30 } else { 31 serializer.serialize_none() 32 } 33} 34 35/// Deserialize Bytes from a CBOR byte string 36pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Bytes>, D::Error> 37where 38 D: Deserializer<'de>, 39{ 40 if deserializer.is_human_readable() { 41 Ok(deserializer.deserialize_any(OptBytesVisitor)?) 42 } else { 43 let vec: Option<Vec<u8>> = serde_bytes::deserialize(deserializer)?; 44 Ok(vec.map(Bytes::from)) 45 } 46} 47 48struct OptBytesVisitor; 49 50impl<'de> Visitor<'de> for OptBytesVisitor { 51 type Value = Option<Bytes>; 52 53 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 54 formatter.write_str("a base64-encoded string") 55 } 56 57 fn visit_none<E>(self) -> Result<Self::Value, E> 58 where 59 E: de::Error, 60 { 61 Ok(None) 62 } 63 64 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error> 65 where 66 D: Deserializer<'de>, 67 { 68 let vec: Vec<u8> = serde_bytes::deserialize(deserializer)?; 69 Ok(Some(Bytes::from(vec))) 70 } 71 72 fn visit_unit<E>(self) -> Result<Self::Value, E> { 73 Ok(None) 74 } 75 76 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 77 where 78 A: MapAccess<'de>, 79 { 80 let mut bytes = None; 81 82 while let Some(key) = map.next_key()? { 83 match key { 84 "$bytes" => { 85 if bytes.is_some() { 86 return Err(de::Error::duplicate_field("$bytes")); 87 } 88 let bytes_str: String = map.next_value()?; 89 // First one should just work. rest are insurance. 90 bytes = if let Ok(bytes) = BASE64_STANDARD.decode(&bytes_str) { 91 Some(Bytes::from_owner(bytes)) 92 } else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(&bytes_str) { 93 Some(Bytes::from_owner(bytes)) 94 } else if let Ok(bytes) = BASE64_URL_SAFE.decode(&bytes_str) { 95 Some(Bytes::from_owner(bytes)) 96 } else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(&bytes_str) { 97 Some(Bytes::from_owner(bytes)) 98 } else { 99 return Err(de::Error::custom("invalid base64 string")); 100 } 101 } 102 _ => { 103 return Err(de::Error::unknown_field(key, &["$bytes"])); 104 } 105 } 106 } 107 108 Ok(bytes) 109 } 110}