|
13 | 13 | ///
|
14 | 14 | /// Requires a fallback branch, even if all direct known classes are handled. The reason for this is that there may be other subclasses which
|
15 | 15 | /// are not statically known by godot-rust (e.g. from a script or GDExtension). The fallback branch can either be `_` (discard object), or
|
16 |
| -/// `_(variable)` to access the original object inside the fallback arm. |
| 16 | +/// `variable @ _` to access the original object inside the fallback arm. |
17 | 17 | ///
|
18 | 18 | /// # Example
|
19 | 19 | /// ```no_run
|
|
27 | 27 | /// let event: Gd<InputEvent> = some_input();
|
28 | 28 | ///
|
29 | 29 | /// let simple_dispatch: i32 = match_class!(event, {
|
30 |
| -/// InputEventMouseButton(btn) => 1, |
31 |
| -/// InputEventMouseMotion(motion) => 2, |
32 |
| -/// InputEventAction(action) => 3, |
| 30 | +/// button @ InputEventMouseButton => 1, |
| 31 | +/// motion @ InputEventMouseMotion => 2, |
| 32 | +/// action @ InputEventAction => 3, |
33 | 33 | /// _ => 0, // Fallback.
|
34 | 34 | /// });
|
35 | 35 | ///
|
36 | 36 | /// // More diverse dispatch patterns are also supported.
|
37 | 37 | /// let fancy_dispatch: i32 = match_class!(some_input(), {
|
38 |
| -/// InputEventMouseButton(btn) => 1, |
| 38 | +/// button @ InputEventMouseButton => 1, |
39 | 39 | ///
|
40 | 40 | /// // Block syntax for multiple statements:
|
41 |
| -/// InputEventMouseMotion(motion) => { |
| 41 | +/// motion @ InputEventMouseMotion => { |
42 | 42 | /// godot_print!("motion");
|
43 | 43 | /// 2
|
44 | 44 | /// },
|
45 | 45 | ///
|
46 | 46 | /// // Qualified types supported:
|
47 |
| -/// godot::classes::InputEventAction(action) => 3, |
| 47 | +/// action @ godot::classes::InputEventAction => 3, |
48 | 48 | ///
|
49 | 49 | /// // Fallback with variable -- retrieves original Gd<InputEvent>.
|
50 |
| -/// // Equivalent to pattern `InputEvent(original)`. |
51 |
| -/// _(original) => 0, |
| 50 | +/// // Equivalent to pattern `original @ InputEvent`. |
| 51 | +/// original @ _ => 0, |
52 | 52 | /// });
|
53 | 53 | ///
|
54 | 54 | /// // event_type is now 0, 1, 2, or 3
|
|
60 | 60 | // Note: annoyingly shows full implementation in docs. For workarounds, either move impl to a helper macro, or use something like
|
61 | 61 | // https://crates.io/crates/clean-macro-docs.
|
62 | 62 | macro_rules! match_class {
|
63 |
| - ($subject:expr, { |
64 |
| - $( |
65 |
| - $($class:ident)::+($var:ident) => $body:expr |
66 |
| - ),+, |
67 |
| - _($fallback_var:ident) => $fallback:expr |
68 |
| - $(,)? |
69 |
| - }) => { |
| 63 | + // TT muncher approach: consume one arm at a time and recurse with the remaining tokens. |
| 64 | + |
| 65 | + // Entry point: grab subject + ENTIRE list of arms as token-trees. |
| 66 | + ($subject:expr, { $($arms:tt)* }) => { |
70 | 67 | (|| {
|
71 |
| - let mut __evt = $subject; |
72 |
| - $( |
73 |
| - __evt = match __evt.try_cast::<$($class)::*>() { |
74 |
| - Ok($var) => return $body, |
75 |
| - Err(e) => e, |
76 |
| - }; |
77 |
| - )+ |
78 |
| - let $fallback_var = __evt; |
79 |
| - $fallback |
| 68 | + let mut __match_subject = $subject; |
| 69 | + match_class!(@munch __match_subject; $($arms)*); |
| 70 | + // unreachable!("match_class hit end with no `_` fallback"); |
80 | 71 | })()
|
81 | 72 | };
|
82 | 73 |
|
83 |
| - ($subject:expr, { |
84 |
| - $( |
85 |
| - $($class:ident)::+($var:ident) => $body:expr |
86 |
| - ),+, |
87 |
| - _ => $fallback:expr |
| 74 | + // ident @ Some::Path => expr, rest... |
| 75 | + (@munch $evt:ident; |
| 76 | + $var:ident @ $($class:ident)::+ => $body:expr, |
| 77 | + $($rest:tt)* |
| 78 | + ) => { |
| 79 | + // try the down‐cast |
| 80 | + $evt = match $evt.try_cast::< $($class)::* >() { |
| 81 | + Ok($var) => return $body, |
| 82 | + Err(e) => e, |
| 83 | + }; |
| 84 | + match_class!(@munch $evt; $($rest)*); |
| 85 | + }; |
| 86 | + |
| 87 | + // foo @ _ => expr |
| 88 | + (@munch $evt:ident; |
| 89 | + $fallback_var:ident @ $pat:tt => $fallback:expr |
88 | 90 | $(,)?
|
89 |
| - }) => { |
90 |
| - (|| { |
91 |
| - let mut __evt = $subject; |
92 |
| - $( |
93 |
| - __evt = match __evt.try_cast::<$($class)::*>() { |
94 |
| - Ok($var) => return $body, |
95 |
| - Err(e) => e, |
96 |
| - }; |
97 |
| - )+ |
98 |
| - $fallback |
99 |
| - })() |
| 91 | + ) => { |
| 92 | + // `$pat` here will only ever be `_` if no typed‐arm matched first, because `Some::Path` would hit the more specific rule above. |
| 93 | + let $fallback_var = $evt; |
| 94 | + return $fallback; |
100 | 95 | };
|
| 96 | + |
| 97 | + // _ => expr |
| 98 | + (@munch $evt:ident; |
| 99 | + _ => $fallback:expr |
| 100 | + $(,)? |
| 101 | + ) => {{ |
| 102 | + return $fallback; |
| 103 | + }}; |
101 | 104 | }
|
0 commit comments