Skip to content

Commit 7768d90

Browse files
authored
Merge pull request #1011 from godot-rust/bugfix/enum-hint-ord
Fix `#[derive(Var)]` generating incorrect `hint_string` for enums
2 parents ca621b3 + 47afe74 commit 7768d90

File tree

3 files changed

+68
-12
lines changed

3 files changed

+68
-12
lines changed

godot-macros/src/derive/data_models/c_style_enum.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::util::bail;
99
use crate::ParseResult;
1010
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
1111
use quote::{quote, ToTokens};
12+
use std::fmt::Write;
1213

1314
/// Stores info from C-style enums for use in deriving `GodotConvert` and other related traits.
1415
#[derive(Clone, Debug)]
@@ -87,22 +88,45 @@ impl CStyleEnum {
8788
}
8889

8990
/// Return a hint string for use with `PropertyHint::ENUM` where each variant has an explicit integer hint.
90-
pub fn to_int_hint(&self) -> String {
91-
self.enumerator_names
91+
pub fn to_int_hint(&self) -> TokenStream {
92+
// We can't build the format string directly, since the ords may be expressions and not literals.
93+
// Thus generate code containing a format!() statement.
94+
95+
let iter = self
96+
.enumerator_names
9297
.iter()
93-
.zip(self.enumerator_ords.iter())
94-
.map(|(name, discrim)| format!("{name}:{discrim}"))
95-
.collect::<Vec<_>>()
96-
.join(",")
98+
.zip(self.enumerator_ords.iter());
99+
100+
let mut fmt = String::new();
101+
let mut fmt_args = Vec::new();
102+
let mut first = true;
103+
104+
for (name, discrim) in iter {
105+
if first {
106+
first = false;
107+
} else {
108+
fmt.push(',');
109+
}
110+
111+
write!(fmt, "{name}:{{}}").expect("write to string");
112+
fmt_args.push(discrim.clone());
113+
}
114+
115+
quote! {
116+
format!(#fmt, #(#fmt_args),*)
117+
}
97118
}
98119

99120
/// Return a hint string for use with `PropertyHint::ENUM` where the variants are just kept as strings.
100-
pub fn to_string_hint(&self) -> String {
101-
self.enumerator_names
121+
pub fn to_string_hint(&self) -> TokenStream {
122+
let hint_string = self
123+
.enumerator_names
102124
.iter()
103125
.map(ToString::to_string)
104126
.collect::<Vec<_>>()
105-
.join(",")
127+
.join(",");
128+
129+
hint_string.to_token_stream()
106130
}
107131
}
108132

godot-macros/src/derive/derive_var.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fn create_property_hint_impl(convert: &GodotConvert) -> TokenStream {
6161
quote! {
6262
::godot::meta::PropertyHintInfo {
6363
hint: ::godot::global::PropertyHint::ENUM,
64-
hint_string: #hint_string.into(),
64+
hint_string: ::godot::builtin::GString::from(#hint_string),
6565
}
6666
}
6767
}

itest/rust/src/object_tests/property_test.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,22 @@ pub enum TestEnum {
313313
C = 2,
314314
}
315315

316+
#[derive(GodotConvert, Var)]
317+
#[godot(via = i64)]
318+
pub enum Behavior {
319+
Peaceful,
320+
Defend,
321+
Aggressive = (3 + 4),
322+
}
323+
324+
#[derive(GodotConvert, Var)]
325+
#[godot(via = GString)]
326+
pub enum StrBehavior {
327+
Peaceful,
328+
Defend,
329+
Aggressive = (3 + 4),
330+
}
331+
316332
#[derive(GodotClass)]
317333
#[class(no_init)]
318334
pub struct DeriveProperty {
@@ -326,22 +342,38 @@ fn derive_property() {
326342
my_enum: TestEnum::B,
327343
};
328344
assert_eq!(class.get_my_enum(), TestEnum::B as i64);
345+
329346
class.set_my_enum(TestEnum::C as i64);
330347
assert_eq!(class.my_enum, TestEnum::C);
331348
}
332349

350+
// Regression test for https://github.com/godot-rust/gdext/issues/1009.
351+
#[itest]
352+
fn enum_var_hint() {
353+
let int_prop = <Behavior as Var>::var_hint();
354+
assert_eq!(int_prop.hint, PropertyHint::ENUM);
355+
assert_eq!(
356+
int_prop.hint_string,
357+
"Peaceful:0,Defend:1,Aggressive:7".into()
358+
);
359+
360+
let str_prop = <StrBehavior as Var>::var_hint();
361+
assert_eq!(str_prop.hint, PropertyHint::ENUM);
362+
assert_eq!(str_prop.hint_string, "Peaceful,Defend,Aggressive".into());
363+
}
364+
333365
#[derive(GodotClass)]
334366
pub struct DeriveExport {
335367
#[export]
336368
pub my_enum: TestEnum,
337369

338370
// Tests also qualified base path (type inference of Base<T> without #[hint]).
339-
pub base: godot::obj::Base<RefCounted>,
371+
pub base: Base<RefCounted>,
340372
}
341373

342374
#[godot_api]
343375
impl IRefCounted for DeriveExport {
344-
fn init(base: godot::obj::Base<Self::Base>) -> Self {
376+
fn init(base: Base<Self::Base>) -> Self {
345377
Self {
346378
my_enum: TestEnum::B,
347379
base,

0 commit comments

Comments
 (0)