use crate::entity::Entity;
use crate::error::Result;
use crate::evolve::mutations::Mutations;
pub trait EntitySerializer<E: Entity> {
fn serialize(&self, entity: &E) -> Result<Vec<u8>>;
fn deserialize(&self, bytes: &[u8]) -> Result<E>;
fn deserialize_versioned(
&self,
bytes: &[u8],
class_version: u16,
mutations: &Mutations,
) -> Result<E> {
let _ = class_version;
let _ = mutations;
self.deserialize(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entity::Entity;
use crate::error::PersistError;
#[derive(Clone, Debug, PartialEq)]
struct Item {
id: u32,
value: String,
}
impl Entity for Item {
type PrimaryKey = u32;
fn primary_key(&self) -> &u32 {
&self.id
}
fn entity_name() -> &'static str {
"Item"
}
}
struct ItemSerializer;
impl EntitySerializer<Item> for ItemSerializer {
fn serialize(&self, entity: &Item) -> Result<Vec<u8>> {
let mut buf = Vec::new();
buf.extend_from_slice(&entity.id.to_be_bytes());
let val_bytes = entity.value.as_bytes();
buf.extend_from_slice(&(val_bytes.len() as u32).to_be_bytes());
buf.extend_from_slice(val_bytes);
Ok(buf)
}
fn deserialize(&self, bytes: &[u8]) -> Result<Item> {
if bytes.len() < 8 {
return Err(PersistError::SerializationError(
"not enough bytes for Item".to_string(),
));
}
let id =
u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let val_len =
u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]])
as usize;
if bytes.len() < 8 + val_len {
return Err(PersistError::SerializationError(
"not enough bytes for Item value".to_string(),
));
}
let value = String::from_utf8(bytes[8..8 + val_len].to_vec())
.map_err(|e| {
PersistError::SerializationError(format!(
"invalid UTF-8: {}",
e
))
})?;
Ok(Item { id, value })
}
}
#[test]
fn test_serialize_round_trip() {
let ser = ItemSerializer;
let item = Item { id: 42, value: "hello".to_string() };
let bytes = ser.serialize(&item).unwrap();
let decoded = ser.deserialize(&bytes).unwrap();
assert_eq!(item, decoded);
}
#[test]
fn test_serialize_empty_value() {
let ser = ItemSerializer;
let item = Item { id: 1, value: String::new() };
let bytes = ser.serialize(&item).unwrap();
let decoded = ser.deserialize(&bytes).unwrap();
assert_eq!(item, decoded);
}
#[test]
fn test_deserialize_too_short() {
let ser = ItemSerializer;
let result = ser.deserialize(&[1, 2, 3]);
assert!(result.is_err());
}
#[test]
fn test_deserialize_truncated_value() {
let ser = ItemSerializer;
let bytes = vec![0, 0, 0, 1, 0, 0, 0, 100, 65, 66];
let result = ser.deserialize(&bytes);
assert!(result.is_err());
}
#[test]
fn test_deserialize_invalid_utf8_in_value() {
let ser = ItemSerializer;
let mut bytes = vec![0u8, 0, 0, 1]; bytes.extend_from_slice(&3u32.to_be_bytes()); bytes.extend_from_slice(&[0xFF, 0xFE, 0xFD]); let result = ser.deserialize(&bytes);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("invalid UTF-8")
|| err_msg.contains("serialization"),
"{}",
err_msg
);
}
#[test]
fn test_serialize_large_value() {
let ser = ItemSerializer;
let large_value = "x".repeat(10_000);
let item = Item { id: 0xDEADBEEF, value: large_value.clone() };
let bytes = ser.serialize(&item).unwrap();
let decoded = ser.deserialize(&bytes).unwrap();
assert_eq!(decoded.id, 0xDEADBEEF);
assert_eq!(decoded.value, large_value);
}
#[test]
fn test_serialize_max_id() {
let ser = ItemSerializer;
let item = Item { id: u32::MAX, value: "max".to_string() };
let bytes = ser.serialize(&item).unwrap();
let decoded = ser.deserialize(&bytes).unwrap();
assert_eq!(decoded.id, u32::MAX);
assert_eq!(decoded.value, "max");
}
#[test]
fn test_serialize_id_zero_empty_value() {
let ser = ItemSerializer;
let item = Item { id: 0, value: String::new() };
let bytes = ser.serialize(&item).unwrap();
assert_eq!(bytes.len(), 8);
let decoded = ser.deserialize(&bytes).unwrap();
assert_eq!(decoded, item);
}
#[test]
fn test_deserialize_exactly_8_bytes_valid() {
let ser = ItemSerializer;
let bytes = vec![0u8, 0, 0, 0, 0, 0, 0, 0];
let result = ser.deserialize(&bytes);
assert!(result.is_ok());
let item = result.unwrap();
assert_eq!(item.id, 0);
assert_eq!(item.value, "");
}
#[test]
fn test_entity_name() {
assert_eq!(Item::entity_name(), "Item");
}
#[test]
fn test_primary_key_returns_id() {
let item = Item { id: 99, value: "test".to_string() };
assert_eq!(*item.primary_key(), 99u32);
}
}