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