Skip to content

Commit c22d860

Browse files
bors[bot]ttencatelilizoey
authored
Merge #93 #94 #97
93: Add some documentation about memory management and thread safety r=Bromeon a=ttencate 94: Add Contributing.md r=Bromeon a=ttencate 97: Add Typechecking to FromVariant r=Bromeon a=sayaks Also add some testing for `Variant::get_type()`. This *should* fix #95, at least if `try_from_variant` breaks now it's an indication of a deeper issue and not just that there is no implementation. Co-authored-by: Thomas ten Cate <[email protected]> Co-authored-by: Lili Zoey <[email protected]>
4 parents 59a914d + 2bdab6b + 156e6be + 67b2bf7 commit c22d860

File tree

5 files changed

+181
-7
lines changed

5 files changed

+181
-7
lines changed

Contributing.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Contributing to `gdextension`
2+
3+
At this stage, we appreciate if users experiment with the library, use it in small projects and report issues and bugs they encounter.
4+
5+
If you plan to make bigger contributions, make sure to discuss them in a [GitHub issue] first. Since the library is evolving quickly, this avoids that multiple people work on the same thing or implement features in a way that doesn't work with other parts. Also don't hesitate to talk to the developers in the `#dev-gdextension` channel on [Discord]!
6+
7+
## Check script
8+
9+
The script `check.sh` in the project root can be used to mimic a CI run locally. It's useful to run this before you commit, push or create a pull request:
10+
11+
```
12+
$ check.sh
13+
```
14+
15+
At the time of writing, this will run formatting, clippy, unit tests and integration tests. More checks may be added in the future. Run `./check.sh --help` to see all available options.
16+
17+
If you like, you can set this as a pre-commit hook in your local clone of the repository:
18+
19+
```
20+
$ ln -sf check.sh .git/hooks/pre-commit
21+
```
22+
23+
## Unit tests
24+
25+
Because most of `gdextension` interacts with the Godot engine, which is not available from the test executable, unit tests (using `cargo test` and the `#[test]` attribute) are pretty limited in scope.
26+
27+
Because additional flags might be needed, the preferred way to run unit tests is through the `check.sh` script:
28+
29+
```
30+
$ ./check.sh test
31+
```
32+
33+
## Integration tests
34+
35+
The `itest/` directory contains a suite of integration tests that actually exercise `gdextension` from within Godot.
36+
37+
The `itest/rust` directory is a Rust `cdylib` library project that can be loaded as a GDExtension in Godot, with an entry point for running integration tests. The `itest/godot` directory contains the Godot project that loads this library and invokes the test suite.
38+
39+
You can run the integration tests like this:
40+
41+
```
42+
$ ./check.sh itest
43+
```
44+
45+
Just like when compiling the crate, the `GODOT4_BIN` environment variable can be used to supply the path and filename of your Godot executable.
46+
47+
## Formatting
48+
49+
`rustfmt` is used to format code. `check.sh` only warns about formatting issues, but does not fix them. To do that, run:
50+
51+
```
52+
$ cargo fmt
53+
```
54+
55+
## Clippy
56+
57+
`clippy` is used for additional lint warnings not implemented in `rustc`. This, too, is best run through `check.sh`:
58+
59+
```
60+
$ check.sh clippy
61+
```
62+
63+
[GitHub issue]: https://github.com/godot-rust/gdextension/issues
64+
[Discord]: https://discord.gg/aKUCJ8rJsc

ReadMe.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,7 @@ those changes available (and only those, no surrounding code).
9090

9191
## Contributing
9292

93-
At this stage, we appreciate if users experiment with the library, use it in small projects and report issues and bugs they encounter.
94-
95-
If you plan to make bigger contributions, make sure to discuss them in a GitHub issue first. Since the library is evolving quickly, this
96-
avoids that multiple people work on the same thing or implement features in a way that doesn't work with other parts. Also don't hesitate
97-
to talk to the developers in the `#gdext-dev` channel on [Discord]!
98-
93+
Contributions are very welcome! If you want to help out, see [`Contributing.md`](Contributing.md) for some pointers on getting started!
9994

10095
[Godot]: https://godotengine.org
10196
[`gdnative`]: https://github.com/godot-rust/gdnative

godot-core/src/builtin/variant/impls.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ macro_rules! impl_variant_traits {
4848
// void String::operator=(const String &p_str) { _cowdata._ref(p_str._cowdata); }
4949
// does a copy-on-write and explodes if this->_cowdata is not initialized.
5050
// We can thus NOT use Self::from_sys_init().
51-
51+
if variant.get_type() != Self::variant_type() {
52+
return Err(VariantConversionError)
53+
}
5254
let mut value = <$T>::default();
5355
let result = unsafe {
5456
let converter = sys::builtin_fn!($to_fn);

godot/src/lib.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,70 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
//! Rust bindings for GDExtension, the extension API of [Godot](https://godotengine.org/) 4.
8+
//!
9+
//! This documentation is a work in progress.
10+
//!
11+
//! # Kinds of types
12+
//!
13+
//! Godot is written in C++, which doesn't have the same strict guarantees about safety and
14+
//! mutability that Rust does. As a result, not everything in this crate will look and feel
15+
//! entirely "Rusty". We distinguish four different kinds of types:
16+
//!
17+
//! 1. **Value types**: `i64`, `f64`, and mathematical types like
18+
//! [`Vector2`][crate::builtin::Vector2] and [`Color`][crate::builtin::Color].
19+
//!
20+
//! These are the simplest to understand and to work with. They implement `Clone` and often
21+
//! `Copy` as well. They are implemented with the same memory layout as their counterparts in
22+
//! Godot itself, and typically have public fields. <br><br>
23+
//!
24+
//! 2. **Copy-on-write types**: [`GodotString`][crate::builtin::GodotString],
25+
//! [`StringName`][crate::builtin::StringName], and `Packed*Array` types.
26+
//!
27+
//! These mostly act like value types, similar to Rust's own `Vec`. You can `Clone` them to get
28+
//! a full copy of the entire object, as you would expect.
29+
//!
30+
//! Under the hood in Godot, these types are implemented with copy-on-write, so that data can be
31+
//! shared until one of the copies needs to be modified. However, this performance optimization
32+
//! is entirely hidden from the API and you don't normally need to worry about it. <br><br>
33+
//!
34+
//! 3. **Reference-counted types**: [`Array`][crate::builtin::Array],
35+
//! [`Dictionary`][crate::builtin::Dictionary], and [`Gd<T>`][crate::obj::Gd] where `T` inherits
36+
//! from [`RefCounted`][crate::engine::RefCounted].
37+
//!
38+
//! These types may share their underlying data between multiple instances: changes to one
39+
//! instance are visible in another. Think of them as `Rc<RefCell<...>>` but without any runtime
40+
//! borrow checking.
41+
//!
42+
//! Since there is no way to prevent or even detect this sharing from Rust, you need to be more
43+
//! careful when using such types. For example, when iterating over an `Array`, make sure that
44+
//! it isn't being modified at the same time through another reference.
45+
//!
46+
//! To avoid confusion these types don't implement `Clone`. You can use
47+
//! [`Share`][crate::obj::Share] to create a new reference to the same instance, and
48+
//! type-specific methods such as
49+
//! [`Array::duplicate_deep()`][crate::builtin::Array::duplicate_deep] to make actual
50+
//! copies. <br><br>
51+
//!
52+
//! 4. **Manually managed types**: [`Gd<T>`][crate::obj::Gd] where `T` inherits from
53+
//! [`Object`][crate::engine::Object] but not from [`RefCounted`][crate::engine::RefCounted];
54+
//! most notably, this includes all `Node` classes.
55+
//!
56+
//! These also share data, but do not use reference counting to manage their memory. Instead,
57+
//! you must either hand over ownership to Godot (e.g. by adding a node to the scene tree) or
58+
//! free them manually using [`Gd::free()`][crate::obj::Gd::free]. <br><br>
59+
//!
60+
//! # Thread safety
61+
//!
62+
//! [Godot's own thread safety
63+
//! rules](https://docs.godotengine.org/en/latest/tutorials/performance/thread_safe_apis.html)
64+
//! apply. Types in this crate implement (or don't implement) `Send` and `Sync` wherever
65+
//! appropriate, but the Rust compiler cannot check what happens to an object through C++ or
66+
//! GDScript.
67+
//!
68+
//! As a rule of thumb, if you must use threading, prefer to use Rust threads instead of Godot
69+
//! threads.
70+
771
#[doc(inline)]
872
pub use godot_core::{builtin, engine, log, obj, sys};
973

itest/rust/src/variant_test.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use godot::builtin::{
1010
};
1111
use godot::engine::Node3D;
1212
use godot::obj::InstanceId;
13+
use godot::prelude::{Array, Dictionary, VariantConversionError};
1314
use godot::sys::{GodotFfi, VariantOperator, VariantType};
1415
use std::cmp::Ordering;
1516
use std::fmt::{Debug, Display};
@@ -27,6 +28,8 @@ pub fn run() -> bool {
2728
ok &= variant_sys_conversion();
2829
ok &= variant_sys_conversion2();
2930
ok &= variant_null_object_is_nil();
31+
ok &= variant_conversion_fails();
32+
ok &= variant_type_correct();
3033
ok
3134
}
3235

@@ -238,6 +241,52 @@ fn variant_sys_conversion2() {
238241
*/
239242
}
240243

244+
#[itest]
245+
fn variant_conversion_fails() {
246+
assert_eq!(
247+
"hello".to_variant().try_to::<i64>(),
248+
Err(VariantConversionError)
249+
);
250+
assert_eq!(28.to_variant().try_to::<f32>(), Err(VariantConversionError));
251+
assert_eq!(
252+
10.to_variant().try_to::<bool>(),
253+
Err(VariantConversionError)
254+
);
255+
assert_eq!(
256+
false.to_variant().try_to::<String>(),
257+
Err(VariantConversionError)
258+
);
259+
assert_eq!(
260+
Array::default().to_variant().try_to::<StringName>(),
261+
Err(VariantConversionError)
262+
);
263+
assert_eq!(
264+
Dictionary::default().to_variant().try_to::<Array>(),
265+
Err(VariantConversionError)
266+
);
267+
assert_eq!(
268+
Variant::nil().to_variant().try_to::<Dictionary>(),
269+
Err(VariantConversionError)
270+
);
271+
}
272+
273+
#[itest]
274+
fn variant_type_correct() {
275+
assert_eq!(Variant::nil().get_type(), VariantType::Nil);
276+
assert_eq!(0.to_variant().get_type(), VariantType::Int);
277+
assert_eq!(3.8.to_variant().get_type(), VariantType::Float);
278+
assert_eq!(false.to_variant().get_type(), VariantType::Bool);
279+
assert_eq!("string".to_variant().get_type(), VariantType::String);
280+
assert_eq!(
281+
StringName::from("string_name").to_variant().get_type(),
282+
VariantType::StringName
283+
);
284+
assert_eq!(Array::default().to_variant().get_type(), VariantType::Array);
285+
assert_eq!(
286+
Dictionary::default().to_variant().get_type(),
287+
VariantType::Dictionary
288+
);
289+
}
241290
// ----------------------------------------------------------------------------------------------------------------------------------------------
242291

243292
fn roundtrip<T>(value: T)

0 commit comments

Comments
 (0)