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}