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,55 @@ impl ExportedField {
208
224
}
209
225
}
210
226
227
+ struct ExportedField {
228
+ field : Field ,
229
+ getter : String ,
230
+ setter : String ,
231
+ }
232
+
233
+ impl ExportedField {
234
+ pub fn new_from_kv (
235
+ field : Field ,
236
+ attr : & Attribute ,
237
+ mut map : KvMap ,
238
+ ) -> Result < ExportedField , venial:: Error > {
239
+ let export_getter: String ;
240
+ let export_setter: String ;
241
+ if let Some ( getter) = map. remove ( "getter" ) {
242
+ if let KvValue :: Lit ( getter) = getter {
243
+ export_getter = getter;
244
+ } else {
245
+ return Err ( bail_error (
246
+ "#[property] attribute with a non-literal getter" ,
247
+ attr,
248
+ ) ) ;
249
+ }
250
+ } else {
251
+ return Err ( bail_error ( "#[property] attribute without a getter" , attr) ) ;
252
+ }
253
+ if let Some ( setter) = map. remove ( "setter" ) {
254
+ if let KvValue :: Lit ( setter) = setter {
255
+ export_setter = setter;
256
+ } else {
257
+ return Err ( bail_error (
258
+ "#[property] attribute with a non-literal setter" ,
259
+ attr,
260
+ ) ) ;
261
+ }
262
+ } else {
263
+ return Err ( bail_error ( "#[property] attribute without a setter" , attr) ) ;
264
+ }
265
+ ensure_kv_empty ( map, attr. __span ( ) ) ?;
266
+ return Ok ( ExportedField {
267
+ field,
268
+ getter : export_getter,
269
+ setter : export_setter,
270
+ } ) ;
271
+ }
272
+ }
273
+
211
274
fn make_godot_init_impl ( class_name : & Ident , fields : Fields ) -> TokenStream {
212
- let base_init = if let Some ( ExportedField { name, .. } ) = fields. base_field {
275
+ let base_init = if let Some ( Field { name, .. } ) = fields. base_field {
213
276
quote ! { #name: base, }
214
277
} else {
215
278
TokenStream :: new ( )
@@ -232,7 +295,7 @@ fn make_godot_init_impl(class_name: &Ident, fields: Fields) -> TokenStream {
232
295
}
233
296
234
297
fn make_deref_impl ( class_name : & Ident , fields : & Fields ) -> TokenStream {
235
- let base_field = if let Some ( ExportedField { name, .. } ) = & fields. base_field {
298
+ let base_field = if let Some ( Field { name, .. } ) = & fields. base_field {
236
299
name
237
300
} else {
238
301
return TokenStream :: new ( ) ;
@@ -253,3 +316,47 @@ fn make_deref_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
253
316
}
254
317
}
255
318
}
319
+
320
+ fn make_exports_impl ( class_name : & Ident , fields : & Fields ) -> TokenStream {
321
+ let export_tokens = fields
322
+ . exported_fields
323
+ . iter ( )
324
+ . map ( |exported_field : & ExportedField | {
325
+ use std:: str:: FromStr ;
326
+ let name = exported_field. field . name . to_string ( ) ;
327
+ let getter = proc_macro2:: Literal :: from_str ( & exported_field. getter ) . unwrap ( ) ;
328
+ let setter = proc_macro2:: Literal :: from_str ( & exported_field. setter ) . unwrap ( ) ;
329
+ quote ! {
330
+ let class_name = :: godot:: builtin:: StringName :: from( #class_name:: CLASS_NAME ) ;
331
+ let property_info = :: godot:: builtin:: meta:: PropertyInfo :: new(
332
+ :: godot:: sys:: VariantType :: Int ,
333
+ :: godot:: builtin:: meta:: ClassName :: new:: <#class_name>( ) ,
334
+ :: godot:: builtin:: StringName :: from( #name) ,
335
+ ) ;
336
+ let property_info_sys = property_info. property_sys( ) ;
337
+
338
+ let getter_string_name = :: godot:: builtin:: StringName :: from( #getter) ;
339
+ let setter_string_name = :: godot:: builtin:: StringName :: from( #setter) ;
340
+ unsafe {
341
+ :: godot:: sys:: interface_fn!( classdb_register_extension_class_property) (
342
+ :: godot:: sys:: get_library( ) ,
343
+ class_name. string_sys( ) ,
344
+ std:: ptr:: addr_of!( property_info_sys) ,
345
+ setter_string_name. string_sys( ) ,
346
+ getter_string_name. string_sys( ) ,
347
+ ) ;
348
+ }
349
+ }
350
+ } ) ;
351
+ quote ! {
352
+ impl :: godot:: obj:: cap:: ImplementsGodotExports for #class_name {
353
+ fn __register_exports( ) {
354
+ #(
355
+ {
356
+ #export_tokens
357
+ }
358
+ ) *
359
+ }
360
+ }
361
+ }
362
+ }
0 commit comments