Skip to content

Commit ccc8284

Browse files
committed
Add a macro to implement serialization of enums using TLVs
1 parent 0e41bba commit ccc8284

File tree

1 file changed

+90
-9
lines changed

1 file changed

+90
-9
lines changed

lightning/src/util/ser_macros.rs

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -325,26 +325,26 @@ macro_rules! read_tlv_fields {
325325
// `Self { ,,vecfield: vecfield }` which is obviously incorrect. Instead, we have to match here to
326326
// detect at least one empty field set and skip the potentially-extra comma.
327327
macro_rules! _init_tlv_based_struct {
328-
({}, {$($field: ident),*}, {$($vecfield: ident),*}) => {
329-
Ok(Self {
328+
($($type: ident)::*, {}, {$($field: ident),*}, {$($vecfield: ident),*}) => {
329+
Ok($($type)::* {
330330
$($field),*,
331331
$($vecfield: $vecfield.unwrap().0),*
332332
})
333333
};
334-
({$($reqfield: ident),*}, {}, {$($vecfield: ident),*}) => {
335-
Ok(Self {
334+
($($type: ident)::*, {$($reqfield: ident),*}, {}, {$($vecfield: ident),*}) => {
335+
Ok($($type)::* {
336336
$($reqfield: $reqfield.0.unwrap()),*,
337337
$($vecfield: $vecfield.unwrap().0),*
338338
})
339339
};
340-
({$($reqfield: ident),*}, {$($field: ident),*}, {}) => {
341-
Ok(Self {
340+
($($type: ident)::*, {$($reqfield: ident),*}, {$($field: ident),*}, {}) => {
341+
Ok($($type)::* {
342342
$($reqfield: $reqfield.0.unwrap()),*,
343343
$($field),*
344344
})
345345
};
346-
({$($reqfield: ident),*}, {$($field: ident),*}, {$($vecfield: ident),*}) => {
347-
Ok(Self {
346+
($($type: ident)::*, {$($reqfield: ident),*}, {$($field: ident),*}, {$($vecfield: ident),*}) => {
347+
Ok($($type)::* {
348348
$($reqfield: $reqfield.0.unwrap()),*,
349349
$($field),*,
350350
$($vecfield: $vecfield.unwrap().0),*
@@ -433,7 +433,88 @@ macro_rules! impl_writeable_tlv_based {
433433
}, {
434434
$(($vectype, $vecfield)),*
435435
});
436-
_init_tlv_based_struct!({$($reqfield),*}, {$($field),*}, {$($vecfield),*})
436+
_init_tlv_based_struct!($st, {$($reqfield),*}, {$($field),*}, {$($vecfield),*})
437+
}
438+
}
439+
}
440+
}
441+
442+
/// Implement Readable and Writeable for an enum, with struct variants stored as TLVs and tuple
443+
/// variants stored directly.
444+
/// The format is, for example
445+
/// impl_writeable_tlv_based_enum!(EnumName,
446+
/// (0, StructVariantA) => {(0, variant_field)}, {(1, variant_optional_field)}, {},
447+
/// (1, StructVariantB) => {(0, variant_field_a), (1, variant_field_b)}, {}, {(2, variant_vec_field)};
448+
/// (2, TupleVariantA), (3, TupleVariantB),
449+
/// );
450+
/// The type is written as a single byte, followed by any variant data.
451+
/// Attempts to read an unknown type byte result in DecodeError::UnknownRequiredFeature.
452+
macro_rules! impl_writeable_tlv_based_enum {
453+
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
454+
{$(($reqtype: expr, $reqfield: ident)),* $(,)*},
455+
{$(($type: expr, $field: ident)),* $(,)*},
456+
{$(($vectype: expr, $vecfield: ident)),* $(,)*}
457+
),* $(,)*;
458+
$(($tuple_variant_id: expr, $tuple_variant_name: ident)),* $(,)*) => {
459+
impl ::util::ser::Writeable for $st {
460+
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
461+
match self {
462+
$($st::$variant_name { $(ref $reqfield),* $(ref $field),*, $(ref $vecfield),* } => {
463+
let id: u8 = $variant_id;
464+
id.write(writer)?;
465+
_write_tlv_fields!(writer, {
466+
$(($reqtype, $reqfield)),*
467+
}, {
468+
$(($type, $field)),*
469+
}, {
470+
$(($vectype, Some(::util::ser::VecWriteWrapper(&$vecfield)))),*
471+
});
472+
}),*
473+
$($st::$tuple_variant_name (ref field) => {
474+
let id: u8 = $tuple_variant_id;
475+
id.write(writer)?;
476+
field.write(writer)?;
477+
}),*
478+
}
479+
Ok(())
480+
}
481+
}
482+
483+
impl ::util::ser::Readable for $st {
484+
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
485+
let id: u8 = ::util::ser::Readable::read(reader)?;
486+
match id {
487+
$($variant_id => {
488+
// Because read_tlv_fields creates a labeled loop, we cannot call it twice
489+
// in the same function body. Instead, we define a closure and call it.
490+
let f = || {
491+
$(
492+
let mut $reqfield = ::util::ser::OptionDeserWrapper(None);
493+
)*
494+
$(
495+
let mut $field = None;
496+
)*
497+
$(
498+
let mut $vecfield = Some(::util::ser::VecReadWrapper(Vec::new()));
499+
)*
500+
_read_tlv_fields!(reader, {
501+
$(($reqtype, $reqfield)),*
502+
}, {
503+
$(($type, $field)),*
504+
}, {
505+
$(($vectype, $vecfield)),*
506+
});
507+
_init_tlv_based_struct!($st::$variant_name, {$($reqfield),*}, {$($field),*}, {$($vecfield),*})
508+
};
509+
f()
510+
}),*
511+
$($tuple_variant_id => {
512+
Ok($st::$tuple_variant_name(Readable::read(reader)?))
513+
}),*
514+
_ => {
515+
Err(DecodeError::UnknownRequiredFeature)?
516+
},
517+
}
437518
}
438519
}
439520
}

0 commit comments

Comments
 (0)