Skip to main content

ygopro_handler/
extract.rs

1//! Practical types used in the ygopro message flow.
2//!
3//! This module provides the request/response machinery that handlers use to extract
4//! their parameters and to produce a response.
5//!
6//! Due to Rust's orphan rule and impl-conflict limitations, only the following
7//! [`FromRequest`] implementations are provided. Names in `<>` are the free type
8//! variables of each impl.
9//!
10//! **Blanket impls**:
11//!
12//! | Extracted type | Source |
13//! |----------------|--------|
14//! | `Extra` (`SocketAddr`, `Netplayer`, `CorePlayer`, `usize`, `u8`, `u32`) | `Request<Message, Extra>` |
15//! | `&Message` | `Request<Message, Extra>` |
16//! | `&mut Bundle<Req, State, Res>` | `Bundle<Req, State, Res>` |
17//! | `&mut Response<Message>` | `Bundle<Req, State, Response<Message>>` |
18//! | `&mut StopFlag` | `Bundle<Req, State, Res>` |
19//! | `&mut Box<dyn Any + Send>` | `Bundle<Box<dyn Any + Send>, State, Res>` |
20//! | `&mut anymap` | `Bundle<Req, State, Res>` (requires `State: ContainsMapMut`) |
21//!
22//! **Per-variant impls** (generated for every message variant of `ctos::`, `stoc::` and
23//! `gm::`, which are treated equally; one example per family):
24//!
25//! | Extracted type | Source |
26//! |----------------|--------|
27//! | `&ctos::JoinGame` | `Request<ctos::Message, Extra>` / `ctos::Message` |
28//! | `&stoc::JoinGame` | `Request<stoc::Message, Extra>` / `stoc::Message` |
29//! | `&gm::Move` | `Request<gm::Message, Extra>` / `gm::Message` |
30//! | `&ctos::JoinGame` | `Request<Complex<ctos::Message>, Extra>` / `Complex<ctos::Message>` |
31//! | `&stoc::JoinGame` | `Request<Complex<stoc::Message>, Extra>` / `Complex<stoc::Message>` |
32//! | `&gm::Move` | `Request<Complex<gm::Message>, Extra>` / `Complex<gm::Message>` |
33//! | `&gm::Move` | `Request<Complex<stoc::Message>, Extra>` / `Complex<stoc::Message>` |
34
35use std::any::Any;
36use std::convert::Infallible;
37use std::net::SocketAddr;
38
39use ygopro_data::complex::Complex;
40use ygopro_data::constants::CorePlayer;
41use ygopro_data::constants::Netplayer;
42use ygopro_data::message::ctos;
43use ygopro_data::message::stoc;
44use ygopro_data::message::gm;
45
46use crate::IntoResponse;
47use crate::handler::Bundle;
48use crate::handler::FromRequest;
49
50/// A request carrying a message and an extra message.
51pub struct Request<Message, Extra> {
52    /// The message to dispatch.
53    pub message: Message,
54    /// The extra data attached to the request (e.g. the sender's address or position).
55    pub extra: Extra,
56}
57
58macro_rules! impl_extractable {
59    ($extra:ty) => {
60        impl<Message, State, Res> FromRequest<Request<Message, $extra>, State, Res> for $extra
61        where
62            Message: Send,
63            State: Send,
64            Res: Send,
65        {
66            fn from_request(bundle: &mut Bundle<Request<Message, $extra>, State, Res>) -> Option<Self> {
67                Some(bundle.request.extra)
68            }
69        }
70    };
71}
72
73impl_extractable!(SocketAddr);
74impl_extractable!(Netplayer);
75impl_extractable!(CorePlayer);
76impl_extractable!(usize);
77impl_extractable!(u8);
78impl_extractable!(u32);
79
80impl<Req, State, Res> FromRequest<Req, State, Res> for &mut Bundle<Req, State, Res>
81where Req: Send, State: Send, Res: Send 
82{
83    fn from_request(bundle: &mut Bundle<Req, State, Res>) -> Option<Self> {
84        Some(unsafe { &mut *(bundle as *mut Bundle<Req, State, Res>) })
85    }
86}
87
88impl<State, Res> FromRequest<Box<dyn Any + Send>, State, Res> for &mut Box<dyn Any + Send>
89where
90    State: Send,
91    Res: Send,
92{
93    fn from_request(bundle: &mut Bundle<Box<dyn Any + Send>, State, Res>) -> Option<Self> {
94        Some(unsafe { &mut *(&mut bundle.request as *mut Box<dyn Any + Send>) })
95    }
96}
97
98impl<Message, Extra, State, Res> FromRequest<Request<Message, Extra>, State, Res> for &Message
99where
100    Message: Send,
101    Extra: Send,
102    State: Send,
103    Res: Send,
104{
105    fn from_request(bundle: &mut Bundle<Request<Message, Extra>, State, Res>) -> Option<Self> {
106        Some(unsafe { &*(&bundle.request.message as *const Message) })
107    }
108}
109
110macro_rules! impl_variant_ref {
111    ($message_mod:ident, $variant:ident) => {
112        impl<Extra, State, Res> FromRequest<Request<$message_mod::Message, Extra>, State, Res> for &$message_mod::$variant
113        where
114            Extra: Send,
115            State: Send,
116            Res: Send,
117        {
118            fn from_request(bundle: &mut Bundle<Request<$message_mod::Message, Extra>, State, Res>) -> Option<Self> {
119                if let $message_mod::Message::$variant(inner) = &bundle.request.message {
120                    Some(unsafe { &*(inner as *const $message_mod::$variant) })
121                } else {
122                    None
123                }
124            }
125        }
126
127        impl<State, Res> FromRequest<$message_mod::Message, State, Res> for &$message_mod::$variant
128        where
129            State: Send,
130            Res: Send,
131        {
132            fn from_request(bundle: &mut Bundle<$message_mod::Message, State, Res>) -> Option<Self> {
133                if let $message_mod::Message::$variant(inner) = &bundle.request {
134                    Some(unsafe { &*(inner as *const $message_mod::$variant) })
135                } else {
136                    None
137                }
138            }
139        }
140    };
141}
142
143macro_rules! impl_variant_complex_ref {
144    ($message_mod:ident, $variant:ident) => {
145        impl<Extra, State, Res> FromRequest<Request<Complex<$message_mod::Message>, Extra>, State, Res> for &$message_mod::$variant
146        where
147            Extra: Send,
148            State: Send,
149            Res: Send,
150        {
151            fn from_request(bundle: &mut Bundle<Request<Complex<$message_mod::Message>, Extra>, State, Res>) -> Option<Self> {
152                if let $message_mod::Message::$variant(inner) = &*bundle.request.message {
153                    Some(unsafe { &*std::ptr::from_ref(inner) })
154                } else {
155                    None
156                }
157            }
158        }
159
160        impl<State, Res> FromRequest<Complex<$message_mod::Message>, State, Res> for &$message_mod::$variant
161        where
162            State: Send,
163            Res: Send,
164        {
165            fn from_request(bundle: &mut Bundle<Complex<$message_mod::Message>, State, Res>) -> Option<Self> {
166                if let $message_mod::Message::$variant(inner) = &*bundle.request {
167                    Some(unsafe { &*std::ptr::from_ref(inner) })
168                } else {
169                    None
170                }
171            }
172        }
173    };
174}
175
176macro_rules! impl_variant_gm_from_stoc {
177    ($variant:ident) => {
178        impl<Extra, State, Res> FromRequest<Request<Complex<stoc::Message>, Extra>, State, Res> for &gm::$variant
179        where
180            Extra: Send,
181            State: Send,
182            Res: Send,
183        {
184            fn from_request(bundle: &mut Bundle<Request<Complex<stoc::Message>, Extra>, State, Res>) -> Option<Self> {
185                if let stoc::Message::GameMessage(game_message) = &*bundle.request.message {
186                    if let gm::Message::$variant(inner) = &game_message.message {
187                        return Some(unsafe { &*std::ptr::from_ref(inner) });
188                    }
189                }
190                None
191            }
192        }
193
194        impl<State, Res> FromRequest<Complex<stoc::Message>, State, Res> for &gm::$variant
195        where
196            State: Send,
197            Res: Send,
198        {
199            fn from_request(bundle: &mut Bundle<Complex<stoc::Message>, State, Res>) -> Option<Self> {
200                if let stoc::Message::GameMessage(game_message) = &*bundle.request {
201                    if let gm::Message::$variant(inner) = &game_message.message {
202                        return Some(unsafe { &*std::ptr::from_ref(inner) });
203                    }
204                }
205                None
206            }
207        }
208    };
209}
210
211macro_rules! impl_variant_gm_response_as_stoc {
212    ($variant:ident) => {
213        impl IntoResponse<Response<stoc::Message>> for gm::$variant {
214            fn into_response(self) -> Response<stoc::Message> {
215                Response::Replace(gm::Message::$variant(self).into())
216            }
217        }
218    };
219}
220
221macro_rules! impl_ctos {
222    ($($variant:ident = $flag:literal),* $(,)?) => {
223        $( impl_variant_ref!(ctos, $variant); )*
224        $( impl_variant_complex_ref!(ctos, $variant); )*
225        $( impl_variant_response!(ctos, $variant); )*
226    };
227}
228
229macro_rules! impl_stoc {
230    ($($variant:ident = $flag:literal),* $(,)?) => {
231        $( impl_variant_ref!(stoc, $variant); )*
232        $( impl_variant_complex_ref!(stoc, $variant); )*
233        $( impl_variant_response!(stoc, $variant); )*
234    };
235}
236
237macro_rules! impl_gm {
238    ($($variant:ident = $flag:literal),* $(,)?) => {
239        $( impl_variant_ref!(gm, $variant); )*
240        $( impl_variant_complex_ref!(gm, $variant); )*
241        $( impl_variant_response!(gm, $variant); )*
242        $( impl_variant_gm_from_stoc!($variant); )*
243        $( impl_variant_gm_response_as_stoc!($variant); )*
244    };
245}
246
247/// An enum conforming to the ygopro data flow.
248/// 
249/// Its variants carry no inherent meaning; what each one means is decided by how the
250/// downstream handles the result.
251pub enum Response<Message> {
252    /// Continue processing the message as normal.
253    Continue,
254    /// Message will be replaced with the given message when sending to its target.
255    Replace(Message),
256    /// Message will be replaced with multiple messages when sending to its target.
257    ReplaceMultiple(Vec<Message>),
258    /// This message will not send to its target.
259    Swallow,
260    /// This message will not send to its target, and stop current room.
261    Terminate,
262    /// This message will not send to its target, and kick its source.
263    Kick,
264}
265
266impl<Req, State, Message> FromRequest<Req, State, Response<Message>> for &mut Response<Message> where Req: Send, State: Send, Message: Send + Sync {
267    fn from_request(bundle: &mut Bundle<Req, State, Response<Message>>) -> Option<Self> {
268        Some(unsafe { &mut *(&mut bundle.response as *mut Response<Message>) })
269    }
270}
271
272impl<Message> std::ops::Mul for Response<Message> {
273    type Output = Response<Message>;
274
275    fn mul(self, rhs: Self) -> Self::Output {
276        match (self, rhs) {
277            (Response::Continue, other) | (other, Response::Continue) => other,
278            (Response::Kick, _) | (_, Response::Kick) => Response::Kick,
279            (Response::Terminate, _) | (_, Response::Terminate) => Response::Terminate,
280            (Response::Swallow, _) | (_, Response::Swallow) => Response::Swallow,
281            (Response::Replace(lhs_message), Response::Replace(rhs_message)) => {
282                Response::ReplaceMultiple(vec![lhs_message, rhs_message])
283            }
284            (Response::Replace(message), Response::ReplaceMultiple(mut messages)) => {
285                messages.insert(0, message);
286                Response::ReplaceMultiple(messages)
287            }
288            (Response::ReplaceMultiple(mut messages), Response::Replace(message)) => {
289                messages.push(message);
290                Response::ReplaceMultiple(messages)
291            }
292            (Response::ReplaceMultiple(mut lhs_messages), Response::ReplaceMultiple(mut rhs_messages)) => {
293                lhs_messages.append(&mut rhs_messages);
294                Response::ReplaceMultiple(lhs_messages)
295            }
296        }
297    }
298}
299
300impl<Message> Default for Response<Message> {
301    fn default() -> Self {
302        Response::Continue
303    }
304}
305
306impl IntoResponse<Response<ctos::Message>> for ctos::Message {
307    fn into_response(self) -> Response<ctos::Message> {
308        Response::Replace(self)
309    }
310}
311
312impl IntoResponse<Response<stoc::Message>> for stoc::Message {
313    fn into_response(self) -> Response<stoc::Message> {
314        Response::Replace(self)
315    }
316}
317
318impl IntoResponse<Response<gm::Message>> for gm::Message {
319    fn into_response(self) -> Response<gm::Message> {
320        Response::Replace(self)
321    }
322}
323
324impl IntoResponse<Response<stoc::Message>> for gm::Message {
325    fn into_response(self) -> Response<stoc::Message> {
326        Response::Replace(self.into())
327    }
328}
329
330macro_rules! impl_variant_response {
331    ($message_mod:ident, $variant:ident) => {
332        impl IntoResponse<Response<$message_mod::Message>> for $message_mod::$variant {
333            fn into_response(self) -> Response<$message_mod::Message> {
334                Response::Replace($message_mod::Message::$variant(self))
335            }
336        }
337    };
338}
339
340impl<Message> IntoResponse<Response<Message>> for () {
341    fn into_response(self) -> Response<Message> {
342        Response::Continue
343    }
344}
345
346impl<Message> IntoResponse<Response<Message>> for Infallible {
347    fn into_response(self) -> Response<Message> {
348        Response::Continue
349    }
350}
351
352impl<Message> IntoResponse<Response<Message>> for Vec<Message> {
353    fn into_response(self) -> Response<Message> {
354        Response::ReplaceMultiple(self)
355    }
356}
357
358impl<Message> IntoResponse<Response<Message>> for bool {
359    fn into_response(self) -> Response<Message> {
360        if self { Response::Terminate } else { Response::Continue }
361    }
362}
363
364impl<Message> IntoResponse<Response<Message>> for &'static str {
365    fn into_response(self) -> Response<Message> {
366        match self {
367            "continue" => Response::Continue,
368            "terminate" => Response::Terminate,
369            "kick" => Response::Kick,
370            "cancel" | "_cancel" => Response::Swallow,
371            _ => Response::Continue,
372        }
373    }
374}
375
376impl<Message, T> IntoResponse<Response<Message>> for Option<T> where T: IntoResponse<Response<Message>> {
377    fn into_response(self) -> Response<Message> {
378        match self {
379            Some(value) => value.into_response(),
380            None => Response::Continue,
381        }
382    }
383}
384
385impl<Message, Response1, Response2> IntoResponse<Response<Message>> for Result<Response1, Response2>
386where Response1: IntoResponse<Response<Message>>, Response2: IntoResponse<Response<Message>> {
387    fn into_response(self) -> Response<Message> {
388        match self {
389            Ok(response1) => response1.into_response(),
390            Err(response2) => response2.into_response(),
391        }
392    }
393}
394
395impl<Message> Response<Message> {
396    /// Map the message(s) inside this response to a new type.
397    pub fn map<Message2>(self, mut f: impl FnMut(Message) -> Message2) -> Response<Message2> {
398        match self {
399            Response::Continue => Response::Continue,
400            Response::Replace(message) => Response::Replace(f(message)),
401            Response::ReplaceMultiple(messages) => Response::ReplaceMultiple(messages.into_iter().map(f).collect()),
402            Response::Swallow => Response::Swallow,
403            Response::Terminate => Response::Terminate,
404            Response::Kick => Response::Kick,
405        }
406    }
407}
408
409impl<Req, State, Res> FromRequest<Req, State, Res> for &mut crate::StopFlag
410where Req: Send, State: Send, Res: Send {
411    fn from_request(bundle: &mut Bundle<Req, State, Res>) -> Option<Self> {
412        Some(unsafe { &mut *(&mut bundle.stop_flag as *mut crate::StopFlag) })
413    }
414}
415
416ygopro_data::every_client_to_server_flat_message!(impl_ctos);
417ygopro_data::every_server_to_client_flat_message!(impl_stoc);
418ygopro_data::every_game_message_flat_message!(impl_gm);
419
420/// A state that exposes an `anymap` by shared reference.
421///
422/// Data is taken out by cloning (`CloneAny`), so it is only suitable for small,
423/// read-only values such as configuration.
424pub trait ContainsMap {
425    /// Get the `anymap` by shared reference.
426    fn get_map(&self) -> &anymap3::Map<dyn anymap3::CloneAny + Send>;
427}
428
429impl<S: ContainsMap> ContainsMap for std::mem::ManuallyDrop<S> {
430    fn get_map(&self) -> &anymap3::Map<dyn anymap3::CloneAny + Send> {
431        ContainsMap::get_map(&**self)
432    }
433}
434
435/// A state that exposes an `anymap` by mutable reference.
436pub trait ContainsMapMut {
437    /// Get the `anymap` by mutable reference.
438    fn get_map(&mut self) -> &mut anymap3::Map<dyn std::any::Any + Send>;
439}
440
441impl<S: ContainsMapMut> ContainsMapMut for std::mem::ManuallyDrop<S> {
442    fn get_map(&mut self) -> &mut anymap3::Map<dyn std::any::Any + Send> {
443        ContainsMapMut::get_map(&mut **self)
444    }
445}
446
447// impl<Req, State, Res> FromRequest<Req, State, Res> for &anymap3::Map<dyn anymap3::CloneAny + Send> where State: ContainsMap + Send, Req: Send, Res: Send {
448//     fn from_request(bundle: &mut Bundle<Req, State, Res>) -> Option<Self> {
449//         Some(unsafe { &*(bundle.state.get_map() as *const anymap3::Map<dyn anymap3::CloneAny + Send> )})
450//     }
451// }
452
453impl<Req, State, Res> FromRequest<Req, State, Res> for &mut anymap3::Map<dyn std::any::Any + Send> where State: ContainsMapMut + Send, Req: Send, Res: Send {
454    fn from_request(bundle: &mut Bundle<Req, State, Res>) -> Option<Self> {
455        Some(unsafe { &mut *(bundle.state.get_map() as *mut anymap3::Map<dyn std::any::Any + Send> )})
456    }
457}