`legacy` fields in TLV stream reads may be used to read fields
which are later moved into some other field using either
`default_value` or `static_value` "reads". This works fine if the
field supports copy semantics, however if it does not, the
accessing of the field in `_decode_and_build` after the TLV stream
read completes but before the struct is built results in a "use
after move" error.
Instead, here, we drop the attempt to hide unused variable warnings
entirely, dropping the post-TLV-stream access to legacy variables,
allowing their use in move semantics for later fields.
As we've grown, we regularly face a question of whether to "break
out" of our nice TLV-based struct/enum reading/writing macros in
order to handle mapping legacy fields to new ones, or deal with
keeping the legacy fields and handling at runtime what should be
hanlded at (de-)serialization time.
This attempts to address this tradeoff by adding support for a
"legacy" TLV type. This TLV style allows us to write a a value
which is not in the struct/enum's layout in memory but can be
calculated from its contents at write-time. The field will also be
read and can be use to calculate other fiels defined with
`static_value` or `default_value`.
It takes a type and a `$write` expression.
They are always read as `option`s to retain a future ability to
remove the `legacy` fields.
Sadly, there's two issues with doing this trivially which force us
into `proc-macro` land:
(a) when matching the original struct we want to list the fields
in the match arm so that we have them available to write.
Sadly, we can't call a macro to have it write out the field
name based on the field type, so instead need to pass the whole
match to a proc-macro and have it walk through to find the
types and skip fields that are `legacy`.
(b) when building a final struct/enum after reading, we need to
list a few `$field: $expr`s and cannot decide whether to
include a field based on a regular macro.
The proc-macros to do so aren't trivial, but they aren't that bad
either. We could instead try to rewrite our TLV stream processing
macros to handle a new set of TLVs which are passed via a separate
argument, but as TLVs are required to in ordered by type this
requires a good chunk of additional generated code in each TLV
write. It also would result in a somewhat less ergonomic callsite
as it would no longer fit into our existing list of TLVs.
Previously, we used the `bdk_macros` dependency for some simple proc
macros in `lightning-transaction-sync`. However, post-1.0 BDK doesn't
further maintain this crate and will at some point probably yank it
together with the old `bdk` crate that was split up.
Here, we create a new crate for utility proc macros and ~~steal~~ add
what we currently use (slightly modified for the latest `syn` version's
API though). In the future we may want to expand this crate, e.g., for
some `maybe_async` macros in the context of an `async KVStore`
implementation.