use super::types::*;
use tinyklv::dec::binary as decb;
use tinyklv::enc::binary as encb;
use tinyklv::prelude::*;
use tinyklv::Klv;
#[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 SensorModule {
#[klv(
key = 0x01,
dec = SensorReading::decode_value,
enc = SensorReading::encode_value,
)]
reading: SensorReading,
#[klv(
key = 0x02,
dec = Color::decode_value,
enc = Color::encode_value,
)]
indicator: 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 Platform {
#[klv(
key = 0x01,
dec = decb::be_u16,
enc = *encb::be_u16,
)]
id: u16,
#[klv(
key = 0x02,
dec = SensorModule::decode_value,
enc = SensorModule::encode_value,
)]
sensor: SensorModule,
#[klv(
key = 0x03,
dec = Coordinate::decode_value,
enc = Coordinate::encode_value,
)]
position: Coordinate,
}
#[test]
fn nested_klv_derived_roundtrip() {
let original = Platform {
id: 42,
sensor: SensorModule {
reading: SensorReading {
kind: SensorKind::Temperature,
value: 23.5,
},
indicator: Color::Green,
},
position: Coordinate {
lat: 48.8566,
lon: 2.3522,
},
};
let encoded = original.encode_value();
let decoded = Platform::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn nested_klv_derived_roundtrip_extreme_values() {
let original = Platform {
id: u16::MAX,
sensor: SensorModule {
reading: SensorReading {
kind: SensorKind::Vibration,
value: f32::MAX,
},
indicator: Color::Unknown(0xDEAD),
},
position: Coordinate {
lat: -90.0,
lon: -180.0,
},
};
let encoded = original.encode_value();
let decoded = Platform::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn nested_klv_derived_roundtrip_zero_values() {
let original = Platform {
id: 0,
sensor: SensorModule {
reading: SensorReading {
kind: SensorKind::Pressure,
value: 0.0,
},
indicator: Color::Red,
},
position: Coordinate { lat: 0.0, lon: 0.0 },
};
let encoded = original.encode_value();
let decoded = Platform::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[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 Core {
#[klv(
key = 0x01,
dec = decb::be_u32,
enc = *encb::be_u32,
)]
value: u32,
}
fn encode_core(v: &Core) -> Vec<u8> {
v.encode_value()
}
#[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 Module {
#[klv(
key = 0x01,
dec = Core::decode_value,
enc = encode_core,
)]
core: Core,
#[klv(
key = 0x02,
dec = Color::decode_value,
enc = Color::encode_value,
)]
color: Color,
}
fn encode_module(v: &Module) -> Vec<u8> {
v.encode_value()
}
#[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 System {
#[klv(
key = 0x01,
dec = Module::decode_value,
enc = encode_module,
)]
module: Module,
#[klv(
key = 0x02,
dec = Timestamp::decode_value,
enc = Timestamp::encode_value,
)]
timestamp: Timestamp,
}
#[test]
fn nested_two_deep() {
let original = System {
module: Module {
core: Core { value: 0xCAFE_BABE },
color: Color::Blue,
},
timestamp: Timestamp {
seconds: 1_700_000_000,
nanos: 500,
},
};
let encoded = original.encode_value();
let decoded = System::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn nested_two_deep_min_values() {
let original = System {
module: Module {
core: Core { value: 0 },
color: Color::Alpha,
},
timestamp: Timestamp {
seconds: 0,
nanos: 0,
},
};
let encoded = original.encode_value();
let decoded = System::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn nested_two_deep_max_values() {
let original = System {
module: Module {
core: Core { value: u32::MAX },
color: Color::Unknown(u16::MAX),
},
timestamp: Timestamp {
seconds: u32::MAX,
nanos: u16::MAX,
},
};
let encoded = original.encode_value();
let decoded = System::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[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 PlatformOptional {
#[klv(
key = 0x01,
dec = decb::be_u16,
enc = *encb::be_u16,
)]
id: u16,
#[klv(
key = 0x02,
dec = SensorModule::decode_value,
enc = SensorModule::encode_value,
)]
sensor: Option<SensorModule>,
}
#[test]
fn nested_optional_sensor_present() {
let original = PlatformOptional {
id: 7,
sensor: Some(SensorModule {
reading: SensorReading {
kind: SensorKind::Humidity,
value: 55.0,
},
indicator: Color::Alpha,
}),
};
let encoded = original.encode_value();
let decoded = PlatformOptional::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
assert!(decoded.sensor.is_some());
}
#[test]
fn nested_optional_sensor_absent() {
let original = PlatformOptional {
id: 99,
sensor: None,
};
let encoded = original.encode_value();
let decoded = PlatformOptional::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
assert!(decoded.sensor.is_none());
}
#[test]
fn nested_optional_roundtrip_toggle() {
let with_sensor = PlatformOptional {
id: 1,
sensor: Some(SensorModule {
reading: SensorReading {
kind: SensorKind::Temperature,
value: -10.0,
},
indicator: Color::Green,
}),
};
let without_sensor = PlatformOptional {
id: 2,
sensor: None,
};
let enc_with = with_sensor.encode_value();
let enc_without = without_sensor.encode_value();
assert_ne!(enc_with, enc_without);
assert_eq!(
PlatformOptional::decode_value(&mut enc_with.as_slice()).unwrap(),
with_sensor
);
assert_eq!(
PlatformOptional::decode_value(&mut enc_without.as_slice()).unwrap(),
without_sensor
);
}
#[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 StatusInner {
#[klv(
key = 0x01,
dec = Color::decode_value,
enc = Color::encode_value,
)]
color: Color,
#[klv(
key = 0x02,
dec = Priority::decode_value,
enc = Priority::encode_value,
)]
priority: Priority,
}
fn encode_status_inner(v: &StatusInner) -> Vec<u8> {
v.encode_value()
}
#[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 StatusOuter {
#[klv(
key = 0x01,
dec = StatusInner::decode_value,
enc = encode_status_inner,
)]
status: StatusInner,
#[klv(
key = 0x02,
dec = Velocity::decode_value,
enc = Velocity::encode_value,
)]
velocity: Velocity,
}
#[test]
fn nested_with_enum_field() {
let original = StatusOuter {
status: StatusInner {
color: Color::Red,
priority: Priority::Critical,
},
velocity: Velocity {
dx: 100,
dy: -50,
dz: 0,
},
};
let encoded = original.encode_value();
let decoded = StatusOuter::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn nested_with_enum_field_all_variants() {
let cases = [
(Color::Red, Priority::Low),
(Color::Green, Priority::Medium),
(Color::Blue, Priority::High),
(Color::Alpha, Priority::Critical),
(Color::Unknown(0x00FF), Priority::Low),
];
for (color, priority) in cases {
let original = StatusOuter {
status: StatusInner { color, priority },
velocity: Velocity {
dx: 1,
dy: 2,
dz: 3,
},
};
let encoded = original.encode_value();
let decoded = StatusOuter::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}
}
#[test]
fn nested_with_enum_field_extreme_velocity() {
let original = StatusOuter {
status: StatusInner {
color: Color::Blue,
priority: Priority::High,
},
velocity: Velocity {
dx: i16::MIN,
dy: i16::MAX,
dz: 0,
},
};
let encoded = original.encode_value();
let decoded = StatusOuter::decode_value(&mut encoded.as_slice()).unwrap();
assert_eq!(decoded, original);
}