+//! Some macros that implement [`Readable`]/[`Writeable`] traits for lightning messages.
+//! They also handle serialization and deserialization of TLVs.
+//!
+//! [`Readable`]: crate::util::ser::Readable
+//! [`Writeable`]: crate::util::ser::Writeable
+
+// There are quite a few TLV serialization "types" which behave differently. We currently only
+// publicly document the `optional` and `required` types, not supporting anything else publicly and
+// changing them at will.
+//
+// Some of the other types include:
+// * (default_value, $default) - reads optionally, reading $default if no TLV is present
+// * (static_value, $value) - ignores any TLVs, always using $value
+// * required_vec - reads into a Vec without a length prefix, failing if no TLV is present.
+// * optional_vec - reads into an Option<Vec> without a length prefix, continuing if no TLV is
+// present. Writes from a Vec directly, only if any elements are present. Note
+// that the struct deserialization macros return a Vec, not an Option.
+// * upgradable_option - reads via MaybeReadable.
+// * upgradable_required - reads via MaybeReadable, requiring a TLV be present but may return None
+// if MaybeReadable::read() returns None.
+
+/// Implements serialization for a single TLV record.
+/// This is exported for use by other exported macros, do not use directly.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _encode_tlv {
+ ($stream: expr, $type: expr, $field: expr, (default_value, $default: expr)) => {
+ $crate::_encode_tlv!($stream, $type, $field, required)
+ };
+ ($stream: expr, $type: expr, $field: expr, (static_value, $value: expr)) => {
+ let _ = &$field; // Ensure we "use" the $field
+ };
+ ($stream: expr, $type: expr, $field: expr, required) => {
+ BigSize($type).write($stream)?;
+ BigSize($field.serialized_length() as u64).write($stream)?;
+ $field.write($stream)?;
+ };
+ ($stream: expr, $type: expr, $field: expr, required_vec) => {
+ $crate::_encode_tlv!($stream, $type, $crate::util::ser::WithoutLength(&$field), required);
+ };
+ ($stream: expr, $optional_type: expr, $optional_field: expr, option) => {
+ if let Some(ref field) = $optional_field {
+ BigSize($optional_type).write($stream)?;
+ BigSize(field.serialized_length() as u64).write($stream)?;
+ field.write($stream)?;
+ }
+ };
+ ($stream: expr, $type: expr, $field: expr, optional_vec) => {
+ if !$field.is_empty() {
+ $crate::_encode_tlv!($stream, $type, $field, required_vec);
+ }
+ };
+ ($stream: expr, $type: expr, $field: expr, upgradable_required) => {
+ $crate::_encode_tlv!($stream, $type, $field, required);
+ };
+ ($stream: expr, $type: expr, $field: expr, upgradable_option) => {
+ $crate::_encode_tlv!($stream, $type, $field, option);
+ };
+ ($stream: expr, $type: expr, $field: expr, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
+ $crate::_encode_tlv!($stream, $type, $field.map(|f| $encoding(f)), option);
+ };
+ ($stream: expr, $type: expr, $field: expr, (option, encoding: $fieldty: ty)) => {
+ $crate::_encode_tlv!($stream, $type, $field, option);
+ };
+ ($stream: expr, $type: expr, $field: expr, (option: $trait: ident $(, $read_arg: expr)?)) => {
+ // Just a read-mapped type
+ $crate::_encode_tlv!($stream, $type, $field, option);
+ };
+}
+
+/// Panics if the last seen TLV type is not numerically less than the TLV type currently being checked.
+/// This is exported for use by other exported macros, do not use directly.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _check_encoded_tlv_order {
+ ($last_type: expr, $type: expr, (static_value, $value: expr)) => { };
+ ($last_type: expr, $type: expr, $fieldty: tt) => {
+ if let Some(t) = $last_type {
+ #[allow(unused_comparisons)] // Note that $type may be 0 making the following comparison always false
+ (debug_assert!(t < $type))
+ }
+ $last_type = Some($type);
+ };
+}
+
+/// Implements the TLVs serialization part in a [`Writeable`] implementation of a struct.
+///
+/// This should be called inside a method which returns `Result<_, `[`io::Error`]`>`, such as
+/// [`Writeable::write`]. It will only return an `Err` if the stream `Err`s or [`Writeable::write`]
+/// on one of the fields `Err`s.
+///
+/// `$stream` must be a `&mut `[`Writer`] which will receive the bytes for each TLV in the stream.
+///
+/// Fields MUST be sorted in `$type`-order.
+///
+/// Note that the lightning TLV requirements require that a single type not appear more than once,
+/// that TLVs are sorted in type-ascending order, and that any even types be understood by the
+/// decoder.
+///
+/// Any `option` fields which have a value of `None` will not be serialized at all.
+///
+/// For example,
+/// ```
+/// # use lightning::encode_tlv_stream;
+/// # fn write<W: lightning::util::ser::Writer> (stream: &mut W) -> Result<(), lightning::io::Error> {
+/// let mut required_value = 0u64;
+/// let mut optional_value: Option<u64> = None;
+/// encode_tlv_stream!(stream, {
+/// (0, required_value, required),
+/// (1, Some(42u64), option),
+/// (2, optional_value, option),
+/// });
+/// // At this point `required_value` has been written as a TLV of type 0, `42u64` has been written
+/// // as a TLV of type 1 (indicating the reader may ignore it if it is not understood), and *no*
+/// // TLV is written with type 2.
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Writeable`]: crate::util::ser::Writeable
+/// [`io::Error`]: crate::io::Error
+/// [`Writeable::write`]: crate::util::ser::Writeable::write
+/// [`Writer`]: crate::util::ser::Writer
+#[macro_export]
+macro_rules! encode_tlv_stream {
+ ($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => { {