4
4
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
*/
6
6
7
- use crate :: util:: { bail, ensure_kv_empty, ident, path_is_single, KvMap , KvValue } ;
7
+ use crate :: util:: {
8
+ bail, bail_error, ensure_kv_empty, ident, parse_kv_group, path_is_single, KvMap , KvValue ,
9
+ } ;
8
10
use crate :: { util, ParseResult } ;
9
11
use proc_macro2:: { Ident , Punct , Span , TokenStream } ;
10
12
use quote:: spanned:: Spanned ;
@@ -30,6 +32,8 @@ pub fn transform(input: TokenStream) -> ParseResult<TokenStream> {
30
32
let prv = quote ! { :: godot:: private } ;
31
33
let deref_impl = make_deref_impl ( class_name, & fields) ;
32
34
35
+ let godot_exports_impl = make_exports_impl ( class_name, & fields) ;
36
+
33
37
let ( godot_init_impl, create_fn) ;
34
38
if struct_cfg. has_generated_init {
35
39
godot_init_impl = make_godot_init_impl ( class_name, fields) ;
@@ -49,6 +53,7 @@ pub fn transform(input: TokenStream) -> ParseResult<TokenStream> {
49
53
}
50
54
51
55
#godot_init_impl
56
+ #godot_exports_impl
52
57
#deref_impl
53
58
54
59
:: godot:: sys:: plugin_add!( __GODOT_PLUGIN_REGISTRY in #prv; #prv:: ClassPlugin {
@@ -111,7 +116,7 @@ fn parse_struct_attributes(class: &Struct) -> ParseResult<ClassAttributes> {
111
116
fn parse_fields ( class : & Struct ) -> ParseResult < Fields > {
112
117
let mut all_field_names = vec ! [ ] ;
113
118
let mut exported_fields = vec ! [ ] ;
114
- let mut base_field = Option :: < ExportedField > :: None ;
119
+ let mut base_field = Option :: < Field > :: None ;
115
120
116
121
let fields: Vec < ( NamedField , Punct ) > = match & class. fields {
117
122
StructFields :: Unit => {
@@ -142,9 +147,18 @@ fn parse_fields(class: &Struct) -> ParseResult<Fields> {
142
147
attr,
143
148
) ?;
144
149
}
145
- base_field = Some ( ExportedField :: new ( & field) )
150
+ base_field = Some ( Field :: new ( & field) )
146
151
} else if path == "export" {
147
- exported_fields. push ( ExportedField :: new ( & field) )
152
+ match parse_kv_group ( & attr. value ) {
153
+ Ok ( export_kv) => {
154
+ let exported_field =
155
+ ExportedField :: new_from_kv ( Field :: new ( & field) , & attr, export_kv) ?;
156
+ exported_fields. push ( exported_field) ;
157
+ }
158
+ Err ( error) => {
159
+ return Err ( error) ;
160
+ }
161
+ }
148
162
}
149
163
}
150
164
}
@@ -158,6 +172,7 @@ fn parse_fields(class: &Struct) -> ParseResult<Fields> {
158
172
Ok ( Fields {
159
173
all_field_names,
160
174
base_field,
175
+ exported_fields,
161
176
} )
162
177
}
163
178
@@ -191,15 +206,16 @@ struct ClassAttributes {
191
206
192
207
struct Fields {
193
208
all_field_names : Vec < Ident > ,
194
- base_field : Option < ExportedField > ,
209
+ base_field : Option < Field > ,
210
+ exported_fields : Vec < ExportedField > ,
195
211
}
196
212
197
- struct ExportedField {
213
+ struct Field {
198
214
name : Ident ,
199
215
_ty : TyExpr ,
200
216
}
201
217
202
- impl ExportedField {
218
+ impl Field {
203
219
fn new ( field : & NamedField ) -> Self {
204
220
Self {
205
221
name : field. name . clone ( ) ,
@@ -208,8 +224,54 @@ impl ExportedField {
208
224
}
209
225
}
210
226
227
+ struct ExportedField {
228
+ field : Field ,
229
+ getter : String ,
230
+ setter : String ,
231
+ variant_type : String ,
232
+ }
233
+
234
+ impl ExportedField {
235
+ pub fn new_from_kv (
236
+ field : Field ,
237
+ attr : & Attribute ,
238
+ mut map : KvMap ,
239
+ ) -> ParseResult < ExportedField > {
240
+ let getter = Self :: require_key_value ( & mut map, "getter" , attr) ?;
241
+ let setter = Self :: require_key_value ( & mut map, "setter" , attr) ?;
242
+ let variant_type = Self :: require_key_value ( & mut map, "variant_type" , attr) ?;
243
+
244
+ ensure_kv_empty ( map, attr. __span ( ) ) ?;
245
+
246
+ return Ok ( ExportedField {
247
+ field,
248
+ getter,
249
+ setter,
250
+ variant_type,
251
+ } ) ;
252
+ }
253
+
254
+ fn require_key_value ( map : & mut KvMap , key : & str , attr : & Attribute ) -> ParseResult < String > {
255
+ if let Some ( value) = map. remove ( key) {
256
+ if let KvValue :: Lit ( value) = value {
257
+ return Ok ( value) ;
258
+ } else {
259
+ return bail (
260
+ format ! (
261
+ "#[export] attribute {} with a non-literal variant_type" ,
262
+ key
263
+ ) ,
264
+ attr,
265
+ ) ?;
266
+ }
267
+ } else {
268
+ return bail ( format ! ( "#[export] attribute without a {}" , key) , attr) ;
269
+ }
270
+ }
271
+ }
272
+
211
273
fn make_godot_init_impl ( class_name : & Ident , fields : Fields ) -> TokenStream {
212
- let base_init = if let Some ( ExportedField { name, .. } ) = fields. base_field {
274
+ let base_init = if let Some ( Field { name, .. } ) = fields. base_field {
213
275
quote ! { #name: base, }
214
276
} else {
215
277
TokenStream :: new ( )
@@ -232,7 +294,7 @@ fn make_godot_init_impl(class_name: &Ident, fields: Fields) -> TokenStream {
232
294
}
233
295
234
296
fn make_deref_impl ( class_name : & Ident , fields : & Fields ) -> TokenStream {
235
- let base_field = if let Some ( ExportedField { name, .. } ) = & fields. base_field {
297
+ let base_field = if let Some ( Field { name, .. } ) = & fields. base_field {
236
298
name
237
299
} else {
238
300
return TokenStream :: new ( ) ;
@@ -253,3 +315,49 @@ fn make_deref_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
253
315
}
254
316
}
255
317
}
318
+
319
+ fn make_exports_impl ( class_name : & Ident , fields : & Fields ) -> TokenStream {
320
+ let export_tokens = fields
321
+ . exported_fields
322
+ . iter ( )
323
+ . map ( |exported_field : & ExportedField | {
324
+ use std:: str:: FromStr ;
325
+ let name = exported_field. field . name . to_string ( ) ;
326
+ let getter = proc_macro2:: Literal :: from_str ( & exported_field. getter ) . unwrap ( ) ;
327
+ let setter = proc_macro2:: Literal :: from_str ( & exported_field. setter ) . unwrap ( ) ;
328
+ let vtype = & exported_field. variant_type ;
329
+ let variant_type: TokenStream = vtype[ 1 ..vtype. len ( ) - 1 ] . parse ( ) . unwrap ( ) ;
330
+ quote ! {
331
+ let class_name = :: godot:: builtin:: StringName :: from( #class_name:: CLASS_NAME ) ;
332
+ let property_info = :: godot:: builtin:: meta:: PropertyInfo :: new(
333
+ #variant_type,
334
+ :: godot:: builtin:: meta:: ClassName :: new:: <#class_name>( ) ,
335
+ :: godot:: builtin:: StringName :: from( #name) ,
336
+ ) ;
337
+ let property_info_sys = property_info. property_sys( ) ;
338
+
339
+ let getter_string_name = :: godot:: builtin:: StringName :: from( #getter) ;
340
+ let setter_string_name = :: godot:: builtin:: StringName :: from( #setter) ;
341
+ unsafe {
342
+ :: godot:: sys:: interface_fn!( classdb_register_extension_class_property) (
343
+ :: godot:: sys:: get_library( ) ,
344
+ class_name. string_sys( ) ,
345
+ std:: ptr:: addr_of!( property_info_sys) ,
346
+ setter_string_name. string_sys( ) ,
347
+ getter_string_name. string_sys( ) ,
348
+ ) ;
349
+ }
350
+ }
351
+ } ) ;
352
+ quote ! {
353
+ impl :: godot:: obj:: cap:: ImplementsGodotExports for #class_name {
354
+ fn __register_exports( ) {
355
+ #(
356
+ {
357
+ #export_tokens
358
+ }
359
+ ) *
360
+ }
361
+ }
362
+ }
363
+ }
0 commit comments