#![forbid(unsafe_code)]
use serde::{Deserialize, Serialize};
use obj::Document;
use obj_core::codec::{encode, DocumentHeader, DOC_HEADER_SIZE};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[allow(clippy::module_name_repetitions)] struct Person {
name: String,
age: u64,
}
impl Document for Person {
const COLLECTION: &'static str = "people";
const VERSION: u32 = 1;
}
pub const PERSON_ADA_36_POSTCARD_U64: &[u8] = &[0x03, 0x41, 0x64, 0x61, 0x24];
pub const PERSON_ADA_36_POSTCARD_I64: &[u8] = &[0x03, 0x41, 0x64, 0x61, 0x48];
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct PersonV2 {
name: String,
age: u64,
}
impl Document for PersonV2 {
const COLLECTION: &'static str = "people_v2";
const VERSION: u32 = 2;
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct PersonI64 {
name: String,
age: i64,
}
impl Document for PersonI64 {
const COLLECTION: &'static str = "people_i64";
const VERSION: u32 = 1;
}
#[test]
fn rust_derive_emits_known_postcard_bytes_u64() {
let person = Person {
name: "Ada".to_owned(),
age: 36,
};
let bytes = postcard::to_allocvec(&person).expect("postcard encode");
assert_eq!(
bytes, PERSON_ADA_36_POSTCARD_U64,
"Rust U64 postcard payload drifted from the cross-language fixture"
);
}
#[test]
fn rust_derive_emits_known_postcard_bytes_i64() {
let person = PersonI64 {
name: "Ada".to_owned(),
age: 36,
};
let bytes = postcard::to_allocvec(&person).expect("postcard encode");
assert_eq!(
bytes, PERSON_ADA_36_POSTCARD_I64,
"Rust I64 postcard payload drifted from the cross-language fixture"
);
}
#[test]
fn rust_derive_round_trips() {
let person = Person {
name: "Ada".to_owned(),
age: 36,
};
let bytes = postcard::to_allocvec(&person).expect("encode");
let back: Person = postcard::from_bytes(&bytes).expect("decode");
assert_eq!(back, person);
}
#[test]
fn db_write_then_read_round_trip() {
let tmpdir = tempfile::tempdir().expect("tempdir");
let path = tmpdir.path().join("interop.obj");
let db = obj::Db::open(&path).expect("open");
let id = db
.insert(Person {
name: "Ada".to_owned(),
age: 36,
})
.expect("insert");
let got: Person = db.get(id).expect("get").expect("present");
assert_eq!(
got,
Person {
name: "Ada".to_owned(),
age: 36,
}
);
}
fn person_v2_expected_record_bytes() -> Vec<u8> {
let doc = PersonV2 {
name: "Ada".to_owned(),
age: 36,
};
encode(&doc, 1).expect("codec encode")
}
#[test]
fn rust_v2_record_header_bytes_are_pinned() {
let bytes = person_v2_expected_record_bytes();
assert_eq!(
bytes.len(),
DOC_HEADER_SIZE + PERSON_ADA_36_POSTCARD_U64.len(),
"expected record = 16-byte header + 5-byte payload"
);
assert_eq!(&bytes[0..4], &[0x01, 0x00, 0x00, 0x00]);
assert_eq!(
&bytes[4..8],
&[0x02, 0x00, 0x00, 0x00],
"type_version field must carry T::VERSION (=2) in LE u32"
);
assert_eq!(&bytes[8..12], &[0x05, 0x00, 0x00, 0x00]);
assert_eq!(&bytes[DOC_HEADER_SIZE..], PERSON_ADA_36_POSTCARD_U64);
let header = DocumentHeader::read_from(&bytes).expect("header decode");
assert_eq!(header.collection_id, 1);
assert_eq!(header.type_version, 2);
assert_eq!(header.payload_len, 5);
eprintln!(
"PersonV2 v2 record bytes (hex): {}",
bytes
.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<_>>()
.join(" ")
);
}
#[test]
fn rust_v2_engine_write_matches_codec_encode() {
let tmpdir = tempfile::tempdir().expect("tempdir");
let path = tmpdir.path().join("v2_record.obj");
let db = obj::Db::open(&path).expect("open");
let _ = db
.insert(PersonV2 {
name: "Ada".to_owned(),
age: 36,
})
.expect("insert");
let dump = db.dump_raw("people_v2", 0).expect("dump_raw");
let records: Vec<_> = dump.map(|step| step.expect("dump step")).collect();
assert_eq!(records.len(), 1, "expected exactly one record");
let record = &records[0];
assert_eq!(record.header.collection_id, 1);
assert_eq!(record.header.type_version, 2);
assert_eq!(record.header.payload_len, 5);
assert_eq!(record.payload.as_slice(), PERSON_ADA_36_POSTCARD_U64);
let mut reassembled = Vec::with_capacity(DOC_HEADER_SIZE + record.payload.len());
record.header.write_to(&mut reassembled);
reassembled.extend_from_slice(&record.payload);
let expected = person_v2_expected_record_bytes();
assert_eq!(
reassembled, expected,
"Rust-derive on-disk record drifted from codec::encode output"
);
}