1
1
use quote:: { format_ident, quote, ToTokens } ;
2
2
use syn:: { visit:: Visit , DeriveInput , ItemImpl , Type } ;
3
+ use std:: fs:: OpenOptions ;
4
+ use std:: io:: { Read , Write } ;
3
5
4
6
use crate :: common:: { AttributeArgs , GetImplMethod } ;
5
- use crate :: types:: { OutputType , SupportedType } ;
7
+ use crate :: types:: { OutputType , SupportedType , GetSupportedType } ;
6
8
7
9
pub fn generate_custom_into_js_result ( parsed : DeriveInput ) -> proc_macro:: TokenStream {
8
10
let name = parsed. ident ;
@@ -14,19 +16,43 @@ pub fn generate_custom_into_js_result(parsed: DeriveInput) -> proc_macro::TokenS
14
16
_ => panic ! ( "custom_into_js proc_macro should only be used on structs" ) ,
15
17
} ;
16
18
17
- let sets: Vec < proc_macro2:: TokenStream > = fields_named
19
+ let mut sets = Vec :: new ( ) ;
20
+ let mut interface = format ! ( "\n interface {} {{\n " , name) ;
21
+
22
+ fields_named
18
23
. named
19
24
. into_pairs ( )
20
- . map ( |p| {
25
+ . for_each ( |p| {
21
26
let v = p. into_value ( ) ;
22
27
let name = v. ident . to_token_stream ( ) . to_string ( ) ;
23
28
let name_ident = v. ident ;
24
- quote ! {
29
+ sets . push ( quote ! {
25
30
let js_item = self . #name_ident. into_js_result( cx) ?;
26
31
js_object. set( cx, #name, js_item) ?;
27
- }
28
- } )
29
- . collect ( ) ;
32
+ } ) ;
33
+ let ty = GetSupportedType :: get_type ( & v. ty ) ;
34
+ let decleration = match & ty {
35
+ SupportedType :: Option ( o) => format ! ( "{}?" , get_typescript_type( o) ) ,
36
+ _ => get_typescript_type ( & ty)
37
+ } ;
38
+ interface. push_str ( & format ! ( "\t {}: {},\n " , name, decleration) ) ;
39
+ } ) ;
40
+
41
+ interface. push ( '}' ) ;
42
+ let mut file = OpenOptions :: new ( )
43
+ . create ( true )
44
+ . write ( true )
45
+ . append ( true )
46
+ . read ( true )
47
+ . open ( "javascript/index.d.ts" )
48
+ . unwrap ( ) ;
49
+ let mut contents = String :: new ( ) ;
50
+ file. read_to_string ( & mut contents)
51
+ . expect ( "Unable to read typescript decleration file" ) ;
52
+ if !contents. contains ( & interface) {
53
+ file. write_all ( interface. as_bytes ( ) )
54
+ . expect ( "Unable to write typescript decleration file" ) ;
55
+ }
30
56
31
57
let out = quote ! {
32
58
impl IntoJsResult for #name {
@@ -77,6 +103,9 @@ pub fn generate_javascript_methods(
77
103
} ;
78
104
let name_ident = format_ident ! ( "{}Javascript" , wrapped_type_ident) ;
79
105
106
+ let javascript_class_name = wrapped_type_ident. to_string ( ) ;
107
+ let mut typescript_declarations = format ! ( "\n declare class {} {{\n " , javascript_class_name) ;
108
+
80
109
// Iterate over the items - see: https://docs.rs/syn/latest/syn/enum.ImplItem.html
81
110
for item in parsed. items {
82
111
// We only create methods for functions listed in the attribute args
@@ -106,6 +135,34 @@ pub fn generate_javascript_methods(
106
135
OutputType :: Default => ( None , None ) ,
107
136
} ;
108
137
138
+ let p1 = method_ident. to_string ( ) ;
139
+ let p2 = method
140
+ . method_arguments
141
+ . iter ( )
142
+ . filter ( |a| !matches ! ( a. 1 , SupportedType :: S ) )
143
+ . map ( |a| {
144
+ match & a. 1 {
145
+ SupportedType :: Option ( o) => format ! ( "{}?: {}" , a. 0 , get_typescript_type( o) ) ,
146
+ _ => format ! ( "{}: {}" , a. 0 , get_typescript_type( & a. 1 ) )
147
+ }
148
+ } )
149
+ . collect :: < Vec < String > > ( )
150
+ . join ( ", " ) ;
151
+ let p3 = match & method. output_type {
152
+ OutputType :: Result ( v) | OutputType :: Other ( v) => {
153
+ match v {
154
+ SupportedType :: S => wrapped_type_ident. to_string ( ) ,
155
+ _ => get_typescript_type ( v) ,
156
+ }
157
+ } ,
158
+ OutputType :: Default => "void" . to_string ( ) ,
159
+ } ;
160
+ if method. is_async {
161
+ typescript_declarations. push_str ( & format ! ( "\n \t {}({}): Promise<{}>;\n " , p1, p2, p3) ) ;
162
+ } else {
163
+ typescript_declarations. push_str ( & format ! ( "\n \t {}({}): {};\n " , p1, p2, p3) ) ;
164
+ }
165
+
109
166
let method_name_string = method_ident. to_string ( ) ;
110
167
object_sets. push ( quote ! {
111
168
let f: Handle <JsFunction > = JsFunction :: new( cx, #name_ident:: #method_ident) ?;
@@ -193,6 +250,23 @@ pub fn generate_javascript_methods(
193
250
methods. push ( mq) ;
194
251
}
195
252
253
+ typescript_declarations. push ( '}' ) ;
254
+
255
+ let mut file = OpenOptions :: new ( )
256
+ . create ( true )
257
+ . write ( true )
258
+ . append ( true )
259
+ . read ( true )
260
+ . open ( "javascript/index.d.ts" )
261
+ . unwrap ( ) ;
262
+ let mut contents = String :: new ( ) ;
263
+ file. read_to_string ( & mut contents)
264
+ . expect ( "Unable to read typescript declaration file for python" ) ;
265
+ if !contents. contains ( & format ! ( "declare class {}" , javascript_class_name) ) {
266
+ file. write_all ( typescript_declarations. as_bytes ( ) )
267
+ . expect ( "Unable to write typescript declaration file for python" ) ;
268
+ }
269
+
196
270
proc_macro:: TokenStream :: from ( quote ! {
197
271
impl #name_ident {
198
272
#( #methods) *
@@ -230,7 +304,7 @@ fn get_method_wrapper_arguments_javascript(
230
304
argument_ident. clone ( ) ,
231
305
argument_type,
232
306
) ;
233
- let argument_type_js = get_javascript_type ( argument_type) ;
307
+ let argument_type_js = get_neon_type ( argument_type) ;
234
308
let method_argument = match argument_type {
235
309
SupportedType :: Option ( _o) => quote ! {
236
310
let #argument_ident = cx. argument_opt( #i as i32 ) ;
@@ -267,9 +341,9 @@ fn convert_method_wrapper_arguments(
267
341
}
268
342
}
269
343
270
- fn get_javascript_type ( ty : & SupportedType ) -> syn:: Type {
344
+ fn get_neon_type ( ty : & SupportedType ) -> syn:: Type {
271
345
match ty {
272
- SupportedType :: Reference ( r) => get_javascript_type ( r) ,
346
+ SupportedType :: Reference ( r) => get_neon_type ( r) ,
273
347
SupportedType :: str | SupportedType :: String => syn:: parse_str ( "JsString" ) . unwrap ( ) ,
274
348
SupportedType :: Vec ( _v) => syn:: parse_str ( "JsArray" ) . unwrap ( ) ,
275
349
SupportedType :: S => syn:: parse_str ( "JsObject" ) . unwrap ( ) ,
@@ -285,7 +359,7 @@ fn get_javascript_type(ty: &SupportedType) -> syn::Type {
285
359
}
286
360
}
287
361
288
- pub fn convert_output_type_convert_from_javascript (
362
+ fn convert_output_type_convert_from_javascript (
289
363
ty : & SupportedType ,
290
364
method : & GetImplMethod ,
291
365
) -> (
@@ -302,7 +376,7 @@ pub fn convert_output_type_convert_from_javascript(
302
376
Some ( format_ident ! ( "{}Javascript" , t. to_string( ) ) . into_token_stream ( ) ) ,
303
377
) ,
304
378
t => {
305
- let ty = get_javascript_type ( t) ;
379
+ let ty = get_neon_type ( t) ;
306
380
( Some ( quote ! { JsResult <' a, #ty>} ) , None )
307
381
}
308
382
} ;
@@ -313,3 +387,37 @@ pub fn convert_output_type_convert_from_javascript(
313
387
( output_type, convert_from)
314
388
}
315
389
}
390
+
391
+ fn get_typescript_type ( ty : & SupportedType ) -> String {
392
+ match ty {
393
+ SupportedType :: Reference ( r) => get_typescript_type ( r) ,
394
+ SupportedType :: str | SupportedType :: String => "string" . to_string ( ) ,
395
+ SupportedType :: Option ( o) => get_typescript_type ( o) ,
396
+ SupportedType :: Vec ( v) => format ! ( "{}[]" , get_typescript_type( v) ) ,
397
+ SupportedType :: HashMap ( ( k, v) ) => {
398
+ format ! ( "Map<{}, {}>" , get_typescript_type( k) , get_typescript_type( v) )
399
+ } ,
400
+ SupportedType :: JsonHashMap => "Map<string, string>" . to_string ( ) ,
401
+ SupportedType :: DateTime => "Date" . to_string ( ) ,
402
+ SupportedType :: Tuple ( t) => {
403
+ let mut types = Vec :: new ( ) ;
404
+ for ty in t {
405
+ types. push ( get_typescript_type ( ty) ) ;
406
+ }
407
+ // Rust's unit type is represented as an empty tuple
408
+ if types. is_empty ( ) {
409
+ "void" . to_string ( )
410
+ } else {
411
+ format ! ( "[{}]" , types. join( ", " ) )
412
+ }
413
+ }
414
+ SupportedType :: i64 | SupportedType :: f64 => "number" . to_string ( ) ,
415
+ // Our own types
416
+ t @ SupportedType :: Database
417
+ | t @ SupportedType :: Collection
418
+ | t @ SupportedType :: Splitter => t. to_string ( ) ,
419
+ | t @ SupportedType :: Model => t. to_string ( ) ,
420
+ // Add more types as required
421
+ _ => "any" . to_string ( ) ,
422
+ }
423
+ }
0 commit comments