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}