@@ -11,7 +11,7 @@ use crate::models::domain::{ExtensionApi, ModName, NativeStructure, TyName};
11
11
use crate :: util:: ident;
12
12
use crate :: { conv, util, SubmitFn } ;
13
13
use proc_macro2:: TokenStream ;
14
- use quote:: quote;
14
+ use quote:: { format_ident , quote} ;
15
15
use std:: path:: Path ;
16
16
17
17
pub fn generate_native_structures_files (
@@ -52,8 +52,15 @@ pub fn generate_native_structures_files(
52
52
53
53
#[ derive( Clone , Eq , PartialEq , Debug ) ]
54
54
pub ( crate ) struct NativeStructuresField {
55
+ /// Identifier for the field name, e.g. `collider`.
55
56
pub field_name : String ,
57
+
58
+ /// Type which has a raw format that is latter mapped to `RustTy`.
59
+ ///
60
+ /// Corresponds to other Godot type names, e.g. `Object*` or `enum::TextServer.Direction`.
56
61
pub field_type : String ,
62
+
63
+ /// If the field is an array, this contains the number of elements.
57
64
pub array_size : Option < usize > ,
58
65
}
59
66
@@ -65,12 +72,13 @@ fn make_native_structure(
65
72
let class_name = & class_name. rust_ty ;
66
73
67
74
let imports = util:: make_imports ( ) ;
68
- let fields = make_native_structure_fields ( & structure. format , ctx) ;
75
+ let ( fields, methods ) = make_native_structure_fields_and_methods ( & structure. format , ctx) ;
69
76
let doc = format ! ( "[`ToGodot`] and [`FromGodot`] are implemented for `*mut {class_name}` and `*const {class_name}`." ) ;
70
77
71
78
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
72
79
let tokens = quote ! {
73
80
#imports
81
+ use std:: ffi:: c_void; // for opaque object pointers
74
82
use crate :: meta:: { GodotConvert , FromGodot , ToGodot } ;
75
83
76
84
/// Native structure; can be passed via pointer in APIs that are not exposed to GDScript.
@@ -82,6 +90,10 @@ fn make_native_structure(
82
90
#fields
83
91
}
84
92
93
+ impl #class_name {
94
+ #methods
95
+ }
96
+
85
97
impl GodotConvert for * mut #class_name {
86
98
type Via = i64 ;
87
99
}
@@ -119,25 +131,35 @@ fn make_native_structure(
119
131
builtins:: GeneratedBuiltin { code : tokens }
120
132
}
121
133
122
- fn make_native_structure_fields ( format_str : & str , ctx : & mut Context ) -> TokenStream {
134
+ fn make_native_structure_fields_and_methods (
135
+ format_str : & str ,
136
+ ctx : & mut Context ,
137
+ ) -> ( TokenStream , TokenStream ) {
123
138
let fields = parse_native_structures_format ( format_str)
124
139
. expect ( "Could not parse native_structures format field" ) ;
125
140
126
- let field_definitions = fields
127
- . into_iter ( )
128
- . map ( |field| make_native_structure_field_definition ( field, ctx) ) ;
141
+ let mut field_definitions = vec ! [ ] ;
142
+ let mut accessors = vec ! [ ] ;
129
143
130
- quote ! {
131
- #( #field_definitions ) *
144
+ for field in fields {
145
+ let ( field_def, accessor) = make_native_structure_field_and_accessor ( field, ctx) ;
146
+ field_definitions. push ( field_def) ;
147
+ if let Some ( accessor) = accessor {
148
+ accessors. push ( accessor) ;
149
+ }
132
150
}
151
+
152
+ let fields = quote ! { #( #field_definitions ) * } ;
153
+ let methods = quote ! { #( #accessors ) * } ;
154
+ ( fields, methods)
133
155
}
134
156
135
- fn make_native_structure_field_definition (
157
+ fn make_native_structure_field_and_accessor (
136
158
field : NativeStructuresField ,
137
159
ctx : & mut Context ,
138
- ) -> TokenStream {
160
+ ) -> ( TokenStream , Option < TokenStream > ) {
139
161
let field_type = normalize_native_structure_field_type ( & field. field_type ) ;
140
- let field_type = conv:: to_rust_type_abi ( & field_type, ctx) ;
162
+ let ( field_type, is_object_ptr ) = conv:: to_rust_type_abi ( & field_type, ctx) ;
141
163
142
164
// Make array if needed.
143
165
let field_type = if let Some ( size) = field. array_size {
@@ -146,11 +168,36 @@ fn make_native_structure_field_definition(
146
168
quote ! { #field_type }
147
169
} ;
148
170
149
- let field_name = ident ( & conv:: to_snake_case ( & field. field_name ) ) ;
171
+ let snake_field_name = ident ( & conv:: to_snake_case ( & field. field_name ) ) ;
172
+
173
+ let ( field_name, accessor) ;
174
+ if is_object_ptr {
175
+ // Highlight that the pointer field is internal/opaque.
176
+ field_name = format_ident ! ( "__unused_{}_ptr" , snake_field_name) ;
177
+
178
+ // Generate method that converts from instance ID.
179
+ let id_field_name = format_ident ! ( "{}_id" , snake_field_name) ;
180
+ accessor = Some ( quote ! {
181
+ /// Returns the object as a `Gd<T>`, or `None` if it no longer exists.
182
+ pub fn #snake_field_name( & self ) -> Option <Gd <Object >> {
183
+ crate :: obj:: InstanceId :: try_from_u64( self . #id_field_name. id)
184
+ . and_then( |id| Gd :: try_from_instance_id( id) . ok( ) )
150
185
151
- quote ! {
186
+ // Sanity check for consistency (if Some(...)):
187
+ // let ptr = self.#field_name as sys::GDExtensionObjectPtr;
188
+ // unsafe { Gd::from_obj_sys(ptr) }
189
+ }
190
+ } ) ;
191
+ } else {
192
+ field_name = snake_field_name;
193
+ accessor = None ;
194
+ } ;
195
+
196
+ let field_def = quote ! {
152
197
pub #field_name: #field_type,
153
- }
198
+ } ;
199
+
200
+ ( field_def, accessor)
154
201
}
155
202
156
203
fn normalize_native_structure_field_type ( field_type : & str ) -> String {
0 commit comments