neli_proc_macros/
lib.rs

1//! Procedural macros to be used with the library
2//! [`neli`](https://github.com/jbaublitz/neli).
3//!
4//! All derive macros other than `Header` generate implicit type
5//! parameter bounds on every type parameter which can be overriden
6//! with struct attributes.
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{parse, Item, Meta};
11
12#[macro_use]
13mod shared;
14
15mod derive_frombytes;
16mod derive_header;
17mod derive_size;
18mod derive_tobytes;
19mod neli_enum;
20
21use derive_frombytes::*;
22use derive_header::*;
23use derive_size::*;
24use derive_tobytes::*;
25use neli_enum::*;
26
27/// Converts an enum from the form:
28///
29/// ```no_compile
30/// use neli_proc_macros::neli_enum;
31///
32/// #[neli_enum(serialized_type = "u16")]
33/// pub enum MyConstants {
34///     ConstOne = 1,
35///     ConstTwo = 2,
36///     ConstThree = 3,
37/// }
38/// ```
39///
40/// to:
41///
42/// ```
43/// pub enum MyConstants {
44///     ConstOne,
45///     ConstTwo,
46///     ConstThree,
47/// }
48/// ```
49///
50/// with [`From`] implemented reflexively for `MyConstants` and
51/// [`u16`].
52#[proc_macro_attribute]
53pub fn neli_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
54    let attr_string = attr.to_string();
55    let meta =
56        parse::<Meta>(attr).unwrap_or_else(|_| panic!("{} is not a valid attribute", attr_string));
57    let enum_item = parse::<Item>(item).unwrap();
58
59    let enm = if let Item::Enum(e) = enum_item {
60        e
61    } else {
62        panic!("This macro only operates on enums");
63    };
64
65    TokenStream::from(generate_neli_enum(enm, meta))
66}
67
68/// Derives the neli `Size` trait for a struct or enum.
69///
70/// Acceptable struct attribute is:
71/// * `#[neli(size_bound = "T: MyTrait")]` which will generate a
72/// trait bound in the impl for the specified type parameter.
73///
74/// Implicit type parameter bound: `Size`.
75#[proc_macro_derive(Size, attributes(neli))]
76pub fn proc_macro_size(ts: TokenStream) -> TokenStream {
77    let item = parse::<Item>(ts).unwrap();
78    TokenStream::from(match item {
79        Item::Struct(strct) => impl_size_struct(strct),
80        Item::Enum(enm) => impl_size_enum(enm),
81        _ => panic!("Size can only be derived for structs and enums"),
82    })
83}
84
85/// Derives the neli `Header` trait for a struct or enum. Unlike
86/// other derive macros in this crate, the `Header` derive macro
87/// does not impose type parameter bounds on type parameters.
88/// See the accepted attribute for more information. The reason for
89/// this is that the last field is considered to be the payload.
90/// Because the payload may be represented by a type parameter,
91/// we cannot blindly restrict type parameters or else we impose
92/// an artificial restriction of `TypeSize` on the payload type
93/// parameter. This is a problem for the `Header` trait as the
94/// payload may be unsized even if the rest of the header is
95/// composed exclusively of statically sized types and are therefore
96/// compatible with the `TypeSize` trait.
97///
98/// Acceptable struct attribute is:
99/// * `#[neli(header_bound = "T: MyTrait")]` which will generate a
100/// trait bound in the impl for the specified type parameter.
101///
102/// While there is no implicit type parameter bound, every type
103/// parameter that does not correspond to a payload should have
104/// a specified type parameter bound of `TypeSize`.
105#[proc_macro_derive(Header, attributes(neli))]
106pub fn proc_macro_header(ts: TokenStream) -> TokenStream {
107    let item = parse::<Item>(ts).unwrap();
108    TokenStream::from(match item {
109        Item::Struct(strct) => impl_header_struct(strct),
110        _ => panic!("Header can only be derived for structs"),
111    })
112}
113
114/// Derives the neli `FromBytes` trait for a struct.
115///
116/// Acceptable struct attribute is:
117/// * `#[neli(from_bytes_bound = "T: MyTrait")]` which will generate
118/// a trait bound in the impl for the specified type parameter.
119/// * `#[neli(padding)]` which will add special handling for padding
120/// for this struct.
121///
122/// Acceptable field attribute forms are:
123/// * `#[neli(input = "input_expression")]` which may only be used
124/// once for a struct. The behavior of this attribute is that a
125/// bound requirement will change from the implicit `FromBytes` to
126/// an implicit `FromBytesWithInput` bound. The method in this trait
127/// will be called with `input_expression` as the input provided.
128/// * `#[neli(input)]` which will transparently pass the input
129/// provided in the `FromBytesWithInput` method through to the
130/// `FromBytesWithInput` method for this field unchanged according
131/// to the rules described above.
132/// * `#[neli(size = "size_var_name")]` which allows specifying a size of the data type
133/// that is different from the input specified by `#[neli(input)]`. Not specifying
134/// this attribute defaults to using `input` as the size as well.
135///
136/// Implicit type parameter bound: `FromBytes`.
137#[proc_macro_derive(FromBytes, attributes(neli))]
138pub fn proc_macro_frombytes(ts: TokenStream) -> TokenStream {
139    let item = parse::<Item>(ts).unwrap();
140    TokenStream::from(match item {
141        Item::Struct(strct) => impl_frombytes_struct(strct, "FromBytes", "from_bytes", None, None),
142        _ => panic!("FromBytes can only be derived for structs"),
143    })
144}
145
146/// Derives the neli `FromBytesWithInput` trait for a struct.
147///
148/// Acceptable struct attribute is:
149/// * `#[neli(from_bytes_bound = "T: MyTrait")]` which will generate
150/// a trait bound in the impl for the specified type parameter.
151/// * `#[neli(padding)]` which will add special handling for padding
152/// for this struct.
153///
154/// Acceptable field attribute forms are:
155/// * `#[neli(input = "input_expression")]` which may only be used
156/// once for a struct. The behavior of this attribute is that a
157/// bound requirement will change from the implicit `FromBytes` to
158/// an implicit `FromBytesWithInput` bound. The method in this trait
159/// will be called with `input_expression` as the input provided.
160/// * `#[neli(input)]` which will transparently pass the input
161/// provided in the `FromBytesWithInput` method through to the
162/// `FromBytesWithInput` method for this field unchanged according
163/// to the rules described above.
164/// * `#[neli(size = "size_var_name")]` which allows specifying a size of the data type
165/// that is different from the input specified by `#[neli(input)]`. Not specifying
166/// this attribute defaults to using `input` as the size as well.
167///
168/// Implicit type parameter bound: `FromBytes`.
169#[proc_macro_derive(FromBytesWithInput, attributes(neli))]
170pub fn proc_macro_frombyteswithinput(ts: TokenStream) -> TokenStream {
171    let item = parse::<Item>(ts).unwrap();
172    TokenStream::from(match item {
173        Item::Struct(strct) => impl_frombytes_struct(
174            strct,
175            "FromBytesWithInput",
176            "from_bytes_with_input",
177            Some(quote! {
178                type Input = usize;
179            }),
180            Some(quote! {
181                , input: Self::Input
182            }),
183        ),
184        _ => panic!("FromBytesWithInput can only be derived for structs"),
185    })
186}
187
188/// Derives the neli `ToBytes` trait for a struct or enum.
189///
190/// Acceptable struct attribute is:
191/// * `#[neli(to_bytes_bound = "T: MyTrait")]` which will generate a
192/// trait bound in the impl for the specified type parameter.
193/// * `#[neli(padding)]` which will add special handling for padding
194/// for this struct.
195///
196/// Implicit type parameter bound: `ToBytes`.
197#[proc_macro_derive(ToBytes, attributes(neli))]
198pub fn proc_macro_tobytes(ts: TokenStream) -> TokenStream {
199    let item = parse::<Item>(ts).unwrap();
200    TokenStream::from(match item {
201        Item::Struct(strct) => impl_tobytes_struct(strct),
202        Item::Enum(enm) => impl_tobytes_enum(enm),
203        _ => panic!("ToBytes can only be derived for structs and enums"),
204    })
205}