Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow GodotExt syntax to be used for virtuals #188

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/src/hud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl Hud {
}

#[godot_api]
impl CanvasLayerVirtual for Hud {
impl GodotExt for Hud {
fn init(base: Base<Self::Base>) -> Self {
Self { base }
}
Expand Down
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/src/main_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl Main {
}

#[godot_api]
impl NodeVirtual for Main {
impl GodotExt for Main {
fn init(base: Base<Node>) -> Self {
Main {
mob_scene: PackedScene::new(),
Expand Down
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/src/mob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Mob {
}

#[godot_api]
impl RigidBody2DVirtual for Mob {
impl GodotExt for Mob {
fn init(base: Base<RigidBody2D>) -> Self {
Mob {
min_speed: 150.0,
Expand Down
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Player {
}

#[godot_api]
impl Area2DVirtual for Player {
impl GodotExt for Player {
fn init(base: Base<Area2D>) -> Self {
Player {
speed: 400.0,
Expand Down
5 changes: 4 additions & 1 deletion godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ pub trait Share {
fn share(&self) -> Self;
}

// Marker trait that exists only for documentation purposes.
// pub trait GodotExt : private::Sealed {}

/// Non-strict inheritance relationship in the Godot class hierarchy.
///
/// `Derived: Inherits<Base>` means that either `Derived` is a subclass of `Base`, or the class `Base` itself (hence "non-strict").
Expand Down Expand Up @@ -164,7 +167,7 @@ pub mod cap {
fn __register_exports();
}

/// Auto-implemented for `#[godot_api] impl ***Virtual for MyClass` blocks
/// Auto-implemented for `#[godot_api] impl GodotExt for MyClass` blocks
pub trait ImplementsGodotVirtual: GodotClass {
#[doc(hidden)]
fn __virtual_call(_name: &str) -> sys::GDExtensionClassCallVirtual;
Expand Down
25 changes: 25 additions & 0 deletions godot-macros/src/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub fn transform(decl: Declaration) -> ParseResult<TokenStream> {
let class_name = &class.name;
let class_name_str = class.name.to_string();
let inherits_macro = format_ident!("inherits_transitive_{}", base_ty);
let virtual_trait = format_ident!("{}Virtual", base_ty);
let virtual_impl_macro = format_ident!("__gdext_virtual_impl_{}", class_name);
let virtual_call_macro = format_ident!("__gdext_virtual_call_{}", class_name);

let prv = quote! { ::godot::private };
let deref_impl = make_deref_impl(class_name, &fields);
Expand Down Expand Up @@ -60,6 +63,28 @@ pub fn transform(decl: Declaration) -> ParseResult<TokenStream> {
});

#prv::class_macros::#inherits_macro!(#class_name);

#[allow(non_snake_case)]
macro_rules! #virtual_impl_macro {
( impl GodotExt for $Class:ident { $($tt:tt)* } ) => {
impl #virtual_trait for $Class { $($tt)* }
};

( impl $Trait:ident for $Class:ident { $($tt:tt)* } ) => {
impl $Trait for $Class { $($tt)* }
};
}

#[allow(non_snake_case)]
macro_rules! #virtual_call_macro {
( <Self as GodotExt> :: $function:ident( $($args:expr)* ) ) => {
<Self as #virtual_trait> :: $function( $($args)* )
};

( <Self as $Trait:ident> :: $function:ident( $($args:expr)* ) ) => {
<Self as $Trait> :: $function( $($args)* )
};
}
})
}

Expand Down
33 changes: 22 additions & 11 deletions godot-macros/src/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

use crate::util;
use crate::util::bail;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use venial::{AttributeValue, Declaration, Error, Function, Impl, ImplMember};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::{format_ident, quote};
use venial::{AttributeValue, Declaration, Error, Function, Impl, ImplMember, TyExpr};

pub fn transform(input_decl: Declaration) -> Result<TokenStream, Error> {
let decl = match input_decl {
Expand Down Expand Up @@ -211,8 +211,13 @@ fn extract_attributes(method: &Function) -> Result<Option<BoundAttr>, Error> {
// ----------------------------------------------------------------------------------------------------------------------------------------------

/// Codegen for `#[godot_api] impl GodotExt for MyType`
fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
fn transform_trait_impl(mut original_impl: Impl) -> Result<TokenStream, Error> {
let (class_name, trait_name) = util::validate_trait_impl_virtual(&original_impl, "godot_api")?;
original_impl.trait_ty = Some(TyExpr {
tokens: vec![TokenTree::Ident(trait_name.clone())],
});
let original_impl = original_impl; // de-mutate

let class_name_str = class_name.to_string();

let mut godot_init_impl = TokenStream::new();
Expand All @@ -225,6 +230,8 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {

let mut virtual_methods = vec![];
let mut virtual_method_names = vec![];
let virtual_impl_macro = format_ident!("__gdext_virtual_impl_{}", class_name);
let virtual_call_macro = format_ident!("__gdext_virtual_call_{}", class_name);

let prv = quote! { ::godot::private };

Expand All @@ -241,21 +248,23 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
register_class_impl = quote! {
impl ::godot::obj::cap::GodotRegisterClass for #class_name {
fn __godot_register_class(builder: &mut ::godot::builder::GodotBuilder<Self>) {
<Self as #trait_name>::register_class(builder)
#virtual_call_macro! { <Self as #trait_name>::register_class(builder) }
}
}
};

register_fn = quote! { Some(#prv::ErasedRegisterFn {
raw: #prv::callbacks::register_class_by_builder::<#class_name>
}) };
register_fn = quote! {
Some(#prv::ErasedRegisterFn {
raw: #prv::callbacks::register_class_by_builder::<#class_name>
})
};
}

"init" => {
godot_init_impl = quote! {
impl ::godot::obj::cap::GodotInit for #class_name {
fn __godot_init(base: ::godot::obj::Base<Self::Base>) -> Self {
<Self as #trait_name>::init(base)
#virtual_call_macro! { <Self as #trait_name>::init(base) }
}
}
};
Expand All @@ -266,7 +275,7 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
to_string_impl = quote! {
impl ::godot::obj::cap::GodotToString for #class_name {
fn __godot_to_string(&self) -> ::godot::builtin::GodotString {
<Self as #trait_name>::to_string(self)
#virtual_call_macro! { <Self as #trait_name>::to_string(self) }
}
}
};
Expand Down Expand Up @@ -297,7 +306,9 @@ fn transform_trait_impl(original_impl: Impl) -> Result<TokenStream, Error> {
}

let result = quote! {
#original_impl
#[doc(hidden)]
#virtual_impl_macro!(#original_impl);

#godot_init_impl
#to_string_impl
#register_class_impl
Expand Down
2 changes: 2 additions & 0 deletions godot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub fn derive_native_class(input: TokenStream) -> TokenStream {
translate(input, derive_godot_class::transform)
}

/// Attribute to annotate `impl` blocks.
#[doc(alias = "GodotExt")]
#[proc_macro_attribute]
pub fn godot_api(_meta: TokenStream, input: TokenStream) -> TokenStream {
translate(input, godot_api::transform)
Expand Down
15 changes: 9 additions & 6 deletions godot-macros/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,9 @@ pub(crate) fn validate_impl(
validate_self(original_impl, attr)
}

/// Validates that the declaration is the of the form `impl Trait for
/// SomeType`, where the name of `Trait` ends in `Virtual`.
/// Validates that the declaration is the of the form `impl Trait for SomeType`, where the name of `Trait` ends in `Virtual`.
///
/// Returns (class_name, trait_name).
pub(crate) fn validate_trait_impl_virtual(
original_impl: &Impl,
attr: &str,
Expand All @@ -390,10 +391,9 @@ pub(crate) fn validate_trait_impl_virtual(
let typename = extract_typename(trait_name);

// Validate trait
if !typename
.as_ref()
.map_or(false, |seg| seg.ident.to_string().ends_with("Virtual"))
{
if !typename.as_ref().map_or(false, |seg| {
seg.ident == "GodotExt" || seg.ident.to_string().ends_with("Virtual")
}) {
return bail(
format!("#[{attr}] for trait impls requires a virtual method trait (trait name should end in 'Virtual')"),
original_impl,
Expand All @@ -407,6 +407,9 @@ pub(crate) fn validate_trait_impl_virtual(
})
}

/// Check the `MyType` (self) part of `impl Trait for MyType`.
///
/// Returns the type name of `Self`, or error.
fn validate_self(original_impl: &Impl, attr: &str) -> ParseResult<Ident> {
if let Some(segment) = extract_typename(&original_impl.self_ty) {
if segment.generic_args.is_none() {
Expand Down
2 changes: 1 addition & 1 deletion itest/rust/src/codegen_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct TestBaseRenamed {
}

#[godot_api]
impl HttpRequestVirtual for TestBaseRenamed {
impl GodotExt for TestBaseRenamed {
fn init(base: Base<HttpRequest>) -> Self {
TestBaseRenamed { base }
}
Expand Down
2 changes: 1 addition & 1 deletion itest/rust/src/export_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl HasProperty {
}

#[godot_api]
impl NodeVirtual for HasProperty {
impl GodotExt for HasProperty {
fn init(base: Base<Node>) -> Self {
HasProperty {
int_val: 0,
Expand Down
2 changes: 1 addition & 1 deletion itest/rust/src/object_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ pub struct ObjPayload {
}

#[godot_api]
impl RefCountedVirtual for ObjPayload {
impl GodotExt for ObjPayload {
fn init(_base: Base<Self::Base>) -> Self {
Self { value: 111 }
}
Expand Down
6 changes: 3 additions & 3 deletions itest/rust/src/virtual_methods_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct VirtualMethodTest {
impl VirtualMethodTest {}

#[godot_api]
impl RefCountedVirtual for VirtualMethodTest {
impl GodotExt for VirtualMethodTest {
fn to_string(&self) -> GodotString {
format!("VirtualMethodTest[integer={}]", self.integer).into()
}
Expand All @@ -50,7 +50,7 @@ struct ReadyVirtualTest {
}

#[godot_api]
impl Node2DVirtual for ReadyVirtualTest {
impl GodotExt for ReadyVirtualTest {
fn init(base: Base<Node2D>) -> Self {
ReadyVirtualTest {
some_base: base,
Expand All @@ -73,7 +73,7 @@ struct TreeVirtualTest {
}

#[godot_api]
impl Node2DVirtual for TreeVirtualTest {
impl GodotExt for TreeVirtualTest {
fn init(base: Base<Node2D>) -> Self {
TreeVirtualTest {
some_base: base,
Expand Down