use super::types::*;
use tinyklv::dec::binary as decb;
use tinyklv::enc::binary as encb;
use tinyklv::prelude::*;
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
default(typ = Color, dec = Color::decode_value, enc = Color::encode_value),
default(typ = Priority, dec = Priority::decode_value, enc = Priority::encode_value),
)]
struct DefaultTyped {
#[klv(key = 0x01)]
color: Color,
#[klv(key = 0x02)]
priority: Priority,
#[klv(
key = 0x03,
dec = Velocity::decode_value,
enc = Velocity::encode_value,
)]
velocity: Velocity,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultExprColor {
#[klv(
key = 0x01,
dec = Color::decode_value,
enc = Color::encode_value,
default = Color::Red,
)]
color: Color,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultExprTimestamp {
#[klv(
key = 0x01,
dec = Timestamp::decode_value,
enc = Timestamp::encode_value,
default = Timestamp { seconds: 0, nanos: 0 }
)]
timestamp: Timestamp,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
allow_unimplemented_encode,
)]
struct WithNonKlvField {
#[klv(
key = 0x01,
dec = Color::decode_value,
enc = Color::encode_value,
)]
color: Color,
counter: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
enum Flavor {
#[default]
Vanilla,
Chocolate,
Strawberry,
Mint,
}
impl tinyklv::DecodeValue<&[u8]> for Flavor {
fn decode_value(input: &mut &[u8]) -> tinyklv::Result<Self> {
let v = decb::u8(input)?;
match v {
0 => Ok(Flavor::Vanilla),
1 => Ok(Flavor::Chocolate),
2 => Ok(Flavor::Strawberry),
3 => Ok(Flavor::Mint),
_ => Err(winnow::error::ParserError::from_input(input)),
}
}
}
impl tinyklv::EncodeValue<Vec<u8>> for Flavor {
fn encode_value(&self) -> Vec<u8> {
let v = match self {
Flavor::Vanilla => 0_u8,
Flavor::Chocolate => 1,
Flavor::Strawberry => 2,
Flavor::Mint => 3,
};
encb::u8(v)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Calibration {
pub gain: f32,
pub offset: i16,
}
impl Default for Calibration {
fn default() -> Self {
Calibration {
gain: 1.0,
offset: 0,
}
}
}
impl tinyklv::DecodeValue<&[u8]> for Calibration {
fn decode_value(input: &mut &[u8]) -> tinyklv::Result<Self> {
let gain = decb::be_f32(input)?;
let offset = decb::be_i16(input)?;
Ok(Calibration { gain, offset })
}
}
impl tinyklv::EncodeValue<Vec<u8>> for Calibration {
fn encode_value(&self) -> Vec<u8> {
let mut v = encb::be_f32(self.gain);
v.extend(encb::be_i16(self.offset));
v
}
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultBareU32 {
#[klv(
key = 0x01,
dec = decb::be_u32,
enc = *encb::be_u32,
default,
)]
counter: u32,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultBareStruct {
#[klv(
key = 0x01,
dec = Calibration::decode_value,
enc = Calibration::encode_value,
default
)]
cal: Calibration,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultBareEnum {
#[klv(
key = 0x01,
dec = Flavor::decode_value,
enc = Flavor::encode_value,
default
)]
flavor: Flavor,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultBareAndExpr {
#[klv(
key = 0x01,
dec = Flavor::decode_value,
enc = Flavor::encode_value,
default
)]
flavor: Flavor,
#[klv(
key = 0x02,
dec = Calibration::decode_value,
enc = Calibration::encode_value,
default = Calibration { gain: 2.5, offset: -7 }
)]
cal: Calibration,
}
#[derive(Klv, Debug, PartialEq)]
#[klv(
stream = &[u8],
key(dec = decb::u8, enc = encb::u8),
len(dec = decb::u8_as_usize, enc = encb::u8_from_usize),
)]
struct DefaultBareOptionWrapped {
#[klv(
key = 0x01,
dec = Flavor::decode_value,
enc = Flavor::encode_value,
default
)]
flavor: Option<Flavor>,
}
fn tlv(key: u8, value: Vec<u8>) -> Vec<u8> {
let mut out = vec![key, value.len() as u8];
out.extend(value);
out
}
#[test]
fn default_type_color_and_priority() {
let original = DefaultTyped {
color: Color::Alpha,
priority: Priority::Critical,
velocity: Velocity {
dx: 10,
dy: -20,
dz: 5,
},
};
let mut stream: Vec<u8> = Vec::new();
stream.extend(tlv(0x01, original.color.encode_value()));
stream.extend(tlv(0x02, original.priority.encode_value()));
stream.extend(tlv(0x03, original.velocity.encode_value()));
let decoded = DefaultTyped::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(decoded, original, "container-default decode should match");
}
#[test]
fn default_type_roundtrip() {
let original = DefaultTyped {
color: Color::Red,
priority: Priority::Medium,
velocity: Velocity {
dx: 0,
dy: 0,
dz: -1,
},
};
let encoded = original.encode_value();
let decoded = DefaultTyped::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original, "roundtrip via container-level defaults");
}
#[test]
fn default_expr_color_enum_absent() {
let result = DefaultExprColor::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.color,
Color::Red,
"absent key should yield `default = <expr>` value"
);
}
#[test]
fn default_expr_color_enum_present() {
let stream = tlv(0x01, Color::Blue.encode_value());
let result = DefaultExprColor::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(
result.color,
Color::Blue,
"present key should yield decoded value"
);
}
#[test]
fn default_expr_timestamp_struct_absent() {
let zero = Timestamp {
seconds: 0,
nanos: 0,
};
let result = DefaultExprTimestamp::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.timestamp, zero,
"absent key should yield `default = <expr>` Timestamp"
);
}
#[test]
fn default_expr_timestamp_struct_present() {
let ts = Timestamp {
seconds: 1_700_000_000,
nanos: 12_345,
};
let stream = tlv(0x01, ts.encode_value());
let result = DefaultExprTimestamp::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(
result.timestamp, ts,
"present key should yield decoded Timestamp"
);
}
#[test]
fn default_expr_overridden_when_present() {
let stream = tlv(0x01, Color::Blue.encode_value());
let result = DefaultExprColor::decode_value(&mut stream.as_slice()).unwrap();
assert_ne!(
result.color,
Color::Red,
"`default = <expr>` should be overridden by decoded value"
);
assert_eq!(
result.color,
Color::Blue,
"decoded Color::Blue must win over Color::Red default"
);
}
#[test]
fn default_bare_primitive_u32_absent() {
let result = DefaultBareU32::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.counter, 0,
"bare `default` on u32 must yield u32::default() == 0"
);
}
#[test]
fn default_bare_primitive_u32_present() {
let stream = tlv(0x01, encb::be_u32(42));
let result = DefaultBareU32::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(
result.counter, 42,
"present key should override bare `default`"
);
}
#[test]
fn default_bare_custom_struct_absent() {
let result = DefaultBareStruct::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.cal,
Calibration::default(),
"bare `default` must call `<Calibration as Default>::default()`"
);
assert_eq!(result.cal.gain, 1.0);
assert_eq!(result.cal.offset, 0);
}
#[test]
fn default_bare_custom_struct_present() {
let cal = Calibration {
gain: 3.15,
offset: -42,
};
let stream = tlv(0x01, cal.encode_value());
let result = DefaultBareStruct::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(result.cal, cal, "present key overrides bare `default`");
}
#[test]
fn default_bare_enum_with_derive_default_absent() {
let result = DefaultBareEnum::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.flavor,
Flavor::Vanilla,
"bare `default` must yield the `#[default]` variant"
);
}
#[test]
fn default_bare_and_expr_mixed_absent() {
let result = DefaultBareAndExpr::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.flavor,
Flavor::Vanilla,
"bare `default` field -> Default::default()"
);
assert_eq!(
result.cal,
Calibration {
gain: 2.5,
offset: -7
},
"`default = <expr>` field -> inlined expression"
);
}
#[test]
fn default_bare_option_wrapped_absent() {
let result = DefaultBareOptionWrapped::decode_value(&mut [].as_slice()).unwrap();
assert_eq!(
result.flavor,
Some(Flavor::Vanilla),
"Option<Flavor> with bare `default` should be `Some(Flavor::default())`"
);
}
#[test]
fn non_klv_field_default() {
let stream = tlv(0x01, Color::Green.encode_value());
let result = WithNonKlvField::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(
result.color,
Color::Green,
"klv field should decode normally"
);
assert_eq!(
result.counter, 0,
"non-KLV field should be Default::default() = 0"
);
}
#[test]
fn non_klv_field_unaffected_by_unknown_keys() {
let mut stream: Vec<u8> = tlv(0x01, Color::Alpha.encode_value());
stream.extend_from_slice(&[0xFF, 0x02, 0xAB, 0xCD]);
let result = WithNonKlvField::decode_value(&mut stream.as_slice()).unwrap();
assert_eq!(result.color, Color::Alpha);
assert_eq!(
result.counter, 0,
"non-KLV counter must not be affected by unknown keys"
);
}