Skip to content

Commit 4c79b2b

Browse files
committed
Refactor GodotString, StringName, and NodePath, making them more similar
where they are the same. And placing them in their own module together. Add basic tests for `StringName` and `NodePath`. Add `Hash` impl using `InnerX::hash` for all, instead of just `StringName`. Add `new` constructors to all the string types. Add `as_inner` functions to all the string types. Add conversions between all the string types. Add pass-by-value conversions where there used to only be pass-by-reference conversions. Add `VariantMetadata` impl for `String`
1 parent 21f95f7 commit 4c79b2b

File tree

13 files changed

+500
-178
lines changed

13 files changed

+500
-178
lines changed

godot-core/src/builtin/macros.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,27 @@ macro_rules! impl_builtin_traits_inner {
107107
}
108108
}
109109
};
110+
111+
112+
// Requires a `hash` function.
113+
( Hash for $Type:ty ) => {
114+
impl std::hash::Hash for $Type {
115+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
116+
self.hash().hash(state)
117+
}
118+
}
119+
};
110120
}
111121

112122
macro_rules! impl_builtin_traits {
113123
(
114124
for $Type:ty {
115-
$( $Trait:ident => $gd_method:ident; )*
125+
$( $Trait:ident $(=> $gd_method:ident)?; )*
116126
}
117127
) => (
118128
$(
119129
impl_builtin_traits_inner! {
120-
$Trait for $Type => $gd_method
130+
$Trait for $Type $(=> $gd_method)?
121131
}
122132
)*
123133
)

godot-core/src/builtin/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub use callable::*;
4242
pub use color::*;
4343
pub use dictionary_inner::Dictionary;
4444
pub use math::*;
45-
pub use node_path::*;
4645
pub use others::*;
4746
pub use packed_array::*;
4847
pub use plane::*;
@@ -52,7 +51,6 @@ pub use rect2::*;
5251
pub use rect2i::*;
5352
pub use rid::*;
5453
pub use string::*;
55-
pub use string_name::*;
5654
pub use transform2d::*;
5755
pub use transform3d::*;
5856
pub use variant::*;
@@ -95,7 +93,6 @@ mod callable;
9593
mod color;
9694
mod glam_helpers;
9795
mod math;
98-
mod node_path;
9996
mod others;
10097
mod packed_array;
10198
mod plane;
@@ -105,8 +102,6 @@ mod rect2;
105102
mod rect2i;
106103
mod rid;
107104
mod string;
108-
mod string_chars;
109-
mod string_name;
110105
mod transform2d;
111106
mod transform3d;
112107
mod variant;

godot-core/src/builtin/string.rs renamed to godot-core/src/builtin/string/godot_string.rs

Lines changed: 84 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ use godot_ffi as sys;
1010
use sys::types::OpaqueString;
1111
use sys::{ffi_methods, interface_fn, GodotFfi};
1212

13-
use super::{
14-
string_chars::validate_unicode_scalar_sequence, FromVariant, ToVariant, Variant,
15-
VariantConversionError,
16-
};
13+
use crate::builtin::inner;
1714

15+
use super::string_chars::validate_unicode_scalar_sequence;
16+
use super::{NodePath, StringName};
17+
18+
/// Godot's reference counted string type.
1819
#[repr(C, align(8))]
1920
pub struct GodotString {
2021
opaque: OpaqueString,
2122
}
2223

2324
impl GodotString {
25+
/// Construct a new empty GodotString.
2426
pub fn new() -> Self {
2527
Self::default()
2628
}
@@ -29,12 +31,12 @@ impl GodotString {
2931
Self { opaque }
3032
}
3133

32-
ffi_methods! {
33-
type sys::GDExtensionStringPtr = *mut Opaque;
34-
35-
fn from_string_sys = from_sys;
36-
fn from_string_sys_init = from_sys_init;
37-
fn string_sys = sys;
34+
/// Returns a 32-bit integer hash value representing the string.
35+
pub fn hash(&self) -> u32 {
36+
self.as_inner()
37+
.hash()
38+
.try_into()
39+
.expect("Godot hashes are uint32_t")
3840
}
3941

4042
/// Move `self` into a system pointer. This transfers ownership and thus does not call the destructor.
@@ -83,6 +85,19 @@ impl GodotString {
8385
}
8486
std::slice::from_raw_parts(ptr as *const char, len as usize)
8587
}
88+
89+
ffi_methods! {
90+
type sys::GDExtensionStringPtr = *mut Opaque;
91+
92+
fn from_string_sys = from_sys;
93+
fn from_string_sys_init = from_sys_init;
94+
fn string_sys = sys;
95+
}
96+
97+
#[doc(hidden)]
98+
pub fn as_inner(&self) -> inner::InnerString {
99+
inner::InnerString::from_outer(self)
100+
}
86101
}
87102

88103
// SAFETY:
@@ -122,9 +137,28 @@ impl_builtin_traits! {
122137
Drop => string_destroy;
123138
Eq => string_operator_equal;
124139
Ord => string_operator_less;
140+
Hash;
141+
}
142+
}
143+
144+
impl fmt::Display for GodotString {
145+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146+
let s: String = self.chars_checked().iter().collect();
147+
f.write_str(s.as_str())
148+
}
149+
}
150+
151+
/// Uses literal syntax from GDScript: `"string"`
152+
impl fmt::Debug for GodotString {
153+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154+
let s = String::from(self);
155+
write!(f, "\"{s}\"")
125156
}
126157
}
127158

159+
// ----------------------------------------------------------------------------------------------------------------------------------------------
160+
// Conversion from/into rust string-types
161+
128162
impl<S> From<S> for GodotString
129163
where
130164
S: AsRef<str>,
@@ -162,7 +196,14 @@ impl From<&GodotString> for String {
162196
}
163197
}
164198

165-
// TODO From<&NodePath> + test
199+
impl From<GodotString> for String {
200+
/// Converts this `GodotString` to a `String`.
201+
///
202+
/// This is identical to `String::from(&string)`, and as such there is no performance benefit.
203+
fn from(string: GodotString) -> Self {
204+
Self::from(&string)
205+
}
206+
}
166207

167208
impl FromStr for GodotString {
168209
type Err = Infallible;
@@ -172,49 +213,47 @@ impl FromStr for GodotString {
172213
}
173214
}
174215

175-
impl fmt::Display for GodotString {
176-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177-
let s = String::from(self);
178-
f.write_str(s.as_str())
179-
}
180-
}
181-
182-
/// Uses literal syntax from GDScript: `"string"`
183-
impl fmt::Debug for GodotString {
184-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185-
let s = String::from(self);
186-
write!(f, "\"{s}\"")
187-
}
188-
}
216+
// ----------------------------------------------------------------------------------------------------------------------------------------------
217+
// Conversion from other Godot string-types
189218

190-
impl ToVariant for &str {
191-
fn to_variant(&self) -> Variant {
192-
GodotString::from(*self).to_variant()
219+
impl From<&StringName> for GodotString {
220+
fn from(string: &StringName) -> Self {
221+
unsafe {
222+
Self::from_sys_init_default(|self_ptr| {
223+
let ctor = sys::builtin_fn!(string_from_string_name);
224+
let args = [string.sys_const()];
225+
ctor(self_ptr, args.as_ptr());
226+
})
227+
}
193228
}
194229
}
195230

196-
impl ToVariant for String {
197-
fn to_variant(&self) -> Variant {
198-
GodotString::from(self).to_variant()
231+
impl From<StringName> for GodotString {
232+
/// Converts this `StringName` to a `GodotString`.
233+
///
234+
/// This is identical to `GodotString::from(&string_name)`, and as such there is no performance benefit.
235+
fn from(string_name: StringName) -> Self {
236+
Self::from(&string_name)
199237
}
200238
}
201239

202-
impl FromVariant for String {
203-
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
204-
Ok(GodotString::try_from_variant(variant)?.to_string())
240+
impl From<&NodePath> for GodotString {
241+
fn from(path: &NodePath) -> Self {
242+
unsafe {
243+
Self::from_sys_init_default(|self_ptr| {
244+
let ctor = sys::builtin_fn!(string_from_node_path);
245+
let args = [path.sys_const()];
246+
ctor(self_ptr, args.as_ptr());
247+
})
248+
}
205249
}
206250
}
207251

208-
// While this is a nice optimisation for ptrcalls, it's not easily possible
209-
// to pass in &GodotString when doing varcalls.
210-
/*
211-
impl PtrCall for &GodotString {
212-
unsafe fn from_ptr_call_arg(arg: *const godot_ffi::GDExtensionTypePtr) -> Self {
213-
&*(*arg as *const GodotString)
214-
}
215-
216-
unsafe fn to_ptr_call_arg(self, arg: godot_ffi::GDExtensionTypePtr) {
217-
std::ptr::write(arg as *mut GodotString, self.clone());
252+
impl From<NodePath> for GodotString {
253+
/// Converts this `NodePath` to a `GodotString`.
254+
///
255+
/// This is identical to `GodotString::from(&path)`, and as such there is no performance benefit.
256+
fn from(path: NodePath) -> Self {
257+
Self::from(&path)
218258
}
219259
}
220-
*/
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
#![macro_use]
8+
9+
macro_rules! impl_rust_string_conv {
10+
($Ty:ty) => {
11+
impl<S> From<S> for $Ty
12+
where
13+
S: AsRef<str>,
14+
{
15+
fn from(string: S) -> Self {
16+
let intermediate = GodotString::from(string.as_ref());
17+
Self::from(&intermediate)
18+
}
19+
}
20+
21+
impl From<&$Ty> for String {
22+
fn from(string: &$Ty) -> Self {
23+
let intermediate = GodotString::from(string);
24+
Self::from(&intermediate)
25+
}
26+
}
27+
28+
impl From<$Ty> for String {
29+
fn from(string: $Ty) -> Self {
30+
Self::from(&string)
31+
}
32+
}
33+
34+
impl std::str::FromStr for $Ty {
35+
type Err = std::convert::Infallible;
36+
37+
fn from_str(string: &str) -> Result<Self, Self::Err> {
38+
Ok(Self::from(string))
39+
}
40+
}
41+
};
42+
}

godot-core/src/builtin/string/mod.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
//! Godot-types that are Strings.
8+
9+
mod godot_string;
10+
mod macros;
11+
mod node_path;
12+
mod string_chars;
13+
mod string_name;
14+
15+
use godot_ffi::VariantType;
16+
pub use godot_string::*;
17+
pub use node_path::*;
18+
pub use string_name::*;
19+
20+
use super::{meta::VariantMetadata, FromVariant, ToVariant, Variant, VariantConversionError};
21+
22+
impl ToVariant for &str {
23+
fn to_variant(&self) -> Variant {
24+
GodotString::from(*self).to_variant()
25+
}
26+
}
27+
28+
impl ToVariant for String {
29+
fn to_variant(&self) -> Variant {
30+
GodotString::from(self).to_variant()
31+
}
32+
}
33+
34+
impl FromVariant for String {
35+
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
36+
Ok(GodotString::try_from_variant(variant)?.to_string())
37+
}
38+
}
39+
40+
impl VariantMetadata for String {
41+
fn variant_type() -> VariantType {
42+
VariantType::String
43+
}
44+
}

0 commit comments

Comments
 (0)