use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
const FLEXBUF_MAGIC: &[u8] = b"\xFB\x00";
pub fn serialize_property<T: Serialize>(value: &T) -> Result<Vec<u8>> {
let mut serializer = flexbuffers::FlexbufferSerializer::new();
value
.serialize(&mut serializer)
.map_err(|e| Error::Other(format!("flexbuffers serialization failed: {}", e)))?;
let mut result = Vec::with_capacity(FLEXBUF_MAGIC.len() + serializer.view().len());
result.extend_from_slice(FLEXBUF_MAGIC);
result.extend_from_slice(serializer.view());
Ok(result)
}
pub fn deserialize_property<'a, T: Deserialize<'a>>(data: &'a [u8]) -> Result<T> {
if data.is_empty() {
return Err(Error::Other("empty property data".to_string()));
}
if data.len() < FLEXBUF_MAGIC.len() || &data[..FLEXBUF_MAGIC.len()] != FLEXBUF_MAGIC {
return Err(Error::Other(
"invalid property data: missing FlexBuffers magic".to_string(),
));
}
let flexbuf_data = &data[FLEXBUF_MAGIC.len()..];
let reader = flexbuffers::Reader::get_root(flexbuf_data)
.map_err(|e| Error::Other(format!("flexbuffers deserialization failed: {}", e)))?;
T::deserialize(reader)
.map_err(|e| Error::Other(format!("flexbuffers type conversion failed: {}", e)))
}
pub fn serialize_properties(props: &HashMap<String, serde_json::Value>) -> Result<Vec<u8>> {
serialize_property(props)
}
pub fn deserialize_properties(data: &[u8]) -> Result<HashMap<String, serde_json::Value>> {
deserialize_property(data)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_serialize_deserialize_roundtrip() {
let mut props = HashMap::new();
props.insert("name".to_string(), json!("Alice"));
props.insert("age".to_string(), json!(30));
props.insert("active".to_string(), json!(true));
let serialized = serialize_properties(&props).unwrap();
assert_eq!(&serialized[..2], FLEXBUF_MAGIC);
let deserialized = deserialize_properties(&serialized).unwrap();
assert_eq!(deserialized.get("name").unwrap().as_str().unwrap(), "Alice");
assert_eq!(deserialized.get("age").unwrap().as_i64().unwrap(), 30);
assert!(deserialized.get("active").unwrap().as_bool().unwrap());
}
#[test]
fn test_invalid_data_rejected() {
let json_data = r#"{"name":"Bob","score":95}"#;
let json_bytes = json_data.as_bytes();
let result = deserialize_properties(json_bytes);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("missing FlexBuffers magic")
);
}
#[test]
fn test_nested_structures() {
let mut props = HashMap::new();
props.insert(
"metadata".to_string(),
json!({
"tags": ["rust", "database"],
"stats": {
"views": 1000,
"likes": 42
}
}),
);
let serialized = serialize_properties(&props).unwrap();
let deserialized = deserialize_properties(&serialized).unwrap();
let metadata = deserialized.get("metadata").unwrap();
assert_eq!(metadata["tags"][0].as_str().unwrap(), "rust");
assert_eq!(metadata["stats"]["likes"].as_i64().unwrap(), 42);
}
#[test]
fn test_empty_properties() {
let props = HashMap::new();
let serialized = serialize_properties(&props).unwrap();
let deserialized = deserialize_properties(&serialized).unwrap();
assert_eq!(deserialized.len(), 0);
}
#[test]
fn test_flexbuffers_serialization() {
let mut props = HashMap::new();
for i in 0..100 {
props.insert(format!("key{}", i), json!(i));
}
let flexbuf = serialize_properties(&props).unwrap();
let deserialized = deserialize_properties(&flexbuf).unwrap();
assert_eq!(props.len(), deserialized.len());
for i in 0..100 {
let key = format!("key{}", i);
assert_eq!(props.get(&key), deserialized.get(&key));
}
assert!(flexbuf.starts_with(FLEXBUF_MAGIC));
}
}