1#[doc(hidden)]
16#[macro_export]
17macro_rules! __match_packet_opcode {
18 ($opcodeVar:ident =>) => {false};
19 ($opcodeVar:ident => $($packet:ident),+) => {
20 $(<$packet as $crate::__internal::Packet>::ID == $opcodeVar)||+
21 };
22}
23
24#[doc(hidden)]
25#[macro_export]
26macro_rules! __match_protocol_opcode {
27 ($opcodeVar:ident =>) => {false};
28 ($opcodeVar:ident => $($proto:ident),+) => {
29 $($proto::has_opcode($opcodeVar))||+
30 };
31}
32
33#[macro_export]
69macro_rules! define_protocol {
70 ($name:ident => $($enumValue:ident),*) => {
71 define_protocol! {
72 $name => $($enumValue),* +
73 }
74 };
75 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
76 $crate::define_protocol_enum! { $name =>
77 $($enumValue),* + $($innerProto),*
78 }
79
80 $crate::define_input_protocol! { $name =>
81 $($enumValue),* + $($innerProto),*
82 }
83
84 $crate::define_output_protocol! { $name =>
85 $($enumValue),* + $($innerProto),*
86 }
87 };
88}
89
90#[macro_export]
93macro_rules! define_outbound_protocol {
94 ($name:ident => $($enumValue:ident),*) => {
95 define_outbound_protocol! {
96 $name => $($enumValue),* +
97 }
98 };
99 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
100 $crate::define_protocol_enum! { $name =>
101 $($enumValue),* + $($innerProto),*
102 }
103
104 $crate::define_output_protocol! { $name =>
105 $($enumValue),* + $($innerProto),*
106 }
107 }
108}
109
110#[macro_export]
113macro_rules! define_inbound_protocol {
114 ($name:ident => $($enumValue:ident),*) => {
115 define_inbound_protocol! {
116 $name => $($enumValue),* +
117 }
118 };
119 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
120 $crate::define_protocol_enum! { $name =>
121 $($enumValue),* + $($innerProto),*
122 }
123
124 $crate::define_input_protocol! { $name =>
125 $($enumValue),* + $($innerProto),*
126 }
127 }
128}
129
130#[doc(hidden)]
131#[macro_export]
132macro_rules! define_protocol_enum {
133 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
134 #[derive(Debug, Clone)]
135 pub enum $name {
136 $(
137 $enumValue($enumValue),
138 )*
139 $(
140 $innerProto($innerProto),
141 )*
142 }
143
144 $(
145 impl From<$enumValue> for $name {
146 fn from(value: $enumValue) -> Self {
147 $name::$enumValue(value)
148 }
149 }
150 )*
151
152 $(
153 impl From<$innerProto> for $name {
154 fn from(value: $innerProto) -> Self {
155 $name::$innerProto(value)
156 }
157 }
158 )*
159
160 $(
161 impl TryFrom<$name> for $enumValue {
162 type Error = $name;
163 fn try_from(value: $name) -> Result<Self, Self::Error> {
164 #[allow(unreachable_patterns)]
165 match value {
166 $name::$enumValue(inner) => Ok(inner),
167 _ => Err(value)
168 }
169 }
170 }
171 )*
172
173 $(
174 impl TryFrom<$name> for $innerProto {
175 type Error = $name;
176 fn try_from(value: $name) -> Result<Self, Self::Error> {
177 #[allow(unreachable_patterns)]
178 match value {
179 $name::$innerProto(inner) => Ok(inner),
180 _ => Err(value)
181 }
182 }
183 }
184 )*
185 }
186}
187
188#[doc(hidden)]
189#[macro_export]
190macro_rules! define_input_protocol {
191 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
192 impl $crate::__internal::InputProtocol for $name {
193 type Proto = Box<$name>;
194
195 fn create_from(opcode: u16, data: &[u8]) -> Result<(usize, Box<Self>), $crate::__internal::InStreamError> {
196 match opcode {
197 $(
198 <$enumValue as $crate::__internal::Packet>::ID => {
199 let (consumed, res) = <$enumValue as $crate::__internal::TryFromPacket>::try_deserialize(data)?;
200 Ok((consumed, Box::new($name::$enumValue(res))))
201 }
202 )*
203 _ => {
204 #[allow(unused)]
205 use $crate::__internal::MatchOpcode;
206 $(
207 if $innerProto::has_opcode(opcode) {
208 let (consumed, res) = $innerProto::create_from(opcode, data)?;
209 return Ok((consumed, Box::new($name::$innerProto(*res))));
210 }
211 )*
212 Err($crate::__internal::InStreamError::UnmatchedOpcode(opcode))
213 }
214 }
215 }
216 }
217
218 impl $crate::__internal::MatchOpcode for $name {
219 fn has_opcode(opcode: u16) -> bool {
220 $crate::__match_packet_opcode!(opcode => $($enumValue),*) ||
221 $crate::__match_protocol_opcode!(opcode => $($innerProto),*)
222 }
223 }
224 }
225}
226
227#[doc(hidden)]
228#[macro_export]
229macro_rules! define_output_protocol {
230 ($name:ident => $($enumValue:ident),* + $($innerProto:ident),*) => {
231 impl From<$name> for $crate::__internal::OutgoingPacket {
232 fn from(value: $name) -> $crate::__internal::OutgoingPacket {
233 match value {
234 $(
235 $name::$enumValue(inner) => inner.into(),
236 )*
237 $(
238 $name::$innerProto(inner) => inner.into(),
239 )*
240 }
241 }
242 }
243 }
244}
245
246#[doc(hidden)]
247pub mod __internal {
248 pub use skrillax_packet::*;
249 pub use skrillax_stream::stream::*;
250
251 pub trait MatchOpcode {
252 fn has_opcode(opcode: u16) -> bool;
253 }
254
255 impl<T: Packet> MatchOpcode for T {
256 fn has_opcode(opcode: u16) -> bool {
257 Self::ID == opcode
258 }
259 }
260}
261
262#[cfg(test)]
263mod test {
264 use skrillax_packet::{OutgoingPacket, Packet, TryFromPacket};
265 use skrillax_serde::{ByteSize, Deserialize, Serialize};
266 use skrillax_stream::InputProtocol;
267
268 #[derive(Packet, Deserialize, ByteSize, Serialize, Debug, Clone)]
269 #[packet(opcode = 0x1000)]
270 pub struct TestPacket {
271 inner: String,
272 }
273
274 #[derive(Packet, Serialize, ByteSize, Debug, Clone)]
275 #[packet(opcode = 0x1001)]
276 pub struct OutboundPacketOnly {
277 #[silkroad(size = 0)]
278 opt: Option<String>,
279 }
280
281 define_protocol! { TestProtocol =>
282 TestPacket
283 }
284
285 define_protocol! { WrapperProtocol =>
286 +
287 TestProtocol
288 }
289
290 define_outbound_protocol! { OutboundProto =>
291 OutboundPacketOnly
292 }
293
294 #[test]
295 fn test_protocol() {
296 TestProtocol::create_from(0x1000, &[0x00, 0x00]).unwrap();
297 WrapperProtocol::create_from(0x1000, &[0x00, 0x00]).unwrap();
298 }
299
300 #[test]
301 fn test_convert() {
302 let (_, packet) = TestPacket::try_deserialize(&[0x00, 0x00]).unwrap();
303 let proto: TestProtocol = packet.into();
304 let wrapper: WrapperProtocol = proto.into();
305 let inner_proto: TestProtocol = wrapper.try_into().expect("Should get back inner");
306 let _: TestPacket = inner_proto.try_into().expect("Should get back packet");
307 }
308
309 #[test]
310 fn test_outbound_only() {
311 let packet = OutboundPacketOnly { opt: None };
312 let proto: OutboundProto = packet.into();
313 let data: OutgoingPacket = proto.into();
314 assert!(matches!(
315 data,
316 OutgoingPacket::Simple { opcode: 0x1001, .. }
317 ))
318 }
319}