wasm_interfacegen_macro/
lib.rs1use proc_macro::TokenStream;
2use syn::{parse_macro_input, DeriveInput};
3use quote::quote;
4
5mod boolean_types;
6mod string_types;
7mod number_types;
8mod any_types;
9mod array_types;
10
11#[proc_macro_derive(JsInterface)]
12pub fn wasm_interfacegen(input: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(input as DeriveInput);
14 let input_ident = &input.ident;
15 let iname = format!("I{}", input.ident);
16 let iident = syn::Ident::new(&iname, input.ident.span());
17
18 let fields = if let syn::Data::Struct(syn::DataStruct {
19 fields: syn::Fields::Named(syn::FieldsNamed {ref named, .. }),
20 ..
21 }) = input.data {
22 named
23 } else {
24 unimplemented!();
25 };
26
27 let idef_ident = syn::Ident::new(&format!("{}_STYLE", iname.clone().to_uppercase()), input.ident.span());
28 let mut idef = format!("interface {} {{ ", iname);
29 let mut req_impls: Vec<&syn::Type> = Vec::new();
30 for field in fields.iter() {
31 let js_type = match map_r_type_to_js_type(&field.ty) {
32 Ok(t) => t,
33 Err(e) => return e.to_compile_error().into()
34 };
35
36 let field_ident = field.ident.as_ref().unwrap();
37 if !is_simple_type(&js_type) {
38 req_impls.push(&field.ty);
39 }
40 idef.push_str(&format!("{}: {}; ", field_ident, js_type))
41 }
42 idef.push_str("}");
43
44 quote! {
45 #[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
46 const #idef_ident: &'static str = #idef;
47
48 #(wasm_interfacegen::assert_impl_all!(#req_impls: wasm_interfacegen::__JsInterface);)*
49
50 #[wasm_bindgen::prelude::wasm_bindgen]
51 extern "C" {
52 #[wasm_bindgen::prelude::wasm_bindgen(typescript_type= #iname)]
53 pub type #iident;
54 }
55
56 impl wasm_interfacegen::__JsInterface for #input_ident {
57 fn js_interface_name() -> &'static str {
58 #iname
59 }
60 }
61 }.into()
62}
63
64fn map_r_type_to_js_type(ty: &syn::Type) -> Result<String, syn::Error> {
65 let name = &ty_name(ty);
66 let js_type: String;
67
68 if string_types::string_types().iter().any(|t| t == name) {
69 js_type = "string".into();
70 } else if number_types::number_types().iter().any(|t| t == name) {
71 js_type = "number".into();
72 } else if boolean_types::bool_types().iter().any(|t| t == name) {
73 js_type = "boolean".into();
74 } else if any_types::any_types().iter().any(|t| t == name) {
75 js_type = "any".into();
76 } else if array_types::array_types().iter().any(|t| t == name) {
77 js_type = "array".into();
78 } else {
79 js_type = format!("I{}", name);
80 }
82
83 Ok(js_type)
84}
85
86fn is_simple_type(js_type: &str) -> bool {
87 if js_type == "string" || js_type == "number" || js_type == "boolean" || js_type == "any" {
88 return true;
89 }
90 false
91}
92
93fn ty_name(ty: &syn::Type) -> String {
94 quote!(#ty).to_string()
95}
96
97