lightning_encoding_derive/lib.rs
1// Derive macros for lightning network peer protocol encodings
2//
3// Written in 2020-2022 by
4// Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
5//
6// To the extent possible under law, the author(s) have dedicated all
7// copyright and related and neighboring rights to this software to
8// the public domain worldwide. This software is distributed without
9// any warranty.
10//
11// You should have received a copy of the Apache 2.0 License along with this
12// software. If not, see <https://opensource.org/licenses/Apache-2.0>.
13
14// Coding conventions
15#![recursion_limit = "256"]
16#![deny(dead_code, missing_docs, warnings)]
17
18//! Derivation macros for lightning encoding. To learn more about the lightning
19//! encoding please check [`lightning_encoding`] crate.
20//!
21//! # Derivation macros
22//!
23//! Library exports derivation macros `#[derive(`[`LightningEncode`]`)]` and
24//! `#[derive(`[`LightningDecode`]`)]`, which can be added on top of any
25//! structure you'd like to support lightning-network specific encoding (see
26//! Example section below).
27//!
28//! Encoding/decoding implemented by both of these macros may be configured at
29//! type and individual field level using `#[lightning_encoding(...)]` attribute
30//!
31//! # Attribute
32//!
33//! [`LightningEncode`] and [`LightningDecode`] behavior can be customed with
34//! `#[lightning_encoding(...)]` attribute, which accepts different arguments
35//! depending to which part of the data type it is applied.
36//!
37//! ## Attribute arguments at type declaration level
38//!
39//! Derivation macros accept `#[lightning_encoding()]` attribute with the
40//! following arguments:
41//!
42//! ### `crate = ::path::to::lightning_encoding_crate`
43//!
44//! Allows to specify custom path to `lightning_encoding` crate
45//!
46//! ### `use_tlv`
47//!
48//! Applies TLV extension to the data type and allows use of `tlv` and
49//! `unknown_tlvs` arguments on struct fields.
50//!
51//! NB: TLVs work only with structures and not enums.
52//!
53//! ### `repr = <uint>`
54//!
55//! Can be used with enum types only.
56//!
57//! Specifies which unsigned integer type must represent enum variants during
58//! the encoding. Possible values are `biging`, `u8`, `u16`, `u32` and `u64`.
59//!
60//! NB: This argument is not equal to the rust `#[repr(...)]` attribute, which
61//! defines C FFI representation of the enum type. For their combined usage
62//! pls check examples below
63//!
64//! ### `bu_order`/`by_velue`
65//!
66//! Can be used with enum types only, where they define which encoding strategy
67//! should be used for representation of enum variants:
68//! - `by_value` - encodes enum variants using their value representation (see
69//! `repr` above)
70//! - `by_order` - encodes enum variants by their ordinal position starting from
71//! zero. Can't be combined with `by_value`.
72//!
73//! If neither of these two arguments is provided, the macro defaults to
74//! `by_order` encoding.
75//!
76//!
77//! ## Attribute arguments at field and enum variant level
78//!
79//! Derivation macros accept `#[lightning_encoding()]` attribute with the
80//! following arguments
81//!
82//! ### `skip`
83//!
84//! Skips field during serialization and initialize field value with
85//! `Default::default()` on type deserialization.
86//!
87//! Allowed only for named and unnamed (tuple) structure fields and enum variant
88//! associated value fields.
89//!
90//! ### `tlv = <unsigned 16-bit int>`
91//!
92//! Sets the TLV type id for the field. The field type MUST be `Option` and
93//! it must implement `Default`.
94//!
95//! ### `unknown_tlvs`
96//!
97//! Specifies structure field which will be "capture all" for unknown odd TLV
98//! ids. The argument can be used only for a single field within a structure and
99//! the field type must be `BTreeMap<usize, Box<[u8]>]`.
100//!
101//! NB: if an unknown even TLV type id is met, error is raised and the value
102//! does not get into the field.
103//!
104//! ### `value = <unsigned integer>`
105//!
106//! Allowed only for enum variants.
107//!
108//! Assigns custom value for a given enum variant, overriding `by_value` and
109//! `by_order` directives defined at type level and the actual variant value, if
110//! any.
111//!
112//! NB: If the value conflicts with the values of other enum variants, taken
113//! from either their assigned value (for `by_value`-encoded enums), order
114//! index (for `by_order`-encoded enums) or other variant's value from with
115//! explicit `value` argument the compiler will error.
116//!
117//!
118//! # Examples
119//!
120//! ```
121//! # #[macro_use] extern crate lightning_encoding_derive;
122//! use lightning_encoding::LightningEncode;
123//!
124//! // All variants have custom values apart from the first one, which should has
125//! // value = 1
126//! #[derive(LightningEncode, LightningDecode)]
127//! #[lightning_encoding(by_value, repr = u32)]
128//! #[repr(u8)]
129//! enum CustomValues {
130//! Bit8 = 1,
131//!
132//! #[lightning_encoding(value = 0x10)]
133//! Bit16 = 2,
134//!
135//! #[lightning_encoding(value = 0x1000)]
136//! Bit32 = 4,
137//!
138//! #[lightning_encoding(value = 0x100000)]
139//! Bit64 = 8,
140//! }
141//!
142//! assert_eq!(CustomValues::Bit8.lightning_serialize(), Ok(vec![0x00, 0x00, 0x00, 0x01]));
143//! assert_eq!(CustomValues::Bit16.lightning_serialize(), Ok(vec![0x00, 0x00, 0x00, 0x10]));
144//! assert_eq!(CustomValues::Bit32.lightning_serialize(), Ok(vec![0x00, 0x00, 0x10, 0x00]));
145//! assert_eq!(CustomValues::Bit64.lightning_serialize(), Ok(vec![0x00, 0x10, 0x00, 0x00]));
146//! ```
147//!
148//! ```
149//! # #[macro_use] extern crate lightning_encoding_derive;
150//! use lightning_encoding::LightningEncode;
151//!
152//! #[derive(LightningEncode, LightningDecode)]
153//! #[lightning_encoding(by_order)]
154//! enum U16 {
155//! Bit8 = 1, // this will be encoded as 0x0000, since we use `by_order` here
156//! Bit16 = 2,
157//! Bit32 = 4,
158//! Bit64 = 8,
159//! }
160//!
161//! assert_eq!(U16::Bit8.lightning_serialize(), Ok(vec![0x00]));
162//! assert_eq!(U16::Bit16.lightning_serialize(), Ok(vec![0x01]));
163//! assert_eq!(U16::Bit32.lightning_serialize(), Ok(vec![0x02]));
164//! assert_eq!(U16::Bit64.lightning_serialize(), Ok(vec![0x03]));
165//! ```
166//!
167//! ```
168//! # #[macro_use] extern crate lightning_encoding_derive;
169//! use lightning_encoding::{LightningDecode, LightningEncode};
170//!
171//! #[derive(LightningEncode, LightningDecode)]
172//! struct Skipping {
173//! pub data: Vec<u8>,
174//!
175//! // This will initialize the field upon decoding with Option::default()
176//! // value (i.e. `None`)
177//! #[lightning_encoding(skip)]
178//! pub ephemeral: Option<bool>,
179//! }
180//!
181//! let obj = Skipping {
182//! data: b"abc".to_vec(),
183//! ephemeral: Some(true),
184//! };
185//! let ser = obj.lightning_serialize().unwrap();
186//!
187//! assert_eq!(ser, vec![0x00, 0x03, b'a', b'b', b'c']);
188//! let de = Skipping::lightning_deserialize(&ser).unwrap();
189//! assert_eq!(de.ephemeral, None);
190//! assert_eq!(obj.data, de.data);
191//! ```
192//!
193//! [`lightning_encoding`]: https://docs.rs/lightning_encoding
194
195extern crate proc_macro;
196#[macro_use]
197extern crate syn;
198#[macro_use]
199extern crate amplify_syn;
200
201use encoding_derive_helpers::{decode_derive, encode_derive};
202use proc_macro::TokenStream;
203use syn::DeriveInput;
204
205/// Derives [`LightningEncode`] implementation for the type.
206#[proc_macro_derive(LightningEncode, attributes(lightning_encoding))]
207pub fn derive_lightning_encode(input: TokenStream) -> TokenStream {
208 let derive_input = parse_macro_input!(input as DeriveInput);
209 encode_derive(
210 "lightning_encoding",
211 ident!(lightning_encoding),
212 ident!(LightningEncode),
213 ident!(lightning_encode),
214 ident!(lightning_serialize),
215 derive_input,
216 true,
217 )
218 .unwrap_or_else(|e| e.to_compile_error())
219 .into()
220}
221
222/// Derives [`LightningDecode`] implementation for the type.
223#[proc_macro_derive(LightningDecode, attributes(lightning_encoding))]
224pub fn derive_lightning_decode(input: TokenStream) -> TokenStream {
225 let derive_input = parse_macro_input!(input as DeriveInput);
226 let tokens = decode_derive(
227 "lightning_encoding",
228 ident!(lightning_encoding),
229 ident!(LightningDecode),
230 ident!(lightning_decode),
231 ident!(lightning_deserialize),
232 derive_input,
233 true,
234 )
235 .unwrap_or_else(|e| e.to_compile_error());
236 eprintln!("{tokens}");
237 tokens.into()
238}