wasm_typescript_definition/
lib.rs1extern crate proc_macro2;
2extern crate proc_macro;
3#[macro_use]
4extern crate quote;
5extern crate serde_derive;
6extern crate serde_derive_internals;
7extern crate syn;
8extern crate serde;
9
10use serde_derive_internals::{ast, Ctxt, Derive};
11use syn::DeriveInput;
12use quote::TokenStreamExt;
13mod derive_enum;
14mod derive_struct;
15use proc_macro2::{TokenStream, Span};
16
17
18#[cfg(feature = "bytes")]
19extern crate serde_bytes;
20
21
22#[proc_macro_derive(TypescriptDefinition)]
23pub fn derive_typescript_definition(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
24 let input: DeriveInput = syn::parse(input).unwrap();
26
27 let cx = Ctxt::new();
28 let container = ast::Container::from_ast(&cx, &input, Derive::Serialize);
29
30 let typescript = match container.data {
31 ast::Data::Enum(variants) => {
32 derive_enum::derive_enum(variants, &container.attrs)
33 }
34 ast::Data::Struct(style, fields) => {
35 derive_struct::derive_struct(style, fields, &container.attrs)
36 }
37 };
38 let typescript_name = container.ident.clone();
39
40 let type_string = typescript.to_string().replace("\n", " ").replace(" ", " ");
41 let typescript_string = quote!{
42 export type #typescript_name =
43 #typescript
44 ;
45 }.to_string();
46
47 let export_ident = syn::Ident::new(&format!("TS_EXPORT_{}", container.ident.to_string().to_uppercase()), Span::call_site());
54 let mut expanded = quote!{
55
56 #[wasm_bindgen(typescript_custom_section)]
57 const #export_ident : &'static str = #typescript_string;
58
59 };
60
61 if cfg!(any(debug_assertions, feature = "test-export")) {
63 let typescript_ident = syn::Ident::new(&format!("{}___typescript_definition", container.ident), Span::call_site());
64 expanded.append_all(quote!{
65 fn #typescript_ident ( ) -> &'static str {
66 #type_string
67 }
68 });
69 }
70
71 cx.check().unwrap();
72
73 expanded.into()
74}
75
76fn collapse_list_bracket(body: Vec<TokenStream>) -> TokenStream {
77 if body.len() == 1 {
78 body[0].clone()
79 } else {
80 let last_index = body.len() - 1;
81 let tokens = body.into_iter()
82 .enumerate()
83 .fold(quote!{}, |mut agg, (index, tokens)| {
84 if index == last_index {
85 agg.append_all(quote!{ #tokens });
86 } else {
87 agg.append_all(quote!{ #tokens , });
88 }
89 agg
90 });
91 quote!{ [ #tokens ] }
92 }
93}
94
95fn collapse_list_brace(body: Vec<TokenStream>) -> TokenStream {
96 let tokens = body.into_iter().fold(quote!{}, |mut agg, tokens| { agg.append_all(quote!{ #tokens , }); agg });
97 quote!{ { #tokens } }
98}
99
100fn type_to_ts(ty: &syn::Type) -> TokenStream {
101 use syn::Type::*;
103 match ty {
104 Slice(..) => quote!{ any },
105 Array(..) => quote!{ any },
106 Ptr(..) => quote!{ any },
107 Reference(..) => quote!{ any },
108 BareFn(..) => quote!{ any },
109 Never(..) => quote!{ any },
110 Tuple(..) => quote!{ any },
111 TraitObject(..) => quote!{ any },
112 ImplTrait(..) => quote!{ any },
113 Paren(..) => quote!{ any },
114 Group(..) => quote!{ any },
115 Infer(..) => quote!{ any },
116 Macro(..) => quote!{ any },
117 Verbatim(..) => quote!{ any },
118 Path(inner) => {
119 let result = quote!{ #inner };
121 match result.to_string().as_ref() {
122 "u8" | "u16" | "u32" | "u64" | "u128" | "usize" |
123 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" =>
124 quote! { number },
125 "String" | "&str" | "&'static str" =>
126 quote! { string },
127 "bool" => quote!{ boolean },
128 _ => quote! { any },
129 }
130 }
131 }
132}
133
134fn derive_field<'a>(_variant_idx: usize, _field_idx: usize, field: &ast::Field<'a>) -> TokenStream {
135 let field_name = field.attrs.name().serialize_name();
136 let ty = type_to_ts(&field.ty);
137 quote!{
138 #field_name: #ty
139 }
140}
141
142fn derive_element<'a>(_variant_idx: usize, _element_idx: usize, field: &ast::Field<'a>) -> TokenStream {
143 let ty = type_to_ts(&field.ty);
144 quote!{
145 #ty
146 }
147}