byte_struct_derive/lib.rs
1//! # Derive macro for Byte Struct
2//!
3//! This crate provides macros for deriving the [`ByteStruct`] trait
4//! defined in the [`byte_struct` crate](https://docs.rs/byte_struct).
5//!
6//! See [`#[derive(ByteStruct)]`](derive.ByteStruct.html) for using the macro.
7//!
8//! [`ByteStruct`]: https://docs.rs/byte_struct/*/byte_struct/trait.ByteStruct.html
9
10#![recursion_limit = "128"]
11extern crate proc_macro;
12
13use crate::proc_macro::TokenStream;
14use proc_macro2::Span;
15use quote::quote;
16
17#[derive(Clone, Copy)]
18enum Endianness {
19 Little,
20 Big,
21 Unspecified,
22}
23
24/// Derives trait [`ByteStruct`] for a data structure.
25///
26/// Requires all members to implement [`ByteStructUnspecifiedByteOrder`].
27/// This includes most primitive types and nested structures with [`ByteStruct`] derived
28/// (because [`ByteStructUnspecifiedByteOrder`] is automatically implemented for [`ByteStruct`] types)
29///
30/// Byte order attributes `#[byte_struct_le]` or `#[byte_struct_be]` can be attached to individual fields
31/// and/or the entire structure.
32///
33/// When a byte order attribute are attached to a field, it selects which byte order version
34/// of [`ByteStructUnspecifiedByteOrder`] member functions to use on the field
35/// In other words, the attribute specifies the byte order on an byte-order-unspecified type.
36/// These attributes have no effect on fields that implements [`ByteStruct`],
37/// because they always have the same byte packing method regardless of externally specified byte order.
38///
39/// When a byte order attribute is attached to the entire struct,
40/// it works as if it is attached to all fields that don't have byte order attributes.
41///
42/// If a field has no byte order attribute specified
43/// (either explicitly attached to the field or implicitly by the attribute on the entire structure),
44/// it must implement [`ByteStruct`] as well, so that its packing method is not byte-order-dependent.
45/// This is true for all `ByteStruct`-derived structures, but not for primitive types.
46///
47/// [`ByteStruct`]: https://docs.rs/byte_struct/*/byte_struct/trait.ByteStruct.html
48/// [`ByteStructUnspecifiedByteOrder`]: https://docs.rs/byte_struct/*/byte_struct/trait.ByteStructUnspecifiedByteOrder.html
49///
50/// ## Example
51/// ```ignore
52/// #[derive(ByteStruct)]
53/// #[byte_struct_le]
54/// struct Struct1 {
55/// // Packed as little-endian.
56/// a: u32,
57///
58/// // Packed as little-endian as well.
59/// // Redundant attributes doesn't hurt.
60/// #[byte_struct_le]
61/// b: i16,
62///
63/// // Packed as big-endian.
64/// // Attributes on fields override top-level attributes.
65/// #[byte_struct_be]
66/// c: u16,
67/// }
68///
69/// // This struct has no top-level byte order attribute
70/// #[derive(ByteStruct)]
71/// struct Struct2 {
72/// // Packed as big-endian.
73/// // If the attribute is missing here, it won't compile.
74/// #[byte_struct_be]
75/// d: i64,
76///
77/// // Packed as little-endian.
78/// #[byte_struct_le]
79/// e: f32,
80/// }
81///
82/// // This struct has no top-level byte order attribute either
83/// #[derive(ByteStruct)]
84/// struct Struct3 {
85/// // Nested structures don't need attributes.
86/// f: Struct1,
87///
88/// // Even if you give one, it has no effect.
89/// // The endianness of fields inside Struct2 still remain as specified above.
90/// #[byte_struct_le]
91/// g: Struct2,
92/// }
93/// ```
94#[proc_macro_derive(ByteStruct, attributes(byte_struct_le, byte_struct_be))]
95pub fn byte_struct_macro_derive(input: TokenStream) -> TokenStream {
96 byte_struct_macro_derive_impl(input, Endianness::Unspecified)
97}
98
99/// Same effect as [`#[derive(ByteStruct)] #[byte_struct_le]`](derive.ByteStruct.html)
100///
101/// But doesn't support byte order attributes on fields
102#[deprecated]
103#[proc_macro_derive(ByteStructLE)]
104pub fn byte_struct_le_macro_derive(input: TokenStream) -> TokenStream {
105 byte_struct_macro_derive_impl(input, Endianness::Little)
106}
107
108/// Same effect as [`#[derive(ByteStruct)] #[byte_struct_be]`](derive.ByteStruct.html)
109///
110/// But doesn't support byte order attributes on fields
111#[deprecated]
112#[proc_macro_derive(ByteStructBE)]
113pub fn byte_struct_be_macro_derive(input: TokenStream) -> TokenStream {
114 byte_struct_macro_derive_impl(input, Endianness::Big)
115}
116
117fn byte_struct_macro_derive_impl(input: TokenStream, endianness_input: Endianness) -> TokenStream {
118 let ast: syn::DeriveInput = syn::parse(input).unwrap();
119
120 let mut found_le = false;
121 let mut found_be = false;
122 for attr in ast.attrs {
123 let syn::Attribute{meta: syn::Meta::Path(syn::Path{segments, ..}), ..} = attr else {continue};
124 if segments.len() != 1 {
125 continue;
126 }
127 match segments[0].ident.to_string().as_str() {
128 "byte_struct_le" => found_le = true,
129 "byte_struct_be" => found_be = true,
130 _ => ()
131 };
132 }
133 if found_be && found_le {
134 panic!("Found conflicting byte_struct_le and byte_struct_be attributes");
135 }
136 let endianness = if found_le {
137 Endianness::Little
138 } else if found_be {
139 Endianness::Big
140 } else {
141 endianness_input
142 };
143
144 let name = &ast.ident;
145 if let syn::Data::Struct(syn::DataStruct{fields: syn::Fields::Named(
146 syn::FieldsNamed{named, ..}), ..}) = ast.data {
147
148 let mut ty0 = Vec::<syn::Type>::new();
149 let mut ident1 = Vec::<syn::Ident>::new();
150 let mut field_endianness = Vec::<Endianness>::new();
151 for n in named {
152 ty0.push(n.ty.clone());
153 ident1.push(n.ident.unwrap().clone());
154 let mut found_le = false;
155 let mut found_be = false;
156 for attr in n.attrs {
157 let syn::Attribute{meta: syn::Meta::Path(syn::Path{segments, ..}), ..} = attr else {continue};
158 if segments.len() != 1 {
159 continue;
160 }
161 match segments[0].ident.to_string().as_str() {
162 "byte_struct_le" => found_le = true,
163 "byte_struct_be" => found_be = true,
164 _ => ()
165 };
166 }
167 if found_be && found_le {
168 panic!("Found conflicting byte_struct_le and byte_struct_be attributes");
169 }
170 if found_be {
171 field_endianness.push(Endianness::Big);
172 } else if found_le {
173 field_endianness.push(Endianness::Little);
174 } else {
175 field_endianness.push(endianness);
176 }
177 }
178
179 let (write_bytes_fn, read_bytes_fn): (Vec<_>, Vec<_>) =
180 field_endianness.iter().map(|e| {
181 let name_str = match e {
182 Endianness::Little => ("write_bytes_default_le", "read_bytes_default_le"),
183 Endianness::Big => ("write_bytes_default_be", "read_bytes_default_be"),
184 Endianness::Unspecified => ("write_bytes", "read_bytes"),
185 };
186 (syn::Ident::new(name_str.0, Span::call_site()),
187 syn::Ident::new(name_str.1, Span::call_site()))
188 }).unzip();
189
190 // quote! seems not liking using the same object twice in the content
191 let ty1 = ty0.clone();
192 let ty2 = ty0.clone();
193 let ty3 = ty0.clone();
194 let ident2 = ident1.clone();
195 let ident3 = ident1.clone();
196 let gen = quote! {
197 impl ByteStruct for #name {
198 fn write_bytes(&self, bytes: &mut [u8]) {
199 let mut cur: usize = 0;
200 #({
201 let len = <#ty1>::BYTE_LEN;
202 self.#ident1.#write_bytes_fn(&mut bytes[cur .. (cur + len)]);
203 cur += len;
204 })*
205 }
206 fn read_bytes(bytes: &[u8]) -> Self {
207 let mut cur: usize = 0;
208 #(
209 let len = <#ty2>::BYTE_LEN;
210 let #ident2 = <#ty3>::#read_bytes_fn(&bytes[cur .. (cur + len)]);
211 cur += len;
212 )*
213 #name { #(#ident3),* }
214 }
215 }
216
217 impl ByteStructLen for #name {
218 const BYTE_LEN: usize = #(<#ty0>::BYTE_LEN)+*;
219 }
220 };
221 gen.into()
222
223 } else {
224 panic!("Only support struct with named fields!");
225 }
226}