+ #[derive(Debug, PartialEq)]
+ struct TestUpgradable {
+ a: u32,
+ b: u32,
+ c: Option<u32>,
+ }
+
+ fn upgradable_tlv_reader(s: &[u8]) -> Result<Option<TestUpgradable>, DecodeError> {
+ let mut s = Cursor::new(s);
+ let mut a = 0;
+ let mut b = 0;
+ let mut c: Option<u32> = None;
+ decode_tlv_stream!(&mut s, {(2, a, upgradable_required), (3, b, upgradable_required), (4, c, upgradable_option)});
+ Ok(Some(TestUpgradable { a, b, c, }))
+ }
+
+ #[test]
+ fn upgradable_tlv_simple_good_cases() {
+ assert_eq!(upgradable_tlv_reader(&<Vec<u8>>::from_hex(
+ concat!("0204deadbeef", "03041bad1dea", "0404deadbeef")
+ ).unwrap()[..]).unwrap(),
+ Some(TestUpgradable { a: 0xdeadbeef, b: 0x1bad1dea, c: Some(0xdeadbeef) }));
+
+ assert_eq!(upgradable_tlv_reader(&<Vec<u8>>::from_hex(
+ concat!("0204deadbeef", "03041bad1dea")
+ ).unwrap()[..]).unwrap(),
+ Some(TestUpgradable { a: 0xdeadbeef, b: 0x1bad1dea, c: None}));
+ }
+
+ #[test]
+ fn missing_required_upgradable() {
+ if let Err(DecodeError::InvalidValue) = upgradable_tlv_reader(&<Vec<u8>>::from_hex(
+ concat!("0100", "0204deadbeef")
+ ).unwrap()[..]) {
+ } else { panic!(); }
+ if let Err(DecodeError::InvalidValue) = upgradable_tlv_reader(&<Vec<u8>>::from_hex(
+ concat!("0100", "03041bad1dea")
+ ).unwrap()[..]) {
+ } else { panic!(); }
+ }
+
+ /// A "V1" enum with only one variant
+ enum InnerEnumV1 {
+ StructVariantA {
+ field: u32,
+ },
+ }
+
+ impl_writeable_tlv_based_enum_upgradable!(InnerEnumV1,
+ (0, StructVariantA) => {
+ (0, field, required),
+ },
+ );
+
+ struct OuterStructOptionalEnumV1 {
+ inner_enum: Option<InnerEnumV1>,
+ other_field: u32,
+ }
+
+ impl_writeable_tlv_based!(OuterStructOptionalEnumV1, {
+ (0, inner_enum, upgradable_option),
+ (2, other_field, required),
+ });
+
+ /// An upgraded version of [`InnerEnumV1`] that added a second variant
+ enum InnerEnumV2 {
+ StructVariantA {
+ field: u32,
+ },
+ StructVariantB {
+ field2: u64,
+ }
+ }
+
+ impl_writeable_tlv_based_enum_upgradable!(InnerEnumV2,
+ (0, StructVariantA) => {
+ (0, field, required),
+ },
+ (1, StructVariantB) => {
+ (0, field2, required),
+ },
+ );
+
+ struct OuterStructOptionalEnumV2 {
+ inner_enum: Option<InnerEnumV2>,
+ other_field: u32,
+ }
+
+ impl_writeable_tlv_based!(OuterStructOptionalEnumV2, {
+ (0, inner_enum, upgradable_option),
+ (2, other_field, required),
+ });
+
+ #[test]
+ fn upgradable_enum_option() {
+ // Test downgrading from `OuterStructOptionalEnumV2` to `OuterStructOptionalEnumV1` and
+ // ensure we still read the `other_field` just fine.
+ let serialized_bytes = OuterStructOptionalEnumV2 {
+ inner_enum: Some(InnerEnumV2::StructVariantB { field2: 64 }),
+ other_field: 0x1bad1dea,
+ }.encode();
+ let mut s = Cursor::new(serialized_bytes);
+
+ let outer_struct: OuterStructOptionalEnumV1 = Readable::read(&mut s).unwrap();
+ assert!(outer_struct.inner_enum.is_none());
+ assert_eq!(outer_struct.other_field, 0x1bad1dea);
+ }
+
+ /// A struct that is read with an [`InnerEnumV1`] but is written with an [`InnerEnumV2`].
+ struct OuterStructRequiredEnum {
+ #[allow(unused)]
+ inner_enum: InnerEnumV1,
+ }
+
+ impl MaybeReadable for OuterStructRequiredEnum {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
+ let mut inner_enum = crate::util::ser::UpgradableRequired(None);
+ read_tlv_fields!(reader, {
+ (0, inner_enum, upgradable_required),
+ });
+ Ok(Some(Self {
+ inner_enum: inner_enum.0.unwrap(),
+ }))
+ }
+ }
+
+ impl Writeable for OuterStructRequiredEnum {
+ fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ write_tlv_fields!(writer, {
+ (0, InnerEnumV2::StructVariantB { field2: 0xdeadbeef }, required),
+ });
+ Ok(())
+ }
+ }
+
+ struct OuterOuterStruct {
+ outer_struct: Option<OuterStructRequiredEnum>,
+ other_field: u32,
+ }
+
+ impl_writeable_tlv_based!(OuterOuterStruct, {
+ (0, outer_struct, upgradable_option),
+ (2, other_field, required),
+ });
+
+
+ #[test]
+ fn upgradable_enum_required() {
+ // Test downgrading from an `OuterOuterStruct` (i.e. test downgrading an
+ // `upgradable_required` `InnerEnumV2` to an `InnerEnumV1`).
+ //
+ // Note that `OuterStructRequiredEnum` has a split write/read implementation that writes an
+ // `InnerEnumV2::StructVariantB` irrespective of the value of `inner_enum`.
+
+ let dummy_inner_enum = InnerEnumV1::StructVariantA { field: 42 };
+ let serialized_bytes = OuterOuterStruct {
+ outer_struct: Some(OuterStructRequiredEnum { inner_enum: dummy_inner_enum }),
+ other_field: 0x1bad1dea,
+ }.encode();
+ let mut s = Cursor::new(serialized_bytes);
+
+ let outer_outer_struct: OuterOuterStruct = Readable::read(&mut s).unwrap();
+ assert!(outer_outer_struct.outer_struct.is_none());
+ assert_eq!(outer_outer_struct.other_field, 0x1bad1dea);
+ }
+