Skip to content

Commit

Permalink
Merge pull request godot-rust#378 from you-win/feature/func-renaming
Browse files Browse the repository at this point in the history
Feature/func renaming
  • Loading branch information
Bromeon authored Aug 11, 2023
2 parents 63fdd6b + d6589fc commit b4e6fd6
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 18 deletions.
9 changes: 8 additions & 1 deletion godot-macros/src/derive_godot_class/property/field_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use crate::derive_godot_class::{make_existence_check, Field, FieldHint};
use crate::method_registration::make_method_registration;
use crate::method_registration::FuncDefinition;
use crate::util::KvParser;
use crate::{util, ParseResult};
use proc_macro2::{Ident, TokenStream};
Expand Down Expand Up @@ -188,7 +189,13 @@ impl GetterSetterImpl {
};

let signature = util::parse_signature(signature);
let export_token = make_method_registration(class_name, signature);
let export_token = make_method_registration(
class_name,
FuncDefinition {
func: signature,
rename: None,
},
);

Self {
function_name,
Expand Down
40 changes: 26 additions & 14 deletions godot-macros/src/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::method_registration::{make_method_registration, make_virtual_method_callback};
use crate::method_registration::{
make_method_registration, make_virtual_method_callback, FuncDefinition,
};
use crate::util;
use crate::util::bail;
use crate::util::KvParser;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use quote::spanned::Spanned;
Expand Down Expand Up @@ -46,7 +49,7 @@ pub fn transform(input_decl: Declaration) -> Result<TokenStream, Error> {

/// Attribute for user-declared function
enum BoundAttrType {
Func(AttributeValue),
Func { rename: Option<String> },
Signal(AttributeValue),
Const(AttributeValue),
}
Expand Down Expand Up @@ -108,7 +111,7 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {

let methods_registration = funcs
.into_iter()
.map(|func| make_method_registration(&class_name, func));
.map(|func_def| make_method_registration(&class_name, func_def));

let consts = process_godot_constants(&mut decl)?;
let mut integer_constant_names = Vec::new();
Expand Down Expand Up @@ -198,8 +201,8 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
Ok(result)
}

fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Function>), Error> {
let mut func_signatures = vec![];
fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Function>), Error> {
let mut func_definitions = vec![];
let mut signal_signatures = vec![];

let mut removed_indexes = vec![];
Expand Down Expand Up @@ -229,10 +232,10 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Function>),
}

match attr.ty {
BoundAttrType::Func(_attr) => {
BoundAttrType::Func { rename } => {
// Signatures are the same thing without body
let sig = util::reduce_to_signature(method);
func_signatures.push(sig);
func_definitions.push(FuncDefinition { func: sig, rename });
}
BoundAttrType::Signal(ref _attr_val) => {
if method.return_ty.is_some() {
Expand All @@ -259,7 +262,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Function>),
decl.body_items.remove(index);
}

Ok((func_signatures, signal_signatures))
Ok((func_definitions, signal_signatures))
}

fn process_godot_constants(decl: &mut Impl) -> Result<Vec<Constant>, Error> {
Expand All @@ -275,7 +278,7 @@ fn process_godot_constants(decl: &mut Impl) -> Result<Vec<Constant>, Error> {
constant.attributes.remove(attr.index);

match attr.ty {
BoundAttrType::Func(_) => {
BoundAttrType::Func { .. } => {
return bail!(constant, "#[func] can only be used on functions")
}
BoundAttrType::Signal(_) => {
Expand Down Expand Up @@ -308,11 +311,20 @@ where
.expect("get_single_path_segment");

let new_found = match attr_name {
name if name == "func" => Some(BoundAttr {
attr_name: attr_name.clone(),
index,
ty: BoundAttrType::Func(attr.value.clone()),
}),
name if name == "func" => {
// TODO you-win (August 8, 2023): handle default values here as well?

// Safe unwrap since #[func] must be present if we got to this point
let mut parser = KvParser::parse(attributes, "func")?.unwrap();

let rename = parser.handle_expr("rename")?.map(|ts| ts.to_string());

Some(BoundAttr {
attr_name: attr_name.clone(),
index,
ty: BoundAttrType::Func { rename },
})
}
name if name == "signal" => {
// TODO once parameters are supported, this should probably be moved to the struct definition
// E.g. a zero-sized type Signal<(i32, String)> with a provided emit(i32, String) method
Expand Down
8 changes: 8 additions & 0 deletions godot-macros/src/method_registration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ struct SignatureInfo {
pub ret_type: TokenStream,
}

/// Information used for registering a Rust function with Godot.
pub struct FuncDefinition {
/// Raw information about the Rust function.
pub func: venial::Function,
/// The name the function will be exposed as in Godot. If `None`, the Rust function name is used.
pub rename: Option<String>,
}

/// Returns a closure expression that forwards the parameters to the Rust instance.
fn make_forwarding_closure(class_name: &Ident, signature_info: &SignatureInfo) -> TokenStream {
let method_name = &signature_info.method_name;
Expand Down
10 changes: 7 additions & 3 deletions godot-macros/src/method_registration/register_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use quote::quote;
/// Generates code that registers the specified method for the given class.
pub fn make_method_registration(
class_name: &Ident,
method_signature: venial::Function,
func_definition: super::FuncDefinition,
) -> TokenStream {
let signature_info = get_signature_info(&method_signature);
let signature_info = get_signature_info(&func_definition.func);
let sig_tuple =
util::make_signature_tuple_type(&signature_info.ret_type, &signature_info.param_types);

Expand All @@ -33,7 +33,11 @@ pub fn make_method_registration(

// String literals
let class_name_str = class_name.to_string();
let method_name_str = method_name.to_string();
let method_name_str = if let Some(rename) = func_definition.rename {
rename
} else {
method_name.to_string()
};
let param_ident_strs = param_idents.iter().map(|ident| ident.to_string());

quote! {
Expand Down
15 changes: 15 additions & 0 deletions itest/godot/ManualFfiTests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,18 @@ func test_option_export():
assert_eq(obj.optional_export, null)

test_node.free()

func test_func_rename():
var func_rename := FuncRename.new()

assert_eq(func_rename.has_method("long_function_name_for_is_true"), false)
assert_eq(func_rename.has_method("is_true"), true)
assert_eq(func_rename.is_true(), true)

assert_eq(func_rename.has_method("give_one_inner"), false)
assert_eq(func_rename.has_method("give_one"), true)
assert_eq(func_rename.give_one(), 1)

assert_eq(func_rename.has_method("renamed_static"), false)
assert_eq(func_rename.has_method("spell_static"), true)
assert_eq(func_rename.spell_static(), "static")
48 changes: 48 additions & 0 deletions itest/rust/src/func_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 godot::prelude::*;

#[derive(GodotClass)]
#[class(base=RefCounted)]
struct FuncRename;

#[godot_api]
impl FuncRename {
#[func(rename=is_true)]
fn long_function_name_for_is_true(&self) -> bool {
true
}

#[func(rename=give_one)]
fn give_one_inner(&self) -> i32 {
self.give_one()
}

#[func(rename=spell_static)]
fn renamed_static() -> GodotString {
GodotString::from("static")
}
}

impl FuncRename {
/// Unused but present to demonstrate how `rename = ...` can be used to avoid name clashes.
#[allow(dead_code)]
fn is_true(&self) -> bool {
false
}

fn give_one(&self) -> i32 {
1
}
}

#[godot_api]
impl RefCountedVirtual for FuncRename {
fn init(_base: Base<Self::Base>) -> Self {
Self
}
}
1 change: 1 addition & 0 deletions itest/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod color_test;
mod derive_variant;
mod dictionary_test;
mod enum_test;
mod func_test;
mod gdscript_ffi_test;
mod init_test;
mod native_structures_test;
Expand Down

0 comments on commit b4e6fd6

Please sign in to comment.