godot_core/classes/match_class.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8/// Dispatches a class to different subclasses.
9///
10/// Similar to a `match` statement, but with downcasts. Earlier matches dominate, so keep more-derived classes first.
11/// The current implementation checks [`Gd::try_cast()`][crate::obj::Gd::try_cast] linearly with the number of branches.
12/// This may change in the future.
13///
14/// When none of the listed classes match, a _fallback branch_ acts as a catch-all and allows to retrieve the original `Gd` pointer.
15/// If the type of the `match_class!` expression is `()`, you can omit the fallback branch. For all other types, it is required, even if all
16/// direct subclasses are handled. The reason for this is that there may be other subclasses which are not statically known by godot-rust
17/// (e.g. from a script or GDExtension).
18///
19/// The fallback branch can either be `_` (discard object), or `variable` to access the original object inside the fallback arm.
20///
21/// # Example
22/// ```no_run
23/// # use godot::prelude::*;
24/// # use godot_core::classes::{InputEvent, InputEventAction};
25/// # fn some_input() -> Gd<InputEvent> { unimplemented!() }
26/// # // Hack to keep amount of SELECTED_CLASSES limited:
27/// # type InputEventMouseButton = InputEventAction;
28/// # type InputEventMouseMotion = InputEventAction;
29/// # type InputEventKey = InputEventAction;
30/// // Basic syntax.
31/// let event: Gd<InputEvent> = some_input();
32///
33/// let simple_dispatch: i32 = match_class! { event,
34/// button @ InputEventMouseButton => 1,
35/// motion @ InputEventMouseMotion => 2,
36/// action @ InputEventAction => 3,
37/// key @ InputEventKey => 4,
38/// _ => 0, // Fallback.
39/// };
40///
41/// // More diverse dispatch patterns are also supported.
42/// let fancy_dispatch: i32 = match_class! { some_input(),
43/// // Mutable bindings supported:
44/// mut button @ InputEventMouseButton => 1,
45///
46/// // Block syntax for multiple statements:
47/// motion @ InputEventMouseMotion => {
48/// godot_print!("motion");
49/// 2
50/// },
51///
52/// // Qualified types supported:
53/// action @ godot::classes::InputEventAction => 3,
54///
55/// // If you're only interested in the class and not the object,
56/// // discard it with either `_` or `_variable`:
57/// _ @ InputEventKey => 4,
58///
59/// // Fallback with variable -- retrieves original Gd<InputEvent>.
60/// original => 0,
61/// // Can also be used with mut:
62/// // mut original => 0,
63/// // If the match arms have type (), we can also omit the fallback branch.
64/// };
65///
66/// // event_type is now 0, 1, 2, 3 or 4
67/// ```
68///
69/// # Expression and control flow
70/// The `match_class!` macro is an expression, as such it has a type. If that type is not `()`, you typically need to use the expression or
71/// end it with a semicolon.
72///
73/// Control-flow statements like `?`, `return`, `continue`, `break` can be used within the match arms.
74#[macro_export]
75// Note: annoyingly shows full implementation in docs. For workarounds, either move impl to a helper macro, or use something like
76// https://crates.io/crates/clean-macro-docs.
77// Earlier syntax expected curly braces, i.e.: ($subject:expr, { $($tt:tt)* }) => {{ ... }};
78macro_rules! match_class {
79 ($subject:expr, $($tt:tt)*) => {{
80 let subject = $subject;
81 $crate::match_class_muncher!(subject, $($tt)*)
82 }};
83}
84
85#[doc(hidden)]
86#[macro_export]
87macro_rules! match_class_muncher {
88 // mut variable @ Class => { ... }.
89 ($subject:ident, mut $var:ident @ $Ty:ty => $block:expr, $($rest:tt)*) => {{
90 match $subject.try_cast::<$Ty>() {
91 Ok(mut $var) => $block,
92 Err(__obj) => {
93 $crate::match_class_muncher!(__obj, $($rest)*)
94 }
95 }
96 }};
97
98 // variable @ Class => { ... }.
99 ($subject:ident, $var:ident @ $Ty:ty => $block:expr, $($rest:tt)*) => {{
100 match $subject.try_cast::<$Ty>() {
101 Ok($var) => $block,
102 Err(__obj) => {
103 $crate::match_class_muncher!(__obj, $($rest)*)
104 }
105 }
106 }};
107
108 // _ @ Class => { ... }.
109 ($subject:ident, _ @ $Ty:ty => $block:expr, $($rest:tt)*) => {{
110 match $subject.try_cast::<$Ty>() {
111 Ok(_) => $block,
112 Err(__obj) => {
113 $crate::match_class_muncher!(__obj, $($rest)*)
114 }
115 }
116 }};
117
118 // mut variable => { ... }.
119 ($subject:ident, mut $var:ident => $block:expr $(,)?) => {{
120 let mut $var = $subject;
121 $block
122 }};
123
124 // variable => { ... }.
125 ($subject:ident, $var:ident => $block:expr $(,)?) => {{
126 let $var = $subject;
127 $block
128 }};
129
130 // _ => { ... }
131 // or nothing, if fallback is absent and overall expression being ().
132 ($subject:ident, $(_ => $block:expr $(,)?)?) => {{
133 $($block)?
134 }};
135}