binary_util_derive/lib.rs
1//! This crate provides a set of proc-macros that can be used to derive traits from `binary-util`.
2//! This crate is not intended to be used directly, and is only used as a dependency for `binary-util`.
3//! If you are looking for a way to implement the `Reader` and `Writer` traits, please see the documentation for `binary-util`.
4use proc_macro::TokenStream;
5use syn::{parse_macro_input, DeriveInput};
6
7mod io;
8mod legacy;
9
10/// **DEPRECATED**.
11/// This is a legacy proc-macro that is used to generate a BufferStream.
12/// It provides an easy way to implement the `Streamable` trait.
13/// > ⚠️ This proc-macro has been deprecated since `0.3.0` in favor of `binary_util::interfaces::Reader` and `binary_util::interfaces::Writer` and will be removed in `0.4.0`.
14///
15/// This proc-macro automatically implements the `Streamable` trait for the struct or enum it is applied to.
16///
17/// Example:
18/// ```ignore
19/// use binary_util::BinaryStream;
20///
21/// #[derive(BinaryStream)]
22/// struct Test {
23/// a: u8,
24/// b: u16
25/// }
26///
27/// fn main() {
28/// let test = Test { a: 0, b: 0 };
29/// test.parse().unwrap();
30/// }
31/// ```
32///
33/// Please note that this proc-macro does not support unit structs or named enum variants, meaning a code sample like the following will not work:
34/// ```warn
35/// use binary_util::BinaryStream;
36///
37/// // Error: Unit structs are not supported.
38/// #[derive(BinaryStream)]
39/// struct Test;
40///
41/// // Error: Invalid variant.
42/// #[derive(BinaryStream)]
43/// enum TestEnum {
44/// B(Test)
45/// }
46/// ```
47/// <p style="background:rgba(255,181,77,0.16);padding:0.75em;border-left: 2px solid orange;">
48/// <strong>Warning:</strong> This module is deprecated and will be removed in <strong>v0.4.0</strong>.
49/// </p>
50#[proc_macro_derive(BinaryStream)]
51#[deprecated(
52 since = "3.0.0",
53 note = "This is a legacy proc-macro that is used to generate the BinaryStream\nDeprecated: use BinaryReader, and BinaryWriter instead."
54)]
55pub fn derive_stream(input: TokenStream) -> TokenStream {
56 // return syn::Error::new_spanned(
57 // // parse_macro_input!(input as DeriveInput),
58 // quote!{},
59 // "This is a legacy proc-macro that is used to generate the BinaryStream\nDeprecated: use BinaryReader, and BinaryWriter instead."
60 // ).to_compile_error().into();
61 legacy::stream_parse(parse_macro_input!(input as DeriveInput))
62 .unwrap()
63 .into()
64}
65
66/// This proc-macro implements both the `Reader` and `Writer` traits from `binary_util::interfaces`.
67/// It is important to note that not all attributes can be used on all types, and some attributes are exclusive to certain variants.
68///
69/// ## Structs
70/// `BinaryIo` supports both Named, and Unnamed structs. However, this derive macro does not support unit structs.
71/// This macro will encode/decode the fields of the struct in the order they are defined, as long as they are not skipped;
72/// however as an additional requirement, each field **MUST** implement** the `Reader` and `Writer` traits, if they do not, this macro will fail.
73///
74/// **Example:**
75/// The following example will provide both a `Reader` and `Writer` implementation for the struct `ABC`, where each field is encoded as it's respective
76/// type to the `Bytewriter`/`Bytereader`.
77/// ```ignore
78/// use binary_util::interfaces::{Reader, Writer};
79/// use binary_util::BinaryIo;
80///
81/// #[derive(BinaryIo, Debug)]
82/// struct ABC {
83/// a: u8,
84/// b: Option<u8>,
85/// c: u8,
86/// }
87///```
88///
89/// Sometimes it can be more optimal to use Unnamed fields, if you do not care about the field names, and only want to encode/decode the fields in the order they are defined.
90/// The behavior of this macro is the same as the previous example, except the fields are unnamed.
91/// ```ignore
92/// use binary_util::interfaces::{Reader, Writer};
93/// use binary_util::BinaryIo;
94///
95/// #[derive(BinaryIo, Debug)]
96/// struct ABC(u8, Option<u8>, u8);
97/// ```
98/// ---
99///
100/// ## Enums
101/// Enums function a bit differently than structs, and have a few more exclusive attributes that allow you to adjust the behavior of the macro.
102/// Identically to structs, this macro will encode/decode the fields of the enum in the order they are defined, as long as they are not skipped.
103/// > **Note:** Enums require the `#[repr]` attribute to be used, and the `#[repr]` attribute must be a primitive type.
104///
105/// ### Unit Variants
106/// Unit variants are the simplest variant, of an enum and require the `#[repr(usize)]` attribute to be used. <br />
107///
108/// **Example:**
109/// The following example will encode the `ProtcolEnum` enum as a `u8`, where each variant is encoded, by default, starting from 0.
110///
111/// ```ignore
112/// use binary_util::BinaryIo;
113/// use binary_util::{Reader, Writer};
114///
115/// #[derive(BinaryIo, Debug)]
116/// #[repr(u8)]
117/// pub enum ProtocolEnum {
118/// Basic,
119/// Advanced,
120/// Complex
121/// }
122/// ```
123///
124/// ### Unnamed Variants (Tuple)
125/// Unnamed variants allow you to encode the enum with a byte header specified by the discriminant. <br />
126/// However, this variant is limited to the same functionality as a struct. The containing data of each field
127/// within the variant must implement the `Reader` and `Writer` traits. Otherwise, this macro will fail with an error.
128///
129/// **Example:**
130/// The following example makes use of Unnamed variants, in this case `A` to encode both `B` and `C` retrospectively.
131/// Where `A::JustC` will be encoded as `0x02` with the binary data of struct `B`.
132/// ```ignore
133/// use binary_util::BinaryIo;
134/// use binary_util::{Reader, Writer};
135///
136/// #[derive(BinaryIo, Debug)]
137/// pub struct B {
138/// foo: String,
139/// bar: Vec<u8>
140/// }
141///
142/// #[derive(BinaryIo, Debug)]
143/// pub struct C {
144/// foobar: u32,
145/// }
146///
147/// #[derive(BinaryIo, Debug)]
148/// #[repr(u8)]
149/// pub enum A {
150/// JustB(B) = 1,
151/// JustC(C), // 2
152/// Both(B, C) // 3
153/// }
154///
155/// fn main() {
156/// let a = A::JustC(C { foobar: 4 });
157/// let buf = a.write_to_bytes().unwrap();
158///
159/// assert_eq!(buf, &[2, 4, 0, 0, 0]);
160/// }
161/// ```
162///
163/// ---
164///
165/// ## Attributes
166/// Structs and enums have a few exclusive attributes that can be used to control the encoding/decoding of the struct. <br />
167/// These attributes control and modify the behavior of the `BinaryIo` macro.
168/// <br /><br />
169///
170/// ### Skip
171/// The `#[skip]` attribute does as the name implies, and can be used to skip a field when encoding/decoding. <br />
172///
173/// **Syntax:**
174/// ```ignore
175/// #[skip]
176/// ```
177///
178/// **Compatibility:**
179/// - ✅ Named Structs
180/// - ✅ Unnamed Structs
181/// - ✅ Enums
182///
183/// **Example:**
184/// ```ignore
185/// use binary_util::interfaces::{Reader, Writer};
186/// use binary_util::BinaryIo;
187///
188/// #[derive(BinaryIo, Debug)]
189/// struct ABC {
190/// a: u8,
191/// #[skip]
192/// b: Option<u8>,
193/// c: u8
194/// }
195/// ```
196///
197/// ### Require
198/// This attribute explicitly requires a field to be present when either encoding, or decoding; and will fail if the field is not present. <br />
199/// This can be useful if you want to ensure that an optional field is present when encoding, or decoding it.
200///
201/// **Syntax:**
202/// ```ignore
203/// #[require(FIELD)]
204/// ```
205///
206/// **Compatibility:**
207/// - ✅ Named Structs
208/// - ❌ Unnamed Structs
209/// - ❌ Enums
210///
211/// **Example:**
212/// In the following example, `b` is explicitly required to be present when encoding, or decoding `ABC`, and it's value is not allowed to be `None`.
213/// ```ignore
214/// use binary_util::interfaces::{Reader, Writer};
215/// use binary_util::BinaryIo;
216///
217/// #[derive(BinaryIo, Debug)]
218/// struct ABC {
219/// a: u8,
220/// b: Option<u8>,
221/// #[require(b)]
222/// c: Option<u8>
223/// }
224/// ```
225///
226/// ### If Present
227/// This attribute functions identically to `#[require]`, however it does not fail if the field is not present.
228///
229/// ### Satisfy
230/// This attribute will fail if the expression provided does not evaluate to `true`. <br />
231/// This attribute can be used to ensure that a field is only encoded/decoded if a certain condition is met.
232/// This can be useful if you're sending something like `Authorization` or `Authentication` packets, and you want to ensure that the client is authenticated before
233/// sending the packet.
234///
235/// **Syntax:**
236///
237/// ```ignore
238/// #[satisfy(EXPR)]
239/// ```
240/// **Compatibility:**
241/// - ✅ Named Structs
242/// - ❌ Unnamed Structs
243/// - ❌ Enums
244///
245/// **Example:**
246/// ```ignore
247/// #[derive(BinaryIo, Debug)]
248/// struct ABC {
249/// a: u8,
250/// #[satisfy(self.a == 10)]
251/// b: Option<u8>,
252/// c: u8,
253/// }
254/// ```
255/// ---
256///
257#[proc_macro_derive(BinaryIo, attributes(skip, require, if_present, satisfy))]
258pub fn derive_binary_io(input: TokenStream) -> TokenStream {
259 io::binary_encoder(input)
260}