Skip to content

Commit 69c346e

Browse files
authored
Merge pull request #1388 from lilizoey/feature/rename-var
Support `rename` for `#[var]`
2 parents 47aef2e + 230547e commit 69c346e

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
lines changed

godot-macros/src/class/data_models/field_var.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use crate::{util, ParseResult};
1818
/// Store info from `#[var]` attribute.
1919
#[derive(Clone, Debug)]
2020
pub struct FieldVar {
21+
/// What name this variable should have in Godot, if `None` then the Rust name should be used.
22+
pub rename: Option<Ident>,
2123
pub getter: GetterSetter,
2224
pub setter: GetterSetter,
2325
pub hint: FieldHint,
@@ -29,13 +31,15 @@ impl FieldVar {
2931
/// Parse a `#[var]` attribute to a `FieldVar` struct.
3032
///
3133
/// Possible keys:
34+
/// - `rename = ident`
3235
/// - `get = expr`
3336
/// - `set = expr`
3437
/// - `hint = ident`
3538
/// - `hint_string = expr`
3639
/// - `usage_flags =
3740
pub(crate) fn new_from_kv(parser: &mut KvParser) -> ParseResult<Self> {
3841
let span = parser.span();
42+
let rename = parser.handle_ident("rename")?;
3943
let getter = GetterSetter::parse(parser, "get")?;
4044
let setter = GetterSetter::parse(parser, "set")?;
4145

@@ -64,6 +68,7 @@ impl FieldVar {
6468
};
6569

6670
Ok(FieldVar {
71+
rename,
6772
getter,
6873
setter,
6974
hint,
@@ -84,6 +89,7 @@ impl FieldVar {
8489
impl Default for FieldVar {
8590
fn default() -> Self {
8691
Self {
92+
rename: Default::default(),
8793
getter: Default::default(),
8894
setter: Default::default(),
8995
hint: Default::default(),
@@ -131,11 +137,12 @@ impl GetterSetter {
131137
class_name: &Ident,
132138
kind: GetSet,
133139
field: &Field,
140+
rename: &Option<Ident>,
134141
) -> Option<GetterSetterImpl> {
135142
match self {
136143
GetterSetter::Omitted => None,
137144
GetterSetter::Generated => Some(GetterSetterImpl::from_generated_impl(
138-
class_name, kind, field,
145+
class_name, kind, field, rename,
139146
)),
140147
GetterSetter::Custom(function_name) => {
141148
Some(GetterSetterImpl::from_custom_impl(function_name))
@@ -173,14 +180,21 @@ pub struct GetterSetterImpl {
173180
}
174181

175182
impl GetterSetterImpl {
176-
fn from_generated_impl(class_name: &Ident, kind: GetSet, field: &Field) -> Self {
183+
fn from_generated_impl(
184+
class_name: &Ident,
185+
kind: GetSet,
186+
field: &Field,
187+
rename: &Option<Ident>,
188+
) -> Self {
177189
let Field {
178190
name: field_name,
179191
ty: field_type,
180192
..
181193
} = field;
182194

183-
let function_name = format_ident!("{}{field_name}", kind.prefix());
195+
let var_name = rename.as_ref().unwrap_or(field_name);
196+
197+
let function_name = format_ident!("{}{var_name}", kind.prefix());
184198

185199
let signature;
186200
let function_body;

godot-macros/src/class/data_models/property.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,17 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
7676
};
7777

7878
make_groups_registrations(group, subgroup, &mut export_tokens, class_name);
79-
80-
let field_name = field_ident.to_string();
81-
8279
let FieldVar {
80+
rename,
8381
getter,
8482
setter,
8583
hint,
8684
mut usage_flags,
8785
..
8886
} = var;
8987

88+
let field_name = rename.as_ref().unwrap_or(field_ident).to_string();
89+
9090
let export_hint;
9191
let registration_fn;
9292

@@ -151,14 +151,14 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
151151
// Note: {getter,setter}_tokens can be either a path `Class_Functions::constant_name` or an empty string `""`.
152152

153153
let getter_tokens = make_getter_setter(
154-
getter.to_impl(class_name, GetSet::Get, field),
154+
getter.to_impl(class_name, GetSet::Get, field, &rename),
155155
&mut getter_setter_impls,
156156
&mut func_name_consts,
157157
&mut export_tokens,
158158
class_name,
159159
);
160160
let setter_tokens = make_getter_setter(
161-
setter.to_impl(class_name, GetSet::Set, field),
161+
setter.to_impl(class_name, GetSet::Set, field, &rename),
162162
&mut getter_setter_impls,
163163
&mut func_name_consts,
164164
&mut export_tokens,

godot-macros/src/lib.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,23 @@ use crate::util::{bail, ident, KvParser};
206206
/// }
207207
/// ```
208208
///
209-
/// To create a property without a backing field to store data, you can use [`PhantomVar`](../obj/struct.PhantomVar.html).
209+
/// If you want the field to have a different name in Godot and Rust, you can use `rename`:
210+
///
211+
/// ```
212+
/// # use godot::prelude::*;
213+
/// #[derive(GodotClass)]
214+
/// # #[class(init)]
215+
/// struct MyStruct {
216+
/// #[var(rename = my_godot_field)]
217+
/// my_rust_field: i64,
218+
/// }
219+
/// ```
220+
///
221+
/// With this, you can access this field as `my_rust_field` in Rust code, however when accessing it from Godot you have to
222+
/// use `my_godot_field` instead (including when using methods such as [`Object::get`](../classes/struct.Object.html#method.get)).
223+
/// The generated getters and setters will also be named `get/set_my_godot_field`, instead of `get/set_my_rust_field`.
224+
///
225+
/// To create a property without a backing field to store data, you can use [`PhantomVar`](../prelude/struct.PhantomVar.html).
210226
/// This disables autogenerated getters and setters for that field.
211227
///
212228
/// ## Export properties -- `#[export]`

itest/rust/src/object_tests/property_test.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ struct HasProperty {
5353

5454
#[var]
5555
packed_int_array: PackedInt32Array,
56+
57+
#[var(rename = renamed_variable)]
58+
unused_name: GString,
5659
}
5760

5861
#[godot_api]
@@ -147,10 +150,48 @@ impl INode for HasProperty {
147150
texture_val: OnEditor::default(),
148151
texture_val_rw: None,
149152
packed_int_array: PackedInt32Array::new(),
153+
unused_name: GString::new(),
150154
}
151155
}
152156
}
153157

158+
#[itest]
159+
fn test_renamed_var() {
160+
let mut obj = HasProperty::new_alloc();
161+
162+
let prop_list = obj.get_property_list();
163+
assert!(prop_list
164+
.iter_shared()
165+
.any(|d| d.get("name") == Some("renamed_variable".to_variant())));
166+
assert!(!prop_list
167+
.iter_shared()
168+
.any(|d| d.get("name") == Some("unused_name".to_variant())));
169+
170+
assert_eq!(obj.get("renamed_variable"), GString::new().to_variant());
171+
assert_eq!(obj.get("unused_name"), Variant::nil());
172+
173+
let new_value = "variable changed".to_variant();
174+
obj.set("renamed_variable", &new_value);
175+
obj.set("unused_name", &"something different".to_variant());
176+
assert_eq!(obj.get("renamed_variable"), new_value);
177+
assert_eq!(obj.get("unused_name"), Variant::nil());
178+
179+
obj.free();
180+
}
181+
182+
#[itest]
183+
fn test_renamed_var_getter_setter() {
184+
let obj = HasProperty::new_alloc();
185+
186+
assert!(obj.has_method("get_renamed_variable"));
187+
assert!(obj.has_method("set_renamed_variable"));
188+
assert!(!obj.has_method("get_unused_name"));
189+
assert!(!obj.has_method("get_unused_name"));
190+
assert_eq!(obj.bind().get_renamed_variable(), GString::new());
191+
192+
obj.free();
193+
}
194+
154195
#[derive(Default, Copy, Clone)]
155196
#[repr(i64)]
156197
enum SomeCStyleEnum {

0 commit comments

Comments
 (0)