use crate::error::{DbxError, DbxResult};
pub trait Versionable: Sized + Clone {
fn version_key(&self) -> Vec<u8>;
fn serialize(&self) -> DbxResult<Vec<u8>>;
fn deserialize(bytes: &[u8]) -> DbxResult<Self>;
}
impl Versionable for Vec<u8> {
fn version_key(&self) -> Vec<u8> {
self.clone()
}
fn serialize(&self) -> DbxResult<Vec<u8>> {
Ok(self.clone())
}
fn deserialize(bytes: &[u8]) -> DbxResult<Self> {
Ok(bytes.to_vec())
}
}
impl Versionable for String {
fn version_key(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn serialize(&self) -> DbxResult<Vec<u8>> {
Ok(self.as_bytes().to_vec())
}
fn deserialize(bytes: &[u8]) -> DbxResult<Self> {
String::from_utf8(bytes.to_vec())
.map_err(|e| DbxError::Storage(format!("UTF-8 decode error: {}", e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, PartialEq)]
struct TestUser {
id: u64,
name: String,
age: u32,
}
impl Versionable for TestUser {
fn version_key(&self) -> Vec<u8> {
self.id.to_be_bytes().to_vec()
}
fn serialize(&self) -> DbxResult<Vec<u8>> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.id.to_be_bytes());
bytes.extend_from_slice(&self.age.to_be_bytes());
let name_bytes = self.name.as_bytes();
bytes.extend_from_slice(&(name_bytes.len() as u32).to_be_bytes());
bytes.extend_from_slice(name_bytes);
Ok(bytes)
}
fn deserialize(bytes: &[u8]) -> DbxResult<Self> {
if bytes.len() < 16 {
return Err(DbxError::Storage("Invalid TestUser data".into()));
}
let id = u64::from_be_bytes(bytes[0..8].try_into().unwrap());
let age = u32::from_be_bytes(bytes[8..12].try_into().unwrap());
let name_len = u32::from_be_bytes(bytes[12..16].try_into().unwrap()) as usize;
if bytes.len() < 16 + name_len {
return Err(DbxError::Storage("Invalid TestUser name length".into()));
}
let name = String::from_utf8(bytes[16..16 + name_len].to_vec())
.map_err(|e| DbxError::Storage(e.to_string()))?;
Ok(TestUser { id, name, age })
}
}
#[test]
fn test_versionable_basic() {
let data = vec![1, 2, 3, 4, 5];
let key = data.version_key();
assert_eq!(key, data);
let serialized = data.serialize().unwrap();
assert_eq!(serialized, data);
let deserialized = Vec::<u8>::deserialize(&serialized).unwrap();
assert_eq!(deserialized, data);
}
#[test]
fn test_versionable_custom_types() {
let user = TestUser {
id: 42,
name: "Alice".to_string(),
age: 30,
};
let key = user.version_key();
assert_eq!(key, 42u64.to_be_bytes().to_vec());
let serialized = user.serialize().unwrap();
let deserialized = TestUser::deserialize(&serialized).unwrap();
assert_eq!(deserialized, user);
}
#[test]
fn test_versionable_serialization_roundtrip() {
let original = "Hello, MVCC!".to_string();
let serialized = original.serialize().unwrap();
let deserialized = String::deserialize(&serialized).unwrap();
assert_eq!(deserialized, original);
let user = TestUser {
id: 100,
name: "Bob".to_string(),
age: 25,
};
let serialized = user.serialize().unwrap();
let deserialized = TestUser::deserialize(&serialized).unwrap();
assert_eq!(deserialized, user);
}
#[test]
fn test_versionable_version_key_uniqueness() {
let user1 = TestUser {
id: 1,
name: "Alice".to_string(),
age: 30,
};
let user2 = TestUser {
id: 1,
name: "Alice Updated".to_string(),
age: 31,
};
assert_eq!(user1.version_key(), user2.version_key());
let user3 = TestUser {
id: 2,
name: "Bob".to_string(),
age: 25,
};
assert_ne!(user1.version_key(), user3.version_key());
}
#[test]
fn test_versionable_with_complex_data() {
#[derive(Clone, Debug, PartialEq)]
struct ComplexData {
id: u64,
tags: Vec<String>,
metadata: Vec<(String, String)>,
}
impl Versionable for ComplexData {
fn version_key(&self) -> Vec<u8> {
self.id.to_be_bytes().to_vec()
}
fn serialize(&self) -> DbxResult<Vec<u8>> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.id.to_be_bytes());
bytes.extend_from_slice(&(self.tags.len() as u32).to_be_bytes());
for tag in &self.tags {
let tag_bytes = tag.as_bytes();
bytes.extend_from_slice(&(tag_bytes.len() as u32).to_be_bytes());
bytes.extend_from_slice(tag_bytes);
}
bytes.extend_from_slice(&(self.metadata.len() as u32).to_be_bytes());
for (k, v) in &self.metadata {
let k_bytes = k.as_bytes();
let v_bytes = v.as_bytes();
bytes.extend_from_slice(&(k_bytes.len() as u32).to_be_bytes());
bytes.extend_from_slice(k_bytes);
bytes.extend_from_slice(&(v_bytes.len() as u32).to_be_bytes());
bytes.extend_from_slice(v_bytes);
}
Ok(bytes)
}
fn deserialize(bytes: &[u8]) -> DbxResult<Self> {
if bytes.len() < 8 {
return Err(DbxError::Storage("Invalid ComplexData".into()));
}
let mut offset = 0;
let id = u64::from_be_bytes(bytes[offset..offset + 8].try_into().unwrap());
offset += 8;
if bytes.len() < offset + 4 {
return Err(DbxError::Storage("Invalid tags count".into()));
}
let tags_count =
u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
let mut tags = Vec::new();
for _ in 0..tags_count {
if bytes.len() < offset + 4 {
return Err(DbxError::Storage("Invalid tag length".into()));
}
let tag_len =
u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
if bytes.len() < offset + tag_len {
return Err(DbxError::Storage("Invalid tag data".into()));
}
let tag = String::from_utf8(bytes[offset..offset + tag_len].to_vec())
.map_err(|e| DbxError::Storage(e.to_string()))?;
tags.push(tag);
offset += tag_len;
}
if bytes.len() < offset + 4 {
return Err(DbxError::Storage("Invalid metadata count".into()));
}
let metadata_count =
u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
let mut metadata = Vec::new();
for _ in 0..metadata_count {
if bytes.len() < offset + 4 {
return Err(DbxError::Storage("Invalid metadata key length".into()));
}
let k_len =
u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
if bytes.len() < offset + k_len {
return Err(DbxError::Storage("Invalid metadata key data".into()));
}
let k = String::from_utf8(bytes[offset..offset + k_len].to_vec())
.map_err(|e| DbxError::Storage(e.to_string()))?;
offset += k_len;
if bytes.len() < offset + 4 {
return Err(DbxError::Storage("Invalid metadata value length".into()));
}
let v_len =
u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
if bytes.len() < offset + v_len {
return Err(DbxError::Storage("Invalid metadata value data".into()));
}
let v = String::from_utf8(bytes[offset..offset + v_len].to_vec())
.map_err(|e| DbxError::Storage(e.to_string()))?;
offset += v_len;
metadata.push((k, v));
}
Ok(ComplexData { id, tags, metadata })
}
}
let data = ComplexData {
id: 999,
tags: vec![
"rust".to_string(),
"database".to_string(),
"mvcc".to_string(),
],
metadata: vec![
("author".to_string(), "Team C".to_string()),
("version".to_string(), "1.0".to_string()),
],
};
let serialized = data.serialize().unwrap();
let deserialized = ComplexData::deserialize(&serialized).unwrap();
assert_eq!(deserialized, data);
assert_eq!(deserialized.version_key(), 999u64.to_be_bytes().to_vec());
}
}