wasm_typescript_definition/
lib.rs

1extern 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    // eprintln!(".........[input] {}", input);
25    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    // eprintln!("....[typescript] {:?}", typescript_string);
48    // eprintln!("........[schema] {:?}", inner_impl);
49    // eprintln!();
50    // eprintln!();
51    // eprintln!();
52
53    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    // For testing, export a function with its contents
62    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    // println!("??? {:?}", ty);
102    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 ty_string = format!("{}", inner.path);
120            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}