Skip to content

Commit 88a7934

Browse files
authored
Merge pull request #419 from fpdotmonkey/alias-class-definitions
Allow different class names in rust and Godot
2 parents ce3a2e1 + 6e07b1d commit 88a7934

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

godot-core/src/registry.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
227227
fill_into(
228228
&mut c.godot_params.create_instance_func,
229229
generated_create_fn,
230+
)
231+
.unwrap_or_else(|_|
232+
panic!(
233+
"Godot class `{}` is defined multiple times in Rust; you can rename them with #[class(rename=NewName)]",
234+
c.class_name,
235+
)
230236
);
231237
c.godot_params.free_instance_func = Some(free_fn);
232238
}
@@ -245,7 +251,9 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
245251
get_virtual_fn,
246252
} => {
247253
c.user_register_fn = user_register_fn;
248-
fill_into(&mut c.godot_params.create_instance_func, user_create_fn);
254+
// this shouldn't panic since rustc will error if there's
255+
// multiple `impl {Class}Virtual for Thing` definitions
256+
fill_into(&mut c.godot_params.create_instance_func, user_create_fn).unwrap();
249257
c.godot_params.to_string_func = user_to_string_fn;
250258
c.godot_params.notification_func = user_on_notification_fn;
251259
c.godot_params.get_virtual_func = Some(get_virtual_fn);
@@ -260,12 +268,13 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
260268
}
261269

262270
/// If `src` is occupied, it moves the value into `dst`, while ensuring that no previous value is present in `dst`.
263-
fn fill_into<T>(dst: &mut Option<T>, src: Option<T>) {
271+
fn fill_into<T>(dst: &mut Option<T>, src: Option<T>) -> Result<(), ()> {
264272
match (dst, src) {
265273
(dst @ None, src) => *dst = src,
266-
(Some(_), Some(_)) => panic!("option already filled"),
274+
(Some(_), Some(_)) => return Err(()),
267275
(Some(_), None) => { /* do nothing */ }
268276
}
277+
Ok(())
269278
}
270279

271280
/// Registers a class with given the dynamic type information `info`.

godot-macros/src/class/derive_godot_class.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ pub fn derive_godot_class(decl: Declaration) -> ParseResult<TokenStream> {
2121
let fields = parse_fields(class)?;
2222

2323
let class_name = &class.name;
24-
let class_name_str = class.name.to_string();
24+
let class_name_str: String = struct_cfg
25+
.rename
26+
.map_or_else(|| class.name.clone(), |rename| rename)
27+
.to_string();
2528
let class_name_cstr = util::cstr_u8_slice(&class_name_str);
2629
let class_name_obj = util::class_name_obj(class_name);
2730

@@ -108,6 +111,7 @@ fn parse_struct_attributes(class: &Struct) -> ParseResult<ClassAttributes> {
108111
let mut has_generated_init = false;
109112
let mut is_tool = false;
110113
let mut is_editor_plugin = false;
114+
let mut rename: Option<Ident> = None;
111115

112116
// #[class] attribute on struct
113117
if let Some(mut parser) = KvParser::parse(&class.attributes, "class")? {
@@ -127,6 +131,7 @@ fn parse_struct_attributes(class: &Struct) -> ParseResult<ClassAttributes> {
127131
if parser.handle_alone_ident("editor_plugin")?.is_some() {
128132
is_editor_plugin = true;
129133
}
134+
rename = parser.handle_ident("rename")?;
130135

131136
parser.finish()?;
132137
}
@@ -136,6 +141,7 @@ fn parse_struct_attributes(class: &Struct) -> ParseResult<ClassAttributes> {
136141
has_generated_init,
137142
is_tool,
138143
is_editor_plugin,
144+
rename,
139145
})
140146
}
141147

@@ -216,6 +222,7 @@ struct ClassAttributes {
216222
has_generated_init: bool,
217223
is_tool: bool,
218224
is_editor_plugin: bool,
225+
rename: Option<Ident>,
219226
}
220227

221228
fn make_godot_init_impl(class_name: &Ident, fields: Fields) -> TokenStream {

godot-macros/src/lib.rs

+24
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,30 @@ use crate::util::ident;
332332
///
333333
/// This should usually be combined with `#[class(tool)]` so that the code you write will actually run in the
334334
/// editor.
335+
///
336+
/// # Class Renaming
337+
///
338+
/// You may want to have structs with the same name. With Rust, this is allowed using `mod`. However in GDScript,
339+
/// there are no modules, namespaces, or any such disambiguation. Therefore, you need to change the names before they
340+
/// can get to Godot. You can use the `rename` key while defining your `GodotClass` for this.
341+
///
342+
/// ```
343+
/// mod animal {
344+
/// # use godot::prelude::*;
345+
/// #[derive(GodotClass)]
346+
/// #[class(init, rename=AnimalToad)]
347+
/// pub struct Toad {}
348+
/// }
349+
///
350+
/// mod npc {
351+
/// # use godot::prelude::*;
352+
/// #[derive(GodotClass)]
353+
/// #[class(init, rename=NpcToad)]
354+
/// pub struct Toad {}
355+
/// }
356+
/// ```
357+
///
358+
/// These classes will appear in the Godot editor and GDScript as "AnimalToad" or "NpcToad".
335359
#[proc_macro_derive(GodotClass, attributes(class, base, var, export, init, signal))]
336360
pub fn derive_godot_class(input: TokenStream) -> TokenStream {
337361
translate(input, class::derive_godot_class)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
use crate::framework::itest;
8+
use godot::prelude::*;
9+
10+
pub mod dont_rename {
11+
use super::*;
12+
13+
#[derive(GodotClass)]
14+
pub struct RepeatMe {}
15+
}
16+
17+
pub mod rename {
18+
use super::*;
19+
20+
#[derive(GodotClass)]
21+
#[class(rename = NoRepeat)]
22+
pub struct RepeatMe {}
23+
}
24+
25+
#[itest]
26+
fn renaming_changes_the_name() {
27+
assert_ne!(
28+
dont_rename::RepeatMe::class_name(),
29+
rename::RepeatMe::class_name()
30+
);
31+
assert_eq!(dont_rename::RepeatMe::class_name().as_str(), "RepeatMe");
32+
assert_eq!(rename::RepeatMe::class_name().as_str(), "NoRepeat");
33+
}

itest/rust/src/object_tests/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
mod base_test;
8+
mod class_rename_test;
89
mod object_test;
910
mod property_test;
1011
mod singleton_test;

0 commit comments

Comments
 (0)