wasm_interfacegen_macro/
lib.rs

1use 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        // return Err(syn::Error::new(ty.span(), format!("Unknown type, {}; only string, number, and boolean js types are currently supported", name)));
81    }
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// Path(TypePath { qself: None, path: Path { leading_colon: None, segments: [PathSegment { ident: Ident { ident: "String", span: #0 bytes(141..147) }, arguments: None }] } })
98
99// fn ty_inner_type<'a>(wrapper: &str, ty: &'a syn::Type) -> Option<&'a syn::Type> {
100//     if let syn::Type::Path(ref p) = ty {
101//         if p.path.segments.len() != 1 || p.path.segments[0].ident != wrapper {
102//             return None;
103//         }
104
105//         if let syn::PathArguments::AngleBracketed(ref inner_ty) = p.path.segments[0].arguments {
106//             if inner_ty.args.len() != 1 {
107//                 return None;
108//             }
109
110//             let inner_ty = inner_ty.args.first().unwrap();
111//             if let syn::GenericArgument::Type(ref t) = inner_ty.value() {
112//                 return Some(t);
113//             }
114//         }
115//     }
116//     None
117// }