Skip to main content

dpp/serialization/
serde_bytes_var.rs

1//! Serde helper for variable-length `Vec<u8>` byte fields.
2//!
3//! Default serde serializes `Vec<u8>` as a sequence of u8 elements. For JS / wasm
4//! consumers this is verbose and not ergonomic; we want bytes in binary formats
5//! and base64 strings in human-readable formats — matching `BinaryData` (the
6//! widely-used opaque-bytes wrapper in `rs-platform-value`) and the const-generic
7//! `serde_bytes` helper.
8//!
9//! - **Human-readable** formats (JSON): base64-encoded string
10//! - **Binary** formats (bincode / `platform_value`): raw byte sequence (which
11//!   becomes `Uint8Array` through `serde_wasm_bindgen` with
12//!   `serialize_bytes_as_arrays(false)`)
13
14use base64::prelude::BASE64_STANDARD;
15use base64::Engine;
16use serde::de::{self, SeqAccess, Visitor};
17use serde::{Deserializer, Serializer};
18use std::fmt;
19
20pub fn serialize<S: Serializer>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error> {
21    if serializer.is_human_readable() {
22        serializer.serialize_str(&BASE64_STANDARD.encode(bytes))
23    } else {
24        serializer.serialize_bytes(bytes)
25    }
26}
27
28pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
29    // Accept all four input shapes — base64 string, byte buffer, byte slice,
30    // and sequence of u8 — regardless of the deserializer's `is_human_readable`
31    // flag. Required because serde's `ContentDeserializer` (used for internally
32    // tagged enums like `#[serde(tag = "$formatVersion")]`) always reports
33    // `is_human_readable: true`, so a value that started as bytes through a
34    // non-HR deserializer can arrive at this visitor through any path.
35
36    struct AnyShapeVisitor;
37
38    impl<'de> Visitor<'de> for AnyShapeVisitor {
39        type Value = Vec<u8>;
40
41        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
42            f.write_str("bytes, sequence of u8, or base64-encoded string")
43        }
44
45        fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
46            Ok(v.to_vec())
47        }
48
49        fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
50            Ok(v)
51        }
52
53        fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
54            BASE64_STANDARD
55                .decode(v)
56                .map_err(|e| E::custom(format!("expected base64 for bytes: {}", e)))
57        }
58
59        fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
60            let mut bytes = Vec::with_capacity(seq.size_hint().unwrap_or(0));
61            while let Some(b) = seq.next_element::<u8>()? {
62                bytes.push(b);
63            }
64            Ok(bytes)
65        }
66    }
67
68    if deserializer.is_human_readable() {
69        // `deserialize_any` covers true HR (serde_json string) AND
70        // ContentDeserializer (which reports HR but may wrap bytes from a
71        // non-HR source like platform_value).
72        deserializer.deserialize_any(AnyShapeVisitor)
73    } else {
74        // Non-HR (bincode, platform_value): explicit shape hint.
75        deserializer.deserialize_byte_buf(AnyShapeVisitor)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use base64::prelude::BASE64_STANDARD;
82    use base64::Engine;
83    use serde::{Deserialize, Serialize};
84
85    #[derive(Serialize, Deserialize, PartialEq, Debug)]
86    struct Wrap(#[serde(with = "super")] Vec<u8>);
87
88    #[test]
89    fn json_emits_base64_string() {
90        let original = Wrap(vec![0xde, 0xad, 0xbe, 0xef]);
91        let value = serde_json::to_value(&original).expect("serialize");
92        assert_eq!(
93            value,
94            serde_json::json!(BASE64_STANDARD.encode([0xde, 0xad, 0xbe, 0xef]))
95        );
96
97        let restored: Wrap = serde_json::from_value(value).expect("deserialize");
98        assert_eq!(original, restored);
99    }
100
101    #[test]
102    fn empty_vec_round_trips() {
103        let original = Wrap(Vec::new());
104        let value = serde_json::to_value(&original).expect("serialize empty");
105        assert_eq!(value, serde_json::json!(""));
106        let restored: Wrap = serde_json::from_value(value).expect("deserialize empty");
107        assert_eq!(original, restored);
108    }
109
110    #[test]
111    fn binary_round_trip_uses_raw_bytes() {
112        let original = Wrap(vec![1, 2, 3, 4, 5]);
113        let bytes = bincode::serde::encode_to_vec(&original, bincode::config::standard())
114            .expect("bincode encode");
115        let (restored, _): (Wrap, usize) =
116            bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
117                .expect("bincode decode");
118        assert_eq!(original, restored);
119    }
120}