use base64::{Engine as _, engine::general_purpose};
use serde_json::{Map as JsonMap, Value};
pub const TYPE_MARKER_KEY: &str = "__type__";
pub const DATA_KEY: &str = "data";
pub const TYPE_BYTES: &str = "bytes";
pub const TYPE_BUFFER: &str = "buffer";
pub fn try_decode_bytes_object(obj: &serde_json::Map<String, Value>) -> Option<Vec<u8>> {
if let (Some(Value::String(type_str)), Some(Value::String(data))) =
(obj.get(TYPE_MARKER_KEY), obj.get(DATA_KEY))
{
if type_str == TYPE_BYTES || type_str == TYPE_BUFFER {
return general_purpose::STANDARD.decode(data).ok();
}
}
None
}
pub fn encode_bytes_as_json(bytes: &[u8], type_marker: &str) -> Value {
let base64_str = general_purpose::STANDARD.encode(bytes);
let mut map = JsonMap::new();
map.insert(
TYPE_MARKER_KEY.to_string(),
Value::String(type_marker.to_string()),
);
map.insert(DATA_KEY.to_string(), Value::String(base64_str));
Value::Object(map)
}
pub fn encode_bytes_python(bytes: &[u8]) -> Value {
encode_bytes_as_json(bytes, TYPE_BYTES)
}
pub fn encode_bytes_nodejs(bytes: &[u8]) -> Value {
encode_bytes_as_json(bytes, TYPE_BUFFER)
}
pub fn bytes_to_base64(bytes: &[u8]) -> String {
general_purpose::STANDARD.encode(bytes)
}
pub fn base64_to_bytes(encoded: &str) -> Result<Vec<u8>, base64::DecodeError> {
general_purpose::STANDARD.decode(encoded)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_decode_bytes_roundtrip() {
let original = vec![1u8, 2, 3, 4, 5, 255, 0, 128];
let encoded = encode_bytes_python(&original);
if let Value::Object(obj) = &encoded {
let decoded = try_decode_bytes_object(obj).expect("Should decode");
assert_eq!(decoded, original);
} else {
panic!("Expected object");
}
let encoded_node = encode_bytes_nodejs(&original);
if let Value::Object(obj) = &encoded_node {
let decoded = try_decode_bytes_object(obj).expect("Should decode");
assert_eq!(decoded, original);
} else {
panic!("Expected object");
}
}
#[test]
fn test_regular_object_not_decoded() {
let mut obj = JsonMap::new();
obj.insert("key".to_string(), Value::String("value".to_string()));
assert!(try_decode_bytes_object(&obj).is_none());
}
#[test]
fn test_base64_roundtrip() {
let original = b"Hello, World!";
let encoded = bytes_to_base64(original);
let decoded = base64_to_bytes(&encoded).expect("Should decode");
assert_eq!(decoded, original);
}
}