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