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::{Deserialize, 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    if deserializer.is_human_readable() {
30        let s = <String>::deserialize(deserializer)?;
31        BASE64_STANDARD.decode(&s).map_err(serde::de::Error::custom)
32    } else {
33        // Accept both byte-buffer formats (`serde_wasm_bindgen` Uint8Array →
34        // `visit_bytes` / `visit_byte_buf`) and length-prefixed sequences
35        // (bincode, `platform_value::Value::Array(u8)` → `visit_seq`). The
36        // default `<Vec<u8>>::deserialize` only covers the seq path.
37        struct BytesOrSeqVisitor;
38
39        impl<'de> Visitor<'de> for BytesOrSeqVisitor {
40            type Value = Vec<u8>;
41
42            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
43                f.write_str("bytes or sequence of u8")
44            }
45
46            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
47                Ok(v.to_vec())
48            }
49
50            fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
51                Ok(v)
52            }
53
54            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
55                let mut bytes = Vec::with_capacity(seq.size_hint().unwrap_or(0));
56                while let Some(b) = seq.next_element::<u8>()? {
57                    bytes.push(b);
58                }
59                Ok(bytes)
60            }
61        }
62
63        deserializer.deserialize_byte_buf(BytesOrSeqVisitor)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use base64::prelude::BASE64_STANDARD;
70    use base64::Engine;
71    use serde::{Deserialize, Serialize};
72
73    #[derive(Serialize, Deserialize, PartialEq, Debug)]
74    struct Wrap(#[serde(with = "super")] Vec<u8>);
75
76    #[test]
77    fn json_emits_base64_string() {
78        let original = Wrap(vec![0xde, 0xad, 0xbe, 0xef]);
79        let value = serde_json::to_value(&original).expect("serialize");
80        assert_eq!(
81            value,
82            serde_json::json!(BASE64_STANDARD.encode([0xde, 0xad, 0xbe, 0xef]))
83        );
84
85        let restored: Wrap = serde_json::from_value(value).expect("deserialize");
86        assert_eq!(original, restored);
87    }
88
89    #[test]
90    fn empty_vec_round_trips() {
91        let original = Wrap(Vec::new());
92        let value = serde_json::to_value(&original).expect("serialize empty");
93        assert_eq!(value, serde_json::json!(""));
94        let restored: Wrap = serde_json::from_value(value).expect("deserialize empty");
95        assert_eq!(original, restored);
96    }
97
98    #[test]
99    fn binary_round_trip_uses_raw_bytes() {
100        let original = Wrap(vec![1, 2, 3, 4, 5]);
101        let bytes = bincode::serde::encode_to_vec(&original, bincode::config::standard())
102            .expect("bincode encode");
103        let (restored, _): (Wrap, usize) =
104            bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
105                .expect("bincode decode");
106        assert_eq!(original, restored);
107    }
108}