Skip to content

Commit

Permalink
Merge pull request godot-rust#419 from fpdotmonkey/alias-class-defini…
Browse files Browse the repository at this point in the history
…tions

Allow different class names in rust and Godot
  • Loading branch information
Bromeon authored Oct 3, 2023
2 parents ce3a2e1 + 6e07b1d commit 88a7934
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 4 deletions.
15 changes: 12 additions & 3 deletions godot-core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
fill_into(
&mut c.godot_params.create_instance_func,
generated_create_fn,
)
.unwrap_or_else(|_|
panic!(
"Godot class `{}` is defined multiple times in Rust; you can rename them with #[class(rename=NewName)]",
c.class_name,
)
);
c.godot_params.free_instance_func = Some(free_fn);
}
Expand All @@ -245,7 +251,9 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
get_virtual_fn,
} => {
c.user_register_fn = user_register_fn;
fill_into(&mut c.godot_params.create_instance_func, user_create_fn);
// this shouldn't panic since rustc will error if there's
// multiple `impl {Class}Virtual for Thing` definitions
fill_into(&mut c.godot_params.create_instance_func, user_create_fn).unwrap();
c.godot_params.to_string_func = user_to_string_fn;
c.godot_params.notification_func = user_on_notification_fn;
c.godot_params.get_virtual_func = Some(get_virtual_fn);
Expand All @@ -260,12 +268,13 @@ fn fill_class_info(component: PluginComponent, c: &mut ClassRegistrationInfo) {
}

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

/// Registers a class with given the dynamic type information `info`.
Expand Down
9 changes: 8 additions & 1 deletion godot-macros/src/class/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ pub fn derive_godot_class(decl: Declaration) -> ParseResult<TokenStream> {
let fields = parse_fields(class)?;

let class_name = &class.name;
let class_name_str = class.name.to_string();
let class_name_str: String = struct_cfg
.rename
.map_or_else(|| class.name.clone(), |rename| rename)
.to_string();
let class_name_cstr = util::cstr_u8_slice(&class_name_str);
let class_name_obj = util::class_name_obj(class_name);

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

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

parser.finish()?;
}
Expand All @@ -136,6 +141,7 @@ fn parse_struct_attributes(class: &Struct) -> ParseResult<ClassAttributes> {
has_generated_init,
is_tool,
is_editor_plugin,
rename,
})
}

Expand Down Expand Up @@ -216,6 +222,7 @@ struct ClassAttributes {
has_generated_init: bool,
is_tool: bool,
is_editor_plugin: bool,
rename: Option<Ident>,
}

fn make_godot_init_impl(class_name: &Ident, fields: Fields) -> TokenStream {
Expand Down
24 changes: 24 additions & 0 deletions godot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,30 @@ use crate::util::ident;
///
/// This should usually be combined with `#[class(tool)]` so that the code you write will actually run in the
/// editor.
///
/// # Class Renaming
///
/// You may want to have structs with the same name. With Rust, this is allowed using `mod`. However in GDScript,
/// there are no modules, namespaces, or any such disambiguation. Therefore, you need to change the names before they
/// can get to Godot. You can use the `rename` key while defining your `GodotClass` for this.
///
/// ```
/// mod animal {
/// # use godot::prelude::*;
/// #[derive(GodotClass)]
/// #[class(init, rename=AnimalToad)]
/// pub struct Toad {}
/// }
///
/// mod npc {
/// # use godot::prelude::*;
/// #[derive(GodotClass)]
/// #[class(init, rename=NpcToad)]
/// pub struct Toad {}
/// }
/// ```
///
/// These classes will appear in the Godot editor and GDScript as "AnimalToad" or "NpcToad".
#[proc_macro_derive(GodotClass, attributes(class, base, var, export, init, signal))]
pub fn derive_godot_class(input: TokenStream) -> TokenStream {
translate(input, class::derive_godot_class)
Expand Down
33 changes: 33 additions & 0 deletions itest/rust/src/object_tests/class_rename_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::framework::itest;
use godot::prelude::*;

pub mod dont_rename {
use super::*;

#[derive(GodotClass)]
pub struct RepeatMe {}
}

pub mod rename {
use super::*;

#[derive(GodotClass)]
#[class(rename = NoRepeat)]
pub struct RepeatMe {}
}

#[itest]
fn renaming_changes_the_name() {
assert_ne!(
dont_rename::RepeatMe::class_name(),
rename::RepeatMe::class_name()
);
assert_eq!(dont_rename::RepeatMe::class_name().as_str(), "RepeatMe");
assert_eq!(rename::RepeatMe::class_name().as_str(), "NoRepeat");
}
1 change: 1 addition & 0 deletions itest/rust/src/object_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

mod base_test;
mod class_rename_test;
mod object_test;
mod property_test;
mod singleton_test;
Expand Down

0 comments on commit 88a7934

Please sign in to comment.