#![allow(
clippy::approx_constant,
clippy::useless_vec,
clippy::len_zero,
clippy::unnecessary_cast,
clippy::redundant_closure,
clippy::too_many_arguments,
clippy::type_complexity,
clippy::needless_borrow,
clippy::enum_variant_names,
clippy::upper_case_acronyms,
clippy::inconsistent_digit_grouping,
clippy::unit_cmp,
clippy::assertions_on_constants,
clippy::iter_on_single_items,
clippy::expect_fun_call,
clippy::redundant_pattern_matching,
variant_size_differences,
clippy::absurd_extreme_comparisons,
clippy::nonminimal_bool,
clippy::for_kv_map,
clippy::needless_range_loop,
clippy::single_match,
clippy::collapsible_if,
clippy::needless_return,
clippy::redundant_clone,
clippy::map_entry,
clippy::match_single_binding,
clippy::bool_comparison,
clippy::derivable_impls,
clippy::manual_range_contains,
clippy::needless_borrows_for_generic_args,
clippy::manual_map,
clippy::vec_init_then_push,
clippy::identity_op,
clippy::manual_flatten,
clippy::single_char_pattern,
clippy::search_is_some,
clippy::option_map_unit_fn,
clippy::while_let_on_iterator,
clippy::clone_on_copy,
clippy::box_collection,
clippy::redundant_field_names,
clippy::ptr_arg,
clippy::large_enum_variant,
clippy::match_ref_pats,
clippy::needless_pass_by_value,
clippy::unused_unit,
clippy::let_and_return,
clippy::suspicious_else_formatting,
clippy::manual_strip,
clippy::match_like_matches_macro,
clippy::from_over_into,
clippy::wrong_self_convention,
clippy::inherent_to_string,
clippy::new_without_default,
clippy::unnecessary_wraps,
clippy::field_reassign_with_default,
clippy::manual_find,
clippy::unnecessary_lazy_evaluations,
clippy::should_implement_trait,
clippy::missing_safety_doc,
clippy::unusual_byte_groupings,
clippy::bool_assert_comparison,
clippy::zero_prefixed_literal,
clippy::await_holding_lock,
clippy::manual_saturating_arithmetic,
clippy::explicit_counter_loop,
clippy::needless_lifetimes,
clippy::single_component_path_imports,
clippy::uninlined_format_args,
clippy::iter_cloned_collect,
clippy::manual_str_repeat,
clippy::excessive_precision,
clippy::precedence,
clippy::unnecessary_literal_unwrap
)]
use oxicode::{
config, decode_from_slice, decode_from_slice_with_config, encode_to_vec,
encode_to_vec_with_config, Decode, Encode,
};
use std::collections::BTreeMap;
const WIRE_VERSION_V1: u8 = 0x01;
const WIRE_VERSION_V2: u8 = 0x02;
#[derive(Debug, PartialEq, Encode, Decode)]
struct RecordV1 {
id: u32,
name: String,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct RecordV2 {
id: u32,
name: String,
age: u16,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct RecordV3Compat {
id: u32,
name: String,
#[oxicode(skip)]
derived_score: u64,
}
#[test]
fn test_v1_struct_roundtrip() {
let rec = RecordV1 {
id: 1,
name: "Alice".into(),
};
let enc = encode_to_vec(&rec).expect("encode v1");
let (dec, consumed): (RecordV1, usize) = decode_from_slice(&enc).expect("decode v1");
assert_eq!(rec, dec);
assert_eq!(consumed, enc.len());
}
#[test]
fn test_v1_bytes_decode_into_compat_struct_with_skip() {
let v1 = RecordV1 {
id: 42,
name: "Bob".into(),
};
let enc = encode_to_vec(&v1).expect("encode v1");
let (compat, consumed): (RecordV3Compat, usize) =
decode_from_slice(&enc).expect("decode as RecordV3Compat");
assert_eq!(compat.id, 42);
assert_eq!(compat.name, "Bob");
assert_eq!(compat.derived_score, 0u64);
assert_eq!(consumed, enc.len());
}
#[test]
fn test_skip_field_produces_same_layout_as_v1() {
let v1 = RecordV1 {
id: 7,
name: "Carol".into(),
};
let v3 = RecordV3Compat {
id: 7,
name: "Carol".into(),
derived_score: 0xDEAD_BEEF_CAFE_1234,
};
let enc_v1 = encode_to_vec(&v1).expect("encode v1");
let enc_v3 = encode_to_vec(&v3).expect("encode v3");
assert_eq!(enc_v1, enc_v3);
}
#[test]
fn test_manual_version_byte_prepend_and_extract() {
let rec = RecordV1 {
id: 100,
name: "Dave".into(),
};
let payload = encode_to_vec(&rec).expect("encode payload");
let mut versioned: Vec<u8> = Vec::with_capacity(1 + payload.len());
versioned.push(WIRE_VERSION_V1);
versioned.extend_from_slice(&payload);
let version_tag = versioned[0];
assert_eq!(version_tag, WIRE_VERSION_V1);
let (decoded, consumed): (RecordV1, usize) =
decode_from_slice(&versioned[1..]).expect("decode after version byte");
assert_eq!(decoded, rec);
assert_eq!(consumed, payload.len());
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VersionTaggedRecord {
schema_version: u32,
id: u32,
name: String,
}
#[test]
fn test_u32_version_tag_in_struct_roundtrip() {
let rec = VersionTaggedRecord {
schema_version: 1,
id: 55,
name: "Eve".into(),
};
let enc = encode_to_vec(&rec).expect("encode version-tagged record");
let (dec, consumed): (VersionTaggedRecord, usize) =
decode_from_slice(&enc).expect("decode version-tagged record");
assert_eq!(rec, dec);
assert_eq!(consumed, enc.len());
assert_eq!(dec.schema_version, 1u32);
}
#[test]
fn test_binary_layout_field_order_matches_declaration() {
let rec = RecordV1 {
id: 1,
name: "AB".into(),
};
let enc = encode_to_vec(&rec).expect("encode layout test");
assert_eq!(enc[0], 0x01, "id=1 must be first byte 0x01");
assert_eq!(enc[1], 0x02, "string length=2 must be next byte 0x02");
assert_eq!(&enc[2..4], b"AB", "string bytes must follow length");
assert_eq!(enc.len(), 4);
}
#[test]
fn test_v2_struct_roundtrip_all_fields() {
let rec = RecordV2 {
id: 999,
name: "Frank".into(),
age: 35,
};
let enc = encode_to_vec(&rec).expect("encode v2");
let (dec, consumed): (RecordV2, usize) = decode_from_slice(&enc).expect("decode v2");
assert_eq!(rec, dec);
assert_eq!(consumed, enc.len());
}
#[test]
fn test_v1_and_v2_share_common_prefix_bytes() {
let v1 = RecordV1 {
id: 5,
name: "Grace".into(),
};
let v2 = RecordV2 {
id: 5,
name: "Grace".into(),
age: 0,
};
let enc_v1 = encode_to_vec(&v1).expect("encode v1");
let enc_v2 = encode_to_vec(&v2).expect("encode v2");
assert!(
enc_v2.starts_with(&enc_v1),
"V2 encoding must start with the same bytes as V1"
);
assert!(enc_v2.len() > enc_v1.len());
}
#[derive(Debug, PartialEq, Encode, Decode)]
enum EventV1 {
Created,
Updated,
}
#[derive(Debug, PartialEq, Encode, Decode)]
enum EventV2 {
Created,
Updated,
Deleted, }
#[test]
fn test_enum_old_variants_encode_decode_stable() {
let created = EventV1::Created;
let updated = EventV1::Updated;
let enc_c = encode_to_vec(&created).expect("encode Created");
let enc_u = encode_to_vec(&updated).expect("encode Updated");
let v2_created = EventV2::Created;
let v2_updated = EventV2::Updated;
let enc_v2_c = encode_to_vec(&v2_created).expect("encode V2 Created");
let enc_v2_u = encode_to_vec(&v2_updated).expect("encode V2 Updated");
assert_eq!(
enc_c, enc_v2_c,
"Created must have same encoding in V1 and V2"
);
assert_eq!(
enc_u, enc_v2_u,
"Updated must have same encoding in V1 and V2"
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VersionedFrame {
version: u8,
payload_len: u32,
}
#[test]
fn test_version_constant_as_first_byte_in_frame() {
let frame = VersionedFrame {
version: WIRE_VERSION_V2,
payload_len: 128,
};
let enc = encode_to_vec(&frame).expect("encode frame");
assert_eq!(enc[0], WIRE_VERSION_V2);
let (dec, _): (VersionedFrame, usize) = decode_from_slice(&enc).expect("decode frame");
assert_eq!(dec, frame);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct Address {
city: String,
zip: u32,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct PersonV1 {
name: String,
address: Address,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct OrganizationV1 {
org_name: String,
leader: PersonV1,
}
#[test]
fn test_deeply_nested_struct_roundtrip() {
let org = OrganizationV1 {
org_name: "COOLJAPAN OU".into(),
leader: PersonV1 {
name: "Kitasan".into(),
address: Address {
city: "Tallinn".into(),
zip: 10115,
},
},
};
let enc = encode_to_vec(&org).expect("encode nested org");
let (dec, consumed): (OrganizationV1, usize) =
decode_from_slice(&enc).expect("decode nested org");
assert_eq!(org, dec);
assert_eq!(consumed, enc.len());
}
#[test]
fn test_custom_version_tag_mismatch_is_detected() {
let payload = encode_to_vec(&RecordV1 {
id: 77,
name: "Henry".into(),
})
.expect("encode payload");
let mut v2_tagged: Vec<u8> = Vec::with_capacity(1 + payload.len());
v2_tagged.push(WIRE_VERSION_V2);
v2_tagged.extend_from_slice(&payload);
let version_tag = v2_tagged[0];
assert_ne!(version_tag, WIRE_VERSION_V1, "V2 tag must not match V1 tag");
let (rec, _): (RecordV1, usize) =
decode_from_slice(&v2_tagged[1..]).expect("raw payload still valid");
assert_eq!(rec.id, 77);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct OrderedFields {
first: u8,
second: u8,
third: u8,
}
#[test]
fn test_wire_format_field_ordering_matches_declaration() {
let val = OrderedFields {
first: 0xAA,
second: 0xBB,
third: 0xCC,
};
let enc = encode_to_vec(&val).expect("encode ordered fields");
assert_eq!(enc.len(), 3);
assert_eq!(enc[0], 0xAA, "first field at byte 0");
assert_eq!(enc[1], 0xBB, "second field at byte 1");
assert_eq!(enc[2], 0xCC, "third field at byte 2");
}
#[test]
fn test_decode_with_trailing_bytes_consumed_is_encoded_portion_only() {
let rec = RecordV1 {
id: 3,
name: "Ida".into(),
};
let mut enc = encode_to_vec(&rec).expect("encode for trailing test");
let original_len = enc.len();
enc.extend_from_slice(&[0xFF, 0xFE, 0xFD]);
let (dec, consumed): (RecordV1, usize) = decode_from_slice(&enc).expect("decode with trailing");
assert_eq!(dec, rec);
assert_eq!(
consumed, original_len,
"consumed must equal encoded portion only"
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct LargeRecord {
f00: u64,
f01: u64,
f02: u64,
f03: u64,
f04: u64,
f05: u64,
f06: u64,
f07: u64,
f08: String,
f09: String,
f10: bool,
f11: bool,
}
#[test]
fn test_large_struct_many_fields_roundtrip() {
let rec = LargeRecord {
f00: 0,
f01: u64::MAX,
f02: 12345678901234,
f03: 9999999999,
f04: 1,
f05: 2,
f06: 3,
f07: 4,
f08: "field-eight-value".into(),
f09: "field-nine-value".into(),
f10: true,
f11: false,
};
let enc = encode_to_vec(&rec).expect("encode large record");
let (dec, consumed): (LargeRecord, usize) =
decode_from_slice(&enc).expect("decode large record");
assert_eq!(rec, dec);
assert_eq!(consumed, enc.len());
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct RecordWithOpt {
id: u32,
label: Option<String>,
}
#[test]
fn test_option_field_none_and_some_roundtrip() {
let none_rec = RecordWithOpt {
id: 10,
label: None,
};
let some_rec = RecordWithOpt {
id: 20,
label: Some("optlabel".into()),
};
let enc_none = encode_to_vec(&none_rec).expect("encode None option");
let enc_some = encode_to_vec(&some_rec).expect("encode Some option");
let (dec_none, c1): (RecordWithOpt, usize) =
decode_from_slice(&enc_none).expect("decode None option");
let (dec_some, c2): (RecordWithOpt, usize) =
decode_from_slice(&enc_some).expect("decode Some option");
assert_eq!(dec_none, none_rec);
assert_eq!(dec_some, some_rec);
assert_eq!(c1, enc_none.len());
assert_eq!(c2, enc_some.len());
assert!(enc_none.len() < enc_some.len());
}
#[test]
fn test_different_configs_produce_different_byte_sequences() {
let value: u32 = 300;
let varint_cfg = config::standard(); let fixed_cfg = config::standard().with_fixed_int_encoding();
let enc_varint = encode_to_vec_with_config(&value, varint_cfg).expect("encode varint");
let enc_fixed = encode_to_vec_with_config(&value, fixed_cfg).expect("encode fixed");
assert_ne!(
enc_varint, enc_fixed,
"varint and fixed-int configs must produce different bytes for u32=300"
);
assert_eq!(enc_fixed.len(), 4, "fixed u32 must be 4 bytes");
assert!(
enc_varint.len() < 4,
"varint 300 must encode in fewer than 4 bytes (got {})",
enc_varint.len()
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VersionedWrapperV1 {
schema_version: u32,
data: u64,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VersionedWrapperV2 {
schema_version: u32,
data: u64,
}
#[test]
fn test_versioned_wrapper_different_version_tag_produces_different_bytes() {
let w1 = VersionedWrapperV1 {
schema_version: 1,
data: 0xABCD,
};
let w2 = VersionedWrapperV2 {
schema_version: 2,
data: 0xABCD,
};
let enc1 = encode_to_vec(&w1).expect("encode wrapper v1");
let enc2 = encode_to_vec(&w2).expect("encode wrapper v2");
assert_ne!(
enc1, enc2,
"different version tags must yield different bytes"
);
let (d1, _): (VersionedWrapperV1, usize) = decode_from_slice(&enc1).expect("decode wrapper v1");
let (d2, _): (VersionedWrapperV2, usize) = decode_from_slice(&enc2).expect("decode wrapper v2");
assert_eq!(d1.schema_version, 1u32);
assert_eq!(d2.schema_version, 2u32);
assert_eq!(d1.data, d2.data);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct StringVecRecord {
tag: u8,
label: String,
items: Vec<u8>,
}
#[test]
fn test_string_and_vec_fields_at_correct_offsets() {
let rec = StringVecRecord {
tag: 0x05,
label: "XY".into(), items: vec![0x0A, 0x0B, 0x0C], };
let enc = encode_to_vec(&rec).expect("encode string-vec record");
assert_eq!(enc[0], 0x05);
assert_eq!(enc[1], 0x02);
assert_eq!(&enc[2..4], b"XY");
assert_eq!(enc[4], 0x03);
assert_eq!(&enc[5..8], &[0x0A, 0x0B, 0x0C]);
assert_eq!(enc.len(), 8);
}
#[test]
fn test_encode_length_equals_sum_of_field_lengths() {
let id: u32 = 7;
let flag: bool = true;
let name = "LenCheck".to_string();
let enc_id = encode_to_vec(&id).expect("encode id");
let enc_flag = encode_to_vec(&flag).expect("encode flag");
let enc_name = encode_to_vec(&name).expect("encode name");
#[derive(Debug, PartialEq, Encode, Decode)]
struct LenCheckRecord {
id: u32,
flag: bool,
name: String,
}
let rec = LenCheckRecord {
id,
flag,
name: name.clone(),
};
let enc_whole = encode_to_vec(&rec).expect("encode whole record");
assert_eq!(
enc_whole.len(),
enc_id.len() + enc_flag.len() + enc_name.len(),
"whole struct length must equal sum of field lengths"
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct ConfigMapV1 {
version: u8,
settings: BTreeMap<String, String>,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct ConfigMapV2 {
version: u8,
settings: BTreeMap<String, String>,
#[oxicode(skip)]
cached_checksum: u32,
}
#[test]
fn test_btreemap_string_keys_version_compat_roundtrip() {
let mut settings_v1 = BTreeMap::new();
settings_v1.insert("timeout".into(), "30".into());
settings_v1.insert("retries".into(), "3".into());
let cfg_v1 = ConfigMapV1 {
version: 1,
settings: settings_v1.clone(),
};
let enc_v1 = encode_to_vec(&cfg_v1).expect("encode ConfigMapV1");
let (dec_v2, consumed): (ConfigMapV2, usize) =
decode_from_slice(&enc_v1).expect("decode V1 bytes as ConfigMapV2");
assert_eq!(dec_v2.version, 1u8);
assert_eq!(dec_v2.settings, settings_v1);
assert_eq!(dec_v2.cached_checksum, 0u32, "skipped field defaults to 0");
assert_eq!(consumed, enc_v1.len());
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct ComplexRecord {
id: u64,
tags: Vec<String>,
score: f64,
active: bool,
meta: BTreeMap<String, u32>,
}
#[test]
fn test_complex_struct_consumed_equals_total_encoded_bytes() {
let mut meta = BTreeMap::new();
meta.insert("priority".into(), 10u32);
meta.insert("weight".into(), 42u32);
let rec = ComplexRecord {
id: 9876543210,
tags: vec!["alpha".into(), "beta".into(), "gamma".into()],
score: std::f64::consts::PI,
active: true,
meta,
};
let enc = encode_to_vec(&rec).expect("encode complex record");
let (dec, consumed): (ComplexRecord, usize) =
decode_from_slice(&enc).expect("decode complex record");
assert_eq!(rec, dec);
assert_eq!(
consumed,
enc.len(),
"consumed bytes must equal total encoded bytes"
);
let fixed_cfg = config::standard().with_fixed_int_encoding();
let enc_fixed = encode_to_vec_with_config(&rec, fixed_cfg).expect("encode fixed-int");
let (dec_fixed, consumed_fixed): (ComplexRecord, usize) =
decode_from_slice_with_config(&enc_fixed, fixed_cfg).expect("decode fixed-int");
assert_eq!(rec, dec_fixed);
assert_eq!(consumed_fixed, enc_fixed.len());
assert!(enc_fixed.len() >= enc.len());
}