extern crate proc_macro2;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate serde_derive;
extern crate serde_derive_internals;
extern crate syn;
extern crate serde;
use serde_derive_internals::{ast, Ctxt, Derive};
use syn::DeriveInput;
use quote::TokenStreamExt;
mod derive_enum;
mod derive_struct;
use proc_macro2::{TokenStream, Span};
#[cfg(feature = "bytes")]
extern crate serde_bytes;
#[proc_macro_derive(TypescriptDefinition)]
pub fn derive_typescript_definition(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let cx = Ctxt::new();
let container = ast::Container::from_ast(&cx, &input, Derive::Serialize);
let typescript = match container.data {
ast::Data::Enum(variants) => {
derive_enum::derive_enum(variants, &container.attrs)
}
ast::Data::Struct(style, fields) => {
derive_struct::derive_struct(style, fields, &container.attrs)
}
};
let typescript_name = container.ident.clone();
let type_string = typescript.to_string().replace("\n", " ").replace(" ", " ");
let typescript_string = quote!{
export type #typescript_name =
#typescript
;
}.to_string();
let export_ident = syn::Ident::new(&format!("TS_EXPORT_{}", container.ident.to_string().to_uppercase()), Span::call_site());
let mut expanded = quote!{
#[wasm_bindgen(typescript_custom_section)]
const #export_ident : &'static str = #typescript_string;
};
if cfg!(any(debug_assertions, feature = "test-export")) {
let typescript_ident = syn::Ident::new(&format!("{}___typescript_definition", container.ident), Span::call_site());
expanded.append_all(quote!{
fn #typescript_ident ( ) -> &'static str {
#type_string
}
});
}
cx.check().unwrap();
expanded.into()
}
fn collapse_list_bracket(body: Vec<TokenStream>) -> TokenStream {
if body.len() == 1 {
body[0].clone()
} else {
let last_index = body.len() - 1;
let tokens = body.into_iter()
.enumerate()
.fold(quote!{}, |mut agg, (index, tokens)| {
if index == last_index {
agg.append_all(quote!{ #tokens });
} else {
agg.append_all(quote!{ #tokens , });
}
agg
});
quote!{ [ #tokens ] }
}
}
fn collapse_list_brace(body: Vec<TokenStream>) -> TokenStream {
let tokens = body.into_iter().fold(quote!{}, |mut agg, tokens| { agg.append_all(quote!{ #tokens , }); agg });
quote!{ { #tokens } }
}
fn type_to_ts(ty: &syn::Type) -> TokenStream {
use syn::Type::*;
match ty {
Slice(..) => quote!{ any },
Array(..) => quote!{ any },
Ptr(..) => quote!{ any },
Reference(..) => quote!{ any },
BareFn(..) => quote!{ any },
Never(..) => quote!{ any },
Tuple(..) => quote!{ any },
TraitObject(..) => quote!{ any },
ImplTrait(..) => quote!{ any },
Paren(..) => quote!{ any },
Group(..) => quote!{ any },
Infer(..) => quote!{ any },
Macro(..) => quote!{ any },
Verbatim(..) => quote!{ any },
Path(inner) => {
let result = quote!{ #inner };
match result.to_string().as_ref() {
"u8" | "u16" | "u32" | "u64" | "u128" | "usize" |
"i8" | "i16" | "i32" | "i64" | "i128" | "isize" =>
quote! { number },
"String" | "&str" | "&'static str" =>
quote! { string },
"bool" => quote!{ boolean },
_ => quote! { any },
}
}
}
}
fn derive_field<'a>(_variant_idx: usize, _field_idx: usize, field: &ast::Field<'a>) -> TokenStream {
let field_name = field.attrs.name().serialize_name();
let ty = type_to_ts(&field.ty);
quote!{
#field_name: #ty
}
}
fn derive_element<'a>(_variant_idx: usize, _element_idx: usize, field: &ast::Field<'a>) -> TokenStream {
let ty = type_to_ts(&field.ty);
quote!{
#ty
}
}