Skip to content

Commit 5fc4358

Browse files
authored
Merge pull request #1191 from godot-rust/feature/vslice
`vslice![a, b]` for variant slices
2 parents fc43834 + 6c7dd39 commit 5fc4358

File tree

15 files changed

+110
-94
lines changed

15 files changed

+110
-94
lines changed

godot-core/src/builtin/collections/array.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,8 @@ macro_rules! array {
15791579
/// To create a typed `Array` with a single element type, see the [`array!`] macro.
15801580
///
15811581
/// For dictionaries, a similar macro [`dict!`] exists.
1582+
///
1583+
/// To construct slices of variants, use [`vslice!`].
15821584
#[macro_export]
15831585
macro_rules! varray {
15841586
// Note: use to_variant() and not Variant::from(), as that works with both references and values
@@ -1594,6 +1596,49 @@ macro_rules! varray {
15941596
};
15951597
}
15961598

1599+
/// Constructs a slice of [`Variant`] literals, useful for passing to vararg functions.
1600+
///
1601+
/// Many APIs in Godot have variable-length arguments. GDScript can call such functions by simply passing more arguments, but in Rust,
1602+
/// the parameter type `&[Variant]` is used.
1603+
///
1604+
/// This macro creates a [slice](https://doc.rust-lang.org/std/primitive.slice.html) of `Variant` values.
1605+
///
1606+
/// # Examples
1607+
/// Variable number of arguments:
1608+
/// ```no_run
1609+
/// # use godot::prelude::*;
1610+
/// let slice: &[Variant] = vslice![42, "hello", true];
1611+
///
1612+
/// let concat: GString = godot::global::str(slice);
1613+
/// ```
1614+
/// _(In practice, you might want to use [`godot_str!`][crate::global::godot_str] instead of `str()`.)_
1615+
///
1616+
/// Dynamic function call via reflection. NIL can still be passed inside `vslice!`, just use `Variant::nil()`.
1617+
/// ```no_run
1618+
/// # use godot::prelude::*;
1619+
/// # fn some_object() -> Gd<Object> { unimplemented!() }
1620+
/// let mut obj: Gd<Object> = some_object();
1621+
/// obj.call("some_method", vslice![Vector2i::new(1, 2), Variant::nil()]);
1622+
/// ```
1623+
///
1624+
/// # See also
1625+
/// To create typed and untyped `Array`s, use the [`array!`] and [`varray!`] macros respectively.
1626+
///
1627+
/// For dictionaries, a similar macro [`dict!`] exists.
1628+
#[macro_export]
1629+
macro_rules! vslice {
1630+
// Note: use to_variant() and not Variant::from(), as that works with both references and values
1631+
($($elements:expr),* $(,)?) => {
1632+
{
1633+
use $crate::meta::ToGodot as _;
1634+
let mut array = $crate::builtin::VariantArray::default();
1635+
&[
1636+
$( $elements.to_variant(), )*
1637+
]
1638+
}
1639+
};
1640+
}
1641+
15971642
// ----------------------------------------------------------------------------------------------------------------------------------------------
15981643

15991644
#[cfg(feature = "serde")]

godot-core/src/builtin/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub mod __prelude_reexport {
4848
pub use vectors::*;
4949

5050
pub use super::{EulerOrder, Side, VariantOperator, VariantType};
51-
pub use crate::{array, dict, real, reals, varray};
51+
pub use crate::{array, dict, real, reals, varray, vslice};
5252
}
5353

5454
pub use __prelude_reexport::*;

itest/rust/src/builtin_tests/containers/callable_test.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use crate::framework::itest;
99
use godot::builtin::{
10-
array, dict, varray, Array, Callable, Color, GString, NodePath, StringName, Variant,
10+
array, dict, varray, vslice, Array, Callable, Color, GString, NodePath, StringName, Variant,
1111
VariantArray, Vector2,
1212
};
1313
use godot::classes::{Node2D, Object, RefCounted};
@@ -104,20 +104,20 @@ fn callable_variant_method() {
104104
// Dictionary
105105
let dict = dict! { "one": 1, "value": 2 };
106106
let dict_get = Callable::from_variant_method(&dict.to_variant(), "get");
107-
assert_eq!(dict_get.call(&["one".to_variant()]), 1.to_variant());
107+
assert_eq!(dict_get.call(vslice!["one"]), 1.to_variant());
108108

109109
// GString
110110
let string = GString::from("some string").to_variant();
111111
let string_md5 = Callable::from_variant_method(&string, "md5_text");
112112
assert_eq!(
113-
string_md5.call(&[]),
113+
string_md5.call(vslice![]), // use vslice![] as alternative &[] syntax.
114114
"5ac749fbeec93607fc28d666be85e73a".to_variant()
115115
);
116116

117117
// Object
118118
let obj = CallableTestObj::new_gd().to_variant();
119119
let obj_stringify = Callable::from_variant_method(&obj, "stringify_int");
120-
assert_eq!(obj_stringify.call(&[10.to_variant()]), "10".to_variant());
120+
assert_eq!(obj_stringify.call(vslice![10]), "10".to_variant());
121121

122122
// Vector3
123123
let vector = Vector2::new(-1.2, 2.5).to_variant();
@@ -220,18 +220,15 @@ fn callable_call() {
220220
let callable = obj.callable("assign_int");
221221

222222
assert_eq!(obj.bind().value, 0);
223-
callable.call(&[10.to_variant()]);
223+
callable.call(vslice![10]);
224224
assert_eq!(obj.bind().value, 10);
225225

226-
callable.call(&[20.to_variant(), 30.to_variant()]);
226+
callable.call(vslice![20, 30]);
227227
assert_eq!(obj.bind().value, 10);
228228

229-
assert_eq!(callable.call(&["string".to_variant()]), Variant::nil());
229+
assert_eq!(callable.call(vslice!["string"]), Variant::nil());
230230

231-
assert_eq!(
232-
Callable::invalid().call(&[1.to_variant(), 2.to_variant(), 3.to_variant()]),
233-
Variant::nil()
234-
);
231+
assert_eq!(Callable::invalid().call(vslice![1, 2, 3]), Variant::nil());
235232
}
236233

237234
#[itest]
@@ -259,11 +256,11 @@ fn callable_call_engine() {
259256
assert_eq!(cb.method_name(), Some(StringName::from("set_position")));
260257

261258
let pos = Vector2::new(5.0, 7.0);
262-
cb.call(&[pos.to_variant()]);
259+
cb.call(vslice![pos]);
263260
assert_eq!(obj.get_position(), pos);
264261

265262
let pos = Vector2::new(1.0, 23.0);
266-
let bound = cb.bind(&[pos.to_variant()]);
263+
let bound = cb.bind(vslice![pos]);
267264
bound.call(&[]);
268265
assert_eq!(obj.get_position(), pos);
269266

@@ -287,7 +284,7 @@ fn callable_bindv() {
287284
fn callable_bind() {
288285
let obj = CallableTestObj::new_gd();
289286
let callable = obj.callable("stringify_int");
290-
let callable_bound = callable.bind(&[10.to_variant()]);
287+
let callable_bound = callable.bind(vslice![10]);
291288

292289
assert_eq!(
293290
callable_bound.call(&[]),
@@ -303,12 +300,7 @@ fn callable_unbind() {
303300
let callable_unbound = callable.unbind(3);
304301

305302
assert_eq!(
306-
callable_unbound.call(&[
307-
121.to_variant(),
308-
20.to_variant(),
309-
30.to_variant(),
310-
40.to_variant()
311-
]),
303+
callable_unbound.call(vslice![121, 20, 30, 40]),
312304
121.to_variant().stringify().to_variant()
313305
);
314306
}
@@ -327,9 +319,7 @@ fn callable_get_argument_count() {
327319
let concat_array = obj.callable("concat_array");
328320
assert_eq!(concat_array.get_argument_count(), 4);
329321
assert_eq!(
330-
concat_array
331-
.bind(&[10.to_variant(), "hello".to_variant()])
332-
.get_argument_count(),
322+
concat_array.bind(vslice![10, "hello"]).get_argument_count(),
333323
2
334324
);
335325
}

itest/rust/src/builtin_tests/containers/signal_test.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
use crate::framework::itest;
9-
use godot::builtin::{GString, Signal, StringName};
9+
use godot::builtin::{vslice, GString, Signal, StringName};
1010
use godot::classes::object::ConnectFlags;
1111
use godot::classes::{Node, Node3D, Object, RefCounted};
1212
use godot::meta::ToGodot;
@@ -27,7 +27,7 @@ fn signal_basic_connect_emit() {
2727
assert_eq!(receiver.bind().last_received(), LastReceived::Unit);
2828

2929
emitter.connect("signal_int", &receiver.callable("receive_int"));
30-
emitter.emit_signal("signal_int", &[1278.to_variant()]);
30+
emitter.emit_signal("signal_int", vslice![1278]);
3131
assert_eq!(receiver.bind().last_received(), LastReceived::Int(1278));
3232

3333
let emitter_variant = emitter.to_variant();
@@ -636,9 +636,8 @@ impl PubClassPrivSignal {
636636

637637
#[cfg(since_api = "4.2")]
638638
mod custom_callable {
639-
use godot::builtin::{Callable, Signal};
639+
use godot::builtin::{vslice, Callable, Signal};
640640
use godot::classes::Node;
641-
use godot::meta::ToGodot;
642641
use godot::obj::{Gd, NewAlloc};
643642
use std::sync::atomic::{AtomicU32, Ordering};
644643
use std::sync::Arc;
@@ -655,7 +654,7 @@ mod custom_callable {
655654
node.add_user_signal("test_signal");
656655
},
657656
|node| {
658-
node.emit_signal("test_signal", &[987i64.to_variant()]);
657+
node.emit_signal("test_signal", vslice![987i64]);
659658
},
660659
);
661660
}
@@ -669,7 +668,7 @@ mod custom_callable {
669668
node.add_user_signal("test_signal");
670669
},
671670
|node| {
672-
node.emit_signal("test_signal", &[987i64.to_variant()]);
671+
node.emit_signal("test_signal", vslice![987i64]);
673672
},
674673
);
675674
}

itest/rust/src/builtin_tests/containers/variant_test.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use std::cmp::Ordering;
99
use std::fmt::Display;
1010

1111
use godot::builtin::{
12-
array, dict, varray, Array, GString, NodePath, Signal, StringName, Variant, Vector2, Vector3,
12+
array, dict, varray, vslice, Array, GString, NodePath, Signal, StringName, Variant, Vector2,
13+
Vector3,
1314
};
1415
use godot::builtin::{Basis, Dictionary, VariantArray, VariantOperator, VariantType};
1516
use godot::classes::{Node, Node2D};
@@ -308,7 +309,7 @@ fn variant_call() {
308309

309310
// Object
310311
let position = Vector2::new(4.0, 5.0);
311-
let result = variant.call("set_position", &[position.to_variant()]);
312+
let result = variant.call("set_position", vslice![position]);
312313
assert!(result.is_nil());
313314

314315
let result = variant
@@ -333,7 +334,7 @@ fn variant_call() {
333334
// Vector2
334335
let vector = Vector2::new(5.0, 3.0);
335336
let vector_rhs = Vector2::new(1.0, -1.0);
336-
let result = vector.to_variant().call("dot", &[vector_rhs.to_variant()]);
337+
let result = vector.to_variant().call("dot", vslice![vector_rhs]);
337338
assert_eq!(result, 2.0.to_variant());
338339

339340
// Dynamic checks are only available in Debug builds.
@@ -478,7 +479,7 @@ fn variant_null_object_is_nil() {
478479

479480
// Simulates an object that is returned but null
480481
// Use reflection to get a variant as return type
481-
let variant = node.call("get_node_or_null", &[node_path.to_variant()]);
482+
let variant = node.call("get_node_or_null", vslice![node_path]);
482483
let raw_type: sys::GDExtensionVariantType =
483484
unsafe { sys::interface_fn!(variant_get_type)(variant.var_sys()) };
484485

itest/rust/src/engine_tests/async_test.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77

88
use std::ops::Deref;
99

10-
use godot::builtin::{array, Array, Callable, Signal, Variant};
10+
use godot::builtin::{array, vslice, Array, Callable, Signal, Variant};
1111
use godot::classes::{Object, RefCounted};
12-
use godot::meta::ToGodot;
1312
use godot::obj::{Base, Gd, NewAlloc, NewGd};
1413
use godot::prelude::{godot_api, GodotClass};
1514
use godot::task::{self, create_test_signal_future_resolver, SignalFuture, TaskHandle};
@@ -50,10 +49,7 @@ fn start_async_task() -> TaskHandle {
5049

5150
let ref_counted_arg = RefCounted::new_gd();
5251

53-
object.emit_signal(
54-
"custom_signal",
55-
&[10.to_variant(), ref_counted_arg.to_variant()],
56-
);
52+
object.emit_signal("custom_signal", vslice![10, ref_counted_arg]);
5753

5854
task_handle
5955
}
@@ -77,7 +73,7 @@ fn async_task_array() -> TaskHandle {
7773

7874
object.emit_signal(
7975
"custom_signal_array",
80-
&[array![1, 2, 3].to_variant(), ref_counted_arg.to_variant()],
76+
vslice![array![1, 2, 3], ref_counted_arg],
8177
);
8278

8379
task_handle
@@ -154,7 +150,7 @@ fn signal_future_non_send_arg_panic() -> TaskHandle {
154150
std::thread::spawn(move || {
155151
let mut object = unsafe { object.extract() };
156152

157-
object.emit_signal("custom_signal", &[RefCounted::new_gd().to_variant()])
153+
object.emit_signal("custom_signal", vslice![RefCounted::new_gd()])
158154
});
159155

160156
handle
@@ -181,7 +177,7 @@ fn signal_future_send_arg_no_panic() -> TaskHandle {
181177
std::thread::spawn(move || {
182178
let mut object = unsafe { object.extract() };
183179

184-
object.emit_signal("custom_signal", &[1u8.to_variant()])
180+
object.emit_signal("custom_signal", vslice![1u8])
185181
});
186182

187183
handle

itest/rust/src/engine_tests/native_st_niche_pointer_test.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@
1111

1212
use std::ptr;
1313

14-
use godot::builtin::{Dictionary, Rect2, Rid};
14+
use godot::builtin::{vslice, Dictionary, Rect2, Rid};
1515
use godot::classes::native::{CaretInfo, Glyph, ObjectId, PhysicsServer2DExtensionShapeResult};
1616
use godot::classes::text_server::Direction;
1717
use godot::classes::{IRefCounted, Node3D, RefCounted};
18-
use godot::meta::ToGodot;
1918
use godot::obj::{Base, NewAlloc, NewGd};
2019
use godot::register::{godot_api, GodotClass};
2120

@@ -80,7 +79,7 @@ fn native_structure_parameter() {
8079

8180
let ptr = ptr::addr_of!(caret);
8281
let mut object = NativeStructTests::new_gd();
83-
let result: Dictionary = object.call("pass_native_struct", &[ptr.to_variant()]).to();
82+
let result: Dictionary = object.call("pass_native_struct", vslice![ptr]).to();
8483

8584
assert_eq!(
8685
result.at("leading_caret").to::<Rect2>(),

itest/rust/src/engine_tests/node_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use std::str::FromStr;
99

10-
use godot::builtin::{NodePath, Variant};
10+
use godot::builtin::{vslice, NodePath};
1111
use godot::classes::{Node, Node3D, PackedScene, SceneTree};
1212
use godot::global;
1313
use godot::obj::{NewAlloc, NewGd};
@@ -90,5 +90,5 @@ fn node_call_group(ctx: &TestContext) {
9090
let mut tree = node.get_tree().unwrap();
9191

9292
node.add_to_group("group");
93-
tree.call_group("group", "set_name", &[Variant::from("name")]);
93+
tree.call_group("group", "set_name", vslice!["name"]);
9494
}

itest/rust/src/engine_tests/utilities_test.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use crate::framework::itest;
1212

13-
use godot::builtin::{GString, Variant};
13+
use godot::builtin::{vslice, GString, Variant};
1414
use godot::global::*;
1515

1616
#[itest]
@@ -35,12 +35,7 @@ fn utilities_str() {
3535
let b = " is a ";
3636
let c = true;
3737
let d = " number";
38-
let concat = str(&[
39-
Variant::from(a),
40-
Variant::from(b),
41-
Variant::from(c),
42-
Variant::from(d),
43-
]);
38+
let concat = str(vslice![a, b, c, d]);
4439

4540
let empty = str(&[]);
4641

@@ -69,17 +64,13 @@ fn utilities_wrap() {
6964

7065
#[itest]
7166
fn utilities_max() {
72-
let output = max(
73-
&Variant::from(1.0),
74-
&Variant::from(3.0),
75-
&[Variant::from(5.0), Variant::from(7.0)],
76-
);
67+
let output = max(&Variant::from(1.0), &Variant::from(3.0), vslice![5.0, 7.0]);
7768
assert_eq!(output, Variant::from(7.0));
7869

7970
let output = max(
8071
&Variant::from(-1.0),
8172
&Variant::from(-3.0),
82-
&[Variant::from(-5.0), Variant::from(-7.0)],
73+
vslice![-5.0, -7.0],
8374
);
8475
assert_eq!(output, Variant::from(-1.0));
8576
}

0 commit comments

Comments
 (0)