wsbps/packets.rs
1/// ## Writable Type Macro
2/// A macro used internally to convert struct and packet field types
3/// into writable types
4#[macro_export]
5macro_rules! writable_type {
6 // Match VarInts
7 (VarInt, $e:expr) => { *$e };
8 // Match VarLongs
9 (VarLong, $e:expr) => { *$e } ;
10 // Match vectors
11 (Vec<$inner:ident>, $e:expr) => { *$e };
12 // Match all other types
13 ($typ:ty, $e:expr) => { $e };
14}
15
16/// ## Impl Struct Mode Macro
17/// This is the underlying backing macro which is used by the impl_packet_data macro which is used by the
18/// packet_data macro to generic the specific struct trait implementations for the desired packet mode
19#[macro_export]
20macro_rules! impl_struct_mode {
21 (
22 (<-) $Name:ident {
23 $($Field:ident, $FieldType:ty),*
24 }
25 ) => {
26 // Implement the io::Readable trait so this struct can be read
27 impl $crate::Readable for $Name {
28 fn read<_ReadX: std::io::Read>(i: &mut _ReadX) -> $crate::ReadResult<Self> where Self: Sized {
29 // Provide all the fields to a new struct of self
30 Ok(Self {
31 // Read all the fields for the struct
32 $(
33 $Field: <$FieldType>::read(i)?.into(),
34 )*
35 })
36 }
37 }
38 };
39 (
40 (->) $Name:ident {
41 $($Field:ident, $FieldType:ty),*
42 }
43 ) => {
44 // Implement the io::Writable trait so the enum can be written
45 #[allow(unused_imports, unused_variables)]
46 impl $crate::Writable for $Name {
47 fn write<_ReadX: std::io::Write>(&mut self, o: &mut _ReadX) -> $crate::WriteResult {
48 // Create a write call for all of the fields using their type
49 $($crate::writable_type!($FieldType, &mut self.$Field).write(o)?;)*
50 Ok(())
51 }
52 }
53 };
54 (
55 (<->) $Name:ident {
56 $($Field:ident, $FieldType:ty),*
57 }
58 ) => {
59 // Pass the parameters onto the read implementation
60 $crate::impl_struct_mode!(
61 (<-) $Name {
62 $($Field, $FieldType),*
63 }
64 );
65 // Pass the parameters onto the write implementation
66 $crate::impl_struct_mode!(
67 (->) $Name {
68 $($Field, $FieldType),*
69 }
70 );
71 };
72}
73
74
75#[macro_export]
76macro_rules! discriminant_to_literal {
77 (String, $discriminant:expr) => {
78 &*$discriminant
79 };
80 ($discriminant_type:ty, $discriminant:expr) => {
81 $discriminant.into()
82 };
83}
84
85/// ## Impl Enum Mode Macro
86/// This is the underlying backing macro which is used by the impl_packet_data macro which is used by the
87/// packet_data macro to generate the specific enum trait implementations for the desired packet mode
88#[macro_export]
89macro_rules! impl_enum_mode {
90 (
91 (<-) $Name:ident $Type:ty {
92 $($Field:ident, $Value:expr),*
93 }
94 ) => {
95 // Implement the io::Readable trait so this enum can be read
96 impl $crate::Readable for $Name {
97 fn read<B: std::io::Read>(i: &mut B) -> $crate::ReadResult<Self> where Self: Sized {
98 // Use the io::Readable for the type parameter to encode it
99 let value = $crate::discriminant_to_literal!($Type, <$Type>::read(i)?);
100 match value { // Match the value that was read
101 // Match for all the enum fields. Matches will return the enum field
102 $($Value => Ok($Name::$Field),)*
103 // Errors are used if none match
104 _ => Err($crate::PacketError::UnknownEnumValue),
105 }
106 }
107 }
108 };
109 (
110 (->) $Name:ident $Type:ty {
111 $($Field:ident, $Value:expr),*
112 }
113 ) => {
114 // Implement the io::Writable trait so the enum can be written
115 impl $crate::Writable for $Name {
116 fn write<B: std::io::Write>(&mut self, o: &mut B) -> $crate::WriteResult {
117 match self { // Match self
118 // For each of the fields map them to a write call for the type
119 // and the value for that type
120 $($Name::$Field => <$Type>::from($Value).write(o)?,)*
121 };
122 Ok(())
123 }
124 }
125 };
126 (
127 (<->) $Name:ident $Type:ty {
128 $($Field:ident, $Value:expr),*
129 }
130 ) => {
131 // Pass the parameters onto the read implementation
132 $crate::impl_enum_mode!(
133 (<-) $Name $Type {
134 $($Field, $Value),*
135 }
136 );
137 // Pass the parameters onto the write implementation
138 $crate::impl_enum_mode!(
139 (->) $Name $Type {
140 $($Field, $Value),*
141 }
142 );
143 };
144}
145
146/// ## Impl Packet Data
147/// This is the underlying backing macro for packet_data which handles which type should be
148/// implemented and for which mode (enum / struct) this is used to speed up parsing and reduce
149/// the complexity of the packet_data macro
150#[macro_export]
151macro_rules! impl_packet_data {
152 // Matching enums
153 (
154 enum $Name:ident $Mode:tt $Type:ty {
155 $($Field:ident, $Value:expr),*
156 }
157 ) => {
158 // Create the backing enum
159 #[derive(Debug, Clone, PartialEq)]
160 #[allow(dead_code)]
161 pub enum $Name {
162 $($Field),*
163 }
164
165 // Implement the traits for the provided mode
166 $crate::impl_enum_mode!(
167 $Mode $Name $Type {
168 $($Field, $Value),*
169 }
170 );
171 };
172 // Matching structs
173 (
174 struct $Name:ident $Mode:tt {
175 $($Field:ident, $FieldType:ty),*
176 }
177 ) => {
178 // Create the backing struct
179 #[derive(Debug, Clone, PartialEq)]
180 pub struct $Name {
181 $(pub $Field: $FieldType),*
182 }
183
184 // Implement the traits for the provided mode
185 $crate::impl_struct_mode!(
186 $Mode $Name {
187 $($Field, $FieldType),*
188 }
189 );
190 };
191}
192
193/// ## Packet Data
194/// This macro is used to implement read and write traits for enums so they can be used within
195/// packets as packet fields. This is a block and you should use it to implement all of your
196/// structs and enums at once.
197///
198/// ## Directions
199/// (<->) Bi-Direction: This implements both readers and writers for this data. This should
200/// be used in structs and enums that are shared between readable and writable packets.
201///
202/// (->) Write-Only: This implements only the writers for this data. This should be used if
203/// the struct/enum is only going to be sent and not received.
204///
205/// (<-) Read-Only: This implements only the readers for this data. This should be used if
206/// the struct/enum is only going to be received and not send.
207///
208/// ## Example
209///
210/// ```
211/// use wsbps::packet_data;
212/// packet_data! {
213/// struct ExampleBiStruct (<->) {
214/// Field: u8,
215/// Name: String
216/// }
217///
218/// enum TestWriteEnum (->) (u8) {
219/// A: 1,
220/// B: 2
221/// }
222/// }
223/// ```
224///
225#[macro_export]
226macro_rules! packet_data {
227 (
228 $(
229 $Keyword:ident $Name:ident $Mode:tt $(($Type:ty))? {
230 $(
231 $Field:ident:$($EnumValue:literal)?$($FieldType:ty)?
232 ),* $(,)?
233 }
234 )*
235 ) => {
236 $(
237 // Implement the underlying types for each matched value
238 $crate::impl_packet_data!(
239 $Keyword $Name $Mode $($Type)? {
240 $($Field, $($EnumValue)? $($FieldType)?),*
241 }
242 );
243 )*
244 };
245}
246
247/// # Impl Group Mode Macro
248/// This macro implements the specific read/write mode for the group. This also implements the traits
249/// for each specific mode.
250#[macro_export]
251macro_rules! impl_group_mode {
252 (
253 (<-) $Group:ident {
254 $(
255 $Name:ident, $ID:literal {
256 $($Field:ident, $Type:ty),*
257 }
258 );*
259 }
260 ) => {
261 // Implement the io::Readable trait so this enum can be read this must be
262 // implemented here so we can read the packet ID first then read the
263 // respective packet
264 impl $crate::Readable for $Group {
265 fn read<_ReadX: std::io::Read>(i: &mut _ReadX) -> $crate::ReadResult<Self> {
266 let p_id = $crate::VarInt::read(i)?.0;
267 match p_id {
268 // Match for all the packet IDS and read the packet struct and return
269 // the enum value with the struct as the value
270 $(
271 $ID => Ok($Group::$Name {
272 $(
273 $Field: <$Type>::read(i)?.into(),
274 )*
275 }),
276 )*
277 _ => Err($crate::PacketError::UnknownPacket(p_id))
278 }
279 }
280 }
281 };
282 (
283 (->) $Group:ident {
284 $(
285 $Name:ident, $ID:literal {
286 $($Field:ident, $Type:ty),*
287 }
288 );*
289 }
290 ) => {
291 impl $crate::Writable for $Group {
292 fn write<_WriteX: std::io::Write>(&mut self, o: &mut _WriteX) -> $crate::WriteResult {
293 match self {
294 $(
295 $Group::$Name {
296 $($Field),*
297 } => {
298 $crate::VarInt($ID as u32).write(o)?;
299 $($crate::writable_type!($Type, $Field).write(o)?;)*
300 },
301 )*
302 }
303 Ok(())
304 }
305 }
306 };
307 (
308 (<->) $Group:ident {
309 $(
310 $Name:ident, $ID:literal {
311 $($Field:ident, $Type:ty),*
312 }
313 );*
314 }
315 ) => {
316 $crate::impl_group_mode!(
317 (<-) $Group {
318 $(
319 $Name, $ID {
320 $($Field, $Type),*
321 }
322 );*
323 }
324 );
325 $crate::impl_group_mode!(
326 (->) $Group {
327 $(
328 $Name, $ID {
329 $($Field, $Type),*
330 }
331 );*
332 }
333 );
334 };
335}
336
337/// # Packets Macro
338/// This macro is used to define packet groups. It implements the structs for each packet along
339/// with their readers and writers (if they require them) and an enum for the packet group to
340/// read packets.
341///
342/// ## Directions
343/// (<->) Bi-Direction: This implements both readers and writers for this data. This should
344/// be used in structs and enums that are shared between readable and writable packets.
345///
346/// (->) Write-Only: This implements only the writers for this data. This should be used if
347/// the struct/enum is only going to be sent and not received.
348///
349/// (<-) Read-Only: This implements only the readers for this data. This should be used if
350/// the struct/enum is only going to be received and not send.
351///
352/// ## Example
353/// ```
354///
355/// use wsbps::packets;
356///
357/// packets! {
358/// BiPackets (<->) {
359/// APacket (0x02) {
360/// User: u8,
361/// Name: String
362/// }
363/// BPacket (0x05) {
364/// Name: String
365/// }
366/// }
367///
368/// ServerPackets (->) {
369/// CPacket (0x02) {
370/// User: u8,
371/// Name: String
372/// }
373/// DPacket (0x05) {
374/// Name: String
375/// }
376/// }
377/// }
378/// ```
379#[macro_export]
380macro_rules! packets {
381 (
382 $(
383 $Group:ident $Mode:tt {
384 $(
385 $Name:ident ($ID:literal) {
386 $($Field:ident: $Type:ty),* $(,)?
387 }
388 )*
389 }
390 )*
391 ) => {
392 $(
393 // Implement the group enum
394 #[derive(Debug, Clone, PartialEq)]
395 #[allow(dead_code)]
396 pub enum $Group {
397 $(
398 $Name {
399 $(
400 $Field: $Type,
401 )*
402 }
403 ),*
404 }
405
406 // Implement the specified group mode
407 $crate::impl_group_mode!(
408 $Mode $Group {
409 $(
410 $Name, $ID {
411 $($Field, $Type),*
412 }
413 );*
414 }
415 );
416
417 // Implement packet variant ID for each packet enum value
418 impl $Group {
419 // Packet id function to allow retrieval of the packet ID on the packet
420 fn id(&self) -> $crate::VarInt {
421 $crate::VarInt(match self {
422 $($Group::$Name { .. } => $ID as u32,)*
423 })
424 }
425 }
426 )*
427 };
428}