Skip to main content

voltaria_sdk/core/
base64_bytes.rs

1//! Base64 encoding/decoding module for Vec<u8> fields
2//!
3//! This module provides serde helpers for serializing and deserializing
4//! Vec<u8> fields as base64-encoded strings in JSON.
5//!
6//! Usage:
7//! ```rust
8//! use serde::{Deserialize, Serialize};
9//!
10//! #[derive(Serialize, Deserialize)]
11//! struct MyStruct {
12//!     #[serde(with = "crate::core::base64_bytes")]
13//!     data: Vec<u8>,
14//! }
15//! ```
16
17use base64::{engine::general_purpose::STANDARD, Engine};
18use serde::{self, Deserialize, Deserializer, Serializer};
19
20/// Serialize a Vec<u8> as a base64-encoded string
21pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
22where
23    S: Serializer,
24{
25    let encoded = STANDARD.encode(bytes);
26    serializer.serialize_str(&encoded)
27}
28
29/// Deserialize a base64-encoded string into Vec<u8>
30pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
31where
32    D: Deserializer<'de>,
33{
34    let s = String::deserialize(deserializer)?;
35    STANDARD.decode(&s).map_err(serde::de::Error::custom)
36}
37
38/// Module for optional Vec<u8> fields with base64 encoding
39pub mod option {
40    use super::*;
41
42    pub fn serialize<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
43    where
44        S: Serializer,
45    {
46        match bytes {
47            Some(b) => {
48                let encoded = STANDARD.encode(b);
49                serializer.serialize_some(&encoded)
50            }
51            None => serializer.serialize_none(),
52        }
53    }
54
55    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
56    where
57        D: Deserializer<'de>,
58    {
59        let opt: Option<String> = Option::deserialize(deserializer)?;
60        match opt {
61            Some(s) => STANDARD
62                .decode(&s)
63                .map(Some)
64                .map_err(serde::de::Error::custom),
65            None => Ok(None),
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use serde::{Deserialize, Serialize};
74
75    #[derive(Serialize, Deserialize, Debug, PartialEq)]
76    struct TestStruct {
77        #[serde(with = "super")]
78        data: Vec<u8>,
79    }
80
81    #[derive(Serialize, Deserialize, Debug, PartialEq)]
82    struct TestStructOptional {
83        #[serde(default)]
84        #[serde(with = "super::option")]
85        #[serde(skip_serializing_if = "Option::is_none")]
86        data: Option<Vec<u8>>,
87    }
88
89    #[test]
90    fn test_serialize_bytes() {
91        let test = TestStruct {
92            data: b"Hello world!".to_vec(),
93        };
94        let json = serde_json::to_string(&test).unwrap();
95        assert_eq!(json, r#"{"data":"SGVsbG8gd29ybGQh"}"#);
96    }
97
98    #[test]
99    fn test_deserialize_bytes() {
100        let json = r#"{"data":"SGVsbG8gd29ybGQh"}"#;
101        let test: TestStruct = serde_json::from_str(json).unwrap();
102        assert_eq!(test.data, b"Hello world!");
103    }
104
105    #[test]
106    fn test_roundtrip() {
107        let original = TestStruct {
108            data: vec![0, 1, 2, 255, 254, 253],
109        };
110        let json = serde_json::to_string(&original).unwrap();
111        let decoded: TestStruct = serde_json::from_str(&json).unwrap();
112        assert_eq!(original, decoded);
113    }
114
115    #[test]
116    fn test_optional_some() {
117        let test = TestStructOptional {
118            data: Some(b"test".to_vec()),
119        };
120        let json = serde_json::to_string(&test).unwrap();
121        assert_eq!(json, r#"{"data":"dGVzdA=="}"#);
122
123        let decoded: TestStructOptional = serde_json::from_str(&json).unwrap();
124        assert_eq!(test, decoded);
125    }
126
127    #[test]
128    fn test_optional_none() {
129        let test = TestStructOptional { data: None };
130        let json = serde_json::to_string(&test).unwrap();
131        assert_eq!(json, r#"{}"#);
132    }
133
134    #[test]
135    fn test_optional_deserialize_null() {
136        let json = r#"{"data":null}"#;
137        let test: TestStructOptional = serde_json::from_str(json).unwrap();
138        assert_eq!(test.data, None);
139    }
140
141    #[test]
142    fn test_optional_deserialize_missing() {
143        let json = r#"{}"#;
144        let test: TestStructOptional = serde_json::from_str(json).unwrap();
145        assert_eq!(test.data, None);
146    }
147}