neli/consts/macros.rs
1/// For generating a marker trait that flags a new enum as usable in a
2/// field that accepts a generic type. This way, the type parameter
3/// can be constrained by a trait bound to only accept enums that
4/// implement the marker trait.
5///
6/// # Usage
7///
8/// ```
9/// use neli::neli_enum;
10///
11/// /// Define an enum
12/// #[neli_enum(serialized_type = "u16")]
13/// pub enum MyFamilyEnum {
14/// One = 1,
15/// Two = 2,
16/// Three = 3
17/// }
18///
19/// /// Define another enum
20/// #[neli_enum(serialized_type = "u16")]
21/// pub enum MyOtherFamilyEnum {
22/// Four = 4,
23/// Five = 5,
24/// Six = 6,
25/// }
26///
27/// /// Define a marker trait and implement it for MyFamilyEnum and
28/// /// MyOtherFamilyEnum.
29/// neli::impl_trait!(
30/// MyMarkerTrait,
31/// u16,
32/// MyFamilyWrapperType,
33/// MyFamilyEnum,
34/// MyOtherFamilyEnum
35/// );
36/// ```
37///
38/// The result of the example above will be:
39/// * One enum called `MyFamilyEnum`.
40/// * Another called `MyOtherFamilyEnum`.
41/// * A marker trait called `MyMarkerTrait`. This can be used to
42/// constain type parameter so that only `MyFamilyEnum` and
43/// `MyOtherFamilyEnum` variants can be passed in as a value.
44/// * A wrapper enum called `MyFamilyWrapperType`. The definition is
45/// as follows:
46/// ```
47/// enum MyFamilyEnum {
48/// One,
49/// Two,
50/// Three,
51/// }
52///
53/// enum MyOtherFamilyEnum {
54/// Four,
55/// Five,
56/// Six,
57/// }
58///
59/// enum MyFamilyWrapperType {
60/// MyFamilyEnum(MyFamilyEnum),
61/// MyOtherFamilyEnum(MyOtherFamilyEnum),
62/// }
63/// ```
64/// If you are unsure of which type will be passed back, the wrapper
65/// type can be used to automatically determine this for you when
66/// deserializing and accept all values defined across both enums.
67#[macro_export]
68macro_rules! impl_trait {
69 (
70 $(#[$outer:meta])*
71 $vis_trait:vis $trait_name:ident,
72 $to_from_ty:ty,
73 $(
74 #[$wrapper_outer:meta]
75 )*
76 $vis_enum:vis $wrapper_type:ident,
77 $( $const_enum:ident ),+
78 $(,)?
79 ) => {
80 $(#[$outer])*
81 $vis_trait trait $trait_name: PartialEq
82 + Clone
83 + From<$to_from_ty>
84 + Into<$to_from_ty>
85 + Copy
86 + $crate::Size
87 + $crate::TypeSize
88 + $crate::FromBytes
89 + $crate::ToBytes
90 + std::fmt::Debug
91 {}
92
93 impl $trait_name for $to_from_ty {}
94
95 $(
96 impl $trait_name for $const_enum {}
97 )+
98
99 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
100 $(
101 #[$wrapper_outer]
102 )*
103 $vis_enum enum $wrapper_type {
104 $(
105 #[allow(missing_docs)]
106 $const_enum($const_enum),
107 )+
108 /// Constant could not be parsed into a type
109 UnrecognizedConst($to_from_ty),
110 }
111
112 impl $crate::Size for $wrapper_type {
113 fn unpadded_size(&self) -> usize {
114 std::mem::size_of::<$to_from_ty>()
115 }
116 }
117
118 impl $crate::TypeSize for $wrapper_type {
119 fn type_size() -> usize {
120 std::mem::size_of::<$to_from_ty>()
121 }
122 }
123
124 impl $crate::ToBytes for $wrapper_type {
125 fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), $crate::err::SerError> {
126 Ok(match self {
127 $(
128 $wrapper_type::$const_enum(val) => val.to_bytes(buffer)?,
129 )*
130 $wrapper_type::UnrecognizedConst(val) => val.to_bytes(buffer)?,
131 })
132 }
133 }
134
135 impl $crate::FromBytes for $wrapper_type {
136 fn from_bytes(buffer: &mut std::io::Cursor<impl AsRef<[u8]>>) -> Result<Self, $crate::err::DeError> {
137 Ok($wrapper_type::from(<$to_from_ty as $crate::FromBytes>::from_bytes(
138 buffer
139 )?))
140 }
141 }
142
143 impl $trait_name for $wrapper_type {}
144
145 $(
146 impl From<$const_enum> for $wrapper_type {
147 fn from(e: $const_enum) -> Self {
148 $wrapper_type::$const_enum(e)
149 }
150 }
151 )+
152
153 impl From<$wrapper_type> for $to_from_ty {
154 fn from(w: $wrapper_type) -> Self {
155 match w {
156 $(
157 $wrapper_type::$const_enum(inner) => inner.into(),
158 )+
159 $wrapper_type::UnrecognizedConst(v) => v,
160 }
161 }
162 }
163
164 impl From<&$wrapper_type> for $to_from_ty {
165 fn from(w: &$wrapper_type) -> Self {
166 match w {
167 $(
168 $wrapper_type::$const_enum(inner) => inner.into(),
169 )+
170 $wrapper_type::UnrecognizedConst(v) => *v,
171 }
172 }
173 }
174
175 impl From<$to_from_ty> for $wrapper_type {
176 fn from(v: $to_from_ty) -> Self {
177 $(
178 let var = $const_enum::from(v);
179 if !var.is_unrecognized() {
180 return $wrapper_type::$const_enum(var);
181 }
182 )*
183 $wrapper_type::UnrecognizedConst(v)
184 }
185 }
186 };
187}
188
189/// Implement a container for bit flag enums using the [`bitflags`][bitflags] crate.
190///
191/// # Usage
192///
193/// ```
194/// use neli::neli_enum;
195///
196/// neli::impl_flags!(
197/// pub MyFlags: u16 {
198/// ThisFlag = 1,
199/// ThatFlag = 2,
200/// }
201/// );
202/// ```
203///
204/// See [here][bitflags] for the methods that are autogenerated by `bitflags` on
205/// the struct.
206#[macro_export]
207macro_rules! impl_flags {
208 ($(#[$outer:meta])* $vis:vis $name:ident: $bin_type:ty {
209 $($(#[$inner:ident $($tt:tt)*])* $var:ident = $const:expr),*
210 $(,)?
211 }) => {
212 #[derive(Debug, Clone, Copy, Eq, PartialEq, neli_proc_macros::Size, neli_proc_macros::FromBytes, neli_proc_macros::ToBytes)]
213 $(#[$outer])*
214 $vis struct $name($bin_type);
215
216 bitflags::bitflags! {
217 impl $name: $bin_type {
218 $(
219 $(#[$inner $($tt)*])*
220 #[allow(missing_docs)]
221 const $var = $const;
222 )*
223 }
224 }
225
226 impl From<$bin_type> for $name {
227 fn from(bin: $bin_type) -> Self {
228 $name::from_bits_truncate(bin)
229 }
230 }
231
232 impl From<$name> for $bin_type {
233 fn from(ty: $name) -> Self {
234 ty.bits()
235 }
236 }
237
238 impl $crate::TypeSize for $name {
239 fn type_size() -> usize {
240 <$bin_type as $crate::TypeSize>::type_size()
241 }
242 }
243 };
244}