@@ -6,9 +6,9 @@ use proc_macro2::TokenStream;
66use quote:: { format_ident, quote} ;
77
88use std:: collections:: HashMap ;
9- use std:: collections:: HashSet ;
109
1110/// Types of icalls.
11+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
1212pub ( crate ) enum IcallType {
1313 Ptr ,
1414 Varargs ,
@@ -265,6 +265,14 @@ pub(crate) fn generate_methods(
265265 icalls : & mut HashMap < String , MethodSig > ,
266266 docs : Option < & GodotXmlDocs > ,
267267) -> TokenStream {
268+ /// Memorized information about generated methods. Used to generate indexed property accessors.
269+ struct Generated {
270+ icall : proc_macro2:: Ident ,
271+ icall_ty : IcallType ,
272+ maybe_unsafe : TokenStream ,
273+ maybe_unsafe_reason : & ' static str ,
274+ }
275+
268276 // Brings values of some types to a type with less information.
269277 fn arg_erase ( ty : & Ty , name : & proc_macro2:: Ident ) -> TokenStream {
270278 match ty {
@@ -280,11 +288,14 @@ pub(crate) fn generate_methods(
280288
281289 Ty :: Object ( _) => quote ! { #name. as_arg_ptr( ) } ,
282290
291+ // Allow lossy casting of numeric types into similar primitives, see also to_return_post
292+ Ty :: I64 | Ty :: F64 | Ty :: Bool => quote ! { #name as _ } ,
293+
283294 _ => quote ! { #name } ,
284295 }
285296 }
286297
287- let mut method_set = HashSet :: new ( ) ;
298+ let mut generated = HashMap :: new ( ) ;
288299 let mut result = TokenStream :: new ( ) ;
289300
290301 for method in & class. methods {
@@ -301,11 +312,9 @@ pub(crate) fn generate_methods(
301312 let mut rust_ret_type = ret_type. to_rust ( ) ;
302313
303314 // Ensure that methods are not injected several times.
304- let method_name_string = method_name. to_string ( ) ;
305- if method_set. contains ( & method_name_string) {
315+ if generated. contains_key ( method_name) {
306316 continue ;
307317 }
308- method_set. insert ( method_name_string) ;
309318
310319 let mut params_decl = TokenStream :: new ( ) ;
311320 let mut params_use = TokenStream :: new ( ) ;
@@ -387,7 +396,126 @@ pub(crate) fn generate_methods(
387396 }
388397 }
389398 } ;
399+
390400 result. extend ( output) ;
401+
402+ generated. insert (
403+ method_name. to_string ( ) ,
404+ Generated {
405+ icall,
406+ icall_ty,
407+ maybe_unsafe,
408+ maybe_unsafe_reason,
409+ } ,
410+ ) ;
411+ }
412+
413+ for property in & class. properties {
414+ if property. index < 0 || property. name . contains ( '/' ) {
415+ continue ;
416+ }
417+
418+ let property_index = property. index ;
419+ let ty = Ty :: from_src ( & property. type_ ) ;
420+
421+ if let Some ( Generated {
422+ icall,
423+ icall_ty,
424+ maybe_unsafe,
425+ maybe_unsafe_reason,
426+ } ) = generated. get ( & property. getter )
427+ {
428+ let rusty_name = rust_safe_name ( & property. name ) ;
429+ let rust_ret_type = ty. to_rust ( ) ;
430+
431+ let method_bind_fetch = {
432+ let method_table = format_ident ! ( "{}MethodTable" , class. name) ;
433+ let rust_method_name = format_ident ! ( "{}" , property. getter) ;
434+
435+ quote ! {
436+ let method_bind: * mut sys:: godot_method_bind = #method_table:: get( get_api( ) ) . #rust_method_name;
437+ }
438+ } ;
439+
440+ let doc_comment = docs
441+ . and_then ( |docs| {
442+ docs. get_class_method_desc (
443+ class. name . as_str ( ) ,
444+ & format ! ( "get_{}" , property. name) ,
445+ )
446+ } )
447+ . unwrap_or ( "" ) ;
448+
449+ let recover = ret_recover ( & ty, * icall_ty) ;
450+
451+ let output = quote ! {
452+ #[ doc = #doc_comment]
453+ #[ doc = #maybe_unsafe_reason]
454+ #[ inline]
455+ pub #maybe_unsafe fn #rusty_name( & self ) -> #rust_ret_type {
456+ unsafe {
457+ #method_bind_fetch
458+
459+ let ret = crate :: icalls:: #icall( method_bind, self . this. sys( ) . as_ptr( ) , #property_index) ;
460+
461+ #recover
462+ }
463+ }
464+ } ;
465+
466+ result. extend ( output) ;
467+ }
468+
469+ if let Some ( Generated {
470+ icall,
471+ icall_ty,
472+ maybe_unsafe,
473+ maybe_unsafe_reason,
474+ } ) = generated. get ( & property. setter )
475+ {
476+ let rusty_name = rust_safe_name ( & format ! ( "set_{}" , property. name) ) ;
477+
478+ let rust_arg_ty = ty. to_rust_arg ( ) ;
479+ let arg_ident = format_ident ! ( "value" ) ;
480+ let arg_erased = arg_erase ( & ty, & arg_ident) ;
481+
482+ let method_bind_fetch = {
483+ let method_table = format_ident ! ( "{}MethodTable" , class. name) ;
484+ let rust_method_name = format_ident ! ( "{}" , property. setter) ;
485+
486+ quote ! {
487+ let method_bind: * mut sys:: godot_method_bind = #method_table:: get( get_api( ) ) . #rust_method_name;
488+ }
489+ } ;
490+
491+ let doc_comment = docs
492+ . and_then ( |docs| {
493+ docs. get_class_method_desc (
494+ class. name . as_str ( ) ,
495+ & format ! ( "set_{}" , property. name) ,
496+ )
497+ } )
498+ . unwrap_or ( "" ) ;
499+
500+ let recover = ret_recover ( & Ty :: Void , * icall_ty) ;
501+
502+ let output = quote ! {
503+ #[ doc = #doc_comment]
504+ #[ doc = #maybe_unsafe_reason]
505+ #[ inline]
506+ pub #maybe_unsafe fn #rusty_name( & self , #arg_ident: #rust_arg_ty) {
507+ unsafe {
508+ #method_bind_fetch
509+
510+ let ret = crate :: icalls:: #icall( method_bind, self . this. sys( ) . as_ptr( ) , #property_index, #arg_erased) ;
511+
512+ #recover
513+ }
514+ }
515+ } ;
516+
517+ result. extend ( output) ;
518+ }
391519 }
392520
393521 result
0 commit comments