wasm_bindgen_macro_support/
lib.rs1#![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro-support/0.2")]
4
5#[macro_use]
6mod error;
7
8mod ast;
9mod codegen;
10mod encode;
11mod generics;
12mod hash;
13mod parser;
14
15use codegen::TryToTokens;
16use error::Diagnostic;
17pub use parser::BindgenAttrs;
18use parser::{ConvertToAst, MacroParse};
19use proc_macro2::TokenStream;
20use quote::quote;
21use quote::ToTokens;
22use quote::TokenStreamExt;
23use syn::parse::{Parse, ParseStream, Result as SynResult};
24use syn::Token;
25
26pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
28 parser::reset_attrs_used();
29 let item = syn::parse2::<syn::Item>(input)?;
32 if let syn::Item::Struct(mut s) = item {
33 let opts: BindgenAttrs = syn::parse2(attr.clone())?;
34 let wasm_bindgen = opts
35 .wasm_bindgen()
36 .cloned()
37 .unwrap_or_else(|| syn::parse_quote! { ::wasm_bindgen });
38
39 let extends_path = opts.attrs.iter().find_map(|(_, a)| match a {
43 parser::BindgenAttr::Extends(_, path) => Some(path.clone()),
44 _ => None,
45 });
46 parser::inject_parent_field(&mut s, extends_path.as_ref(), &wasm_bindgen)?;
47
48 let item = quote! {
49 #[derive(#wasm_bindgen::__rt::BindgenedStruct)]
50 #[wasm_bindgen(#attr)]
51 #s
52 };
53 return Ok(item);
54 }
55
56 let opts = syn::parse2(attr)?;
57 let mut tokens = proc_macro2::TokenStream::new();
58 let mut program = ast::Program::default();
59 item.macro_parse(&mut program, (Some(opts), &mut tokens))?;
60 program.try_to_tokens(&mut tokens)?;
61
62 parser::check_unused_attrs(&mut tokens);
66
67 Ok(tokens)
68}
69
70pub fn expand_link_to(input: TokenStream) -> Result<TokenStream, Diagnostic> {
72 parser::reset_attrs_used();
73 let opts = syn::parse2(input)?;
74
75 let mut tokens = proc_macro2::TokenStream::new();
76 let link = parser::link_to(opts)?;
77 link.try_to_tokens(&mut tokens)?;
78
79 Ok(tokens)
80}
81
82pub fn expand_class_marker(
84 attr: TokenStream,
85 input: TokenStream,
86) -> Result<TokenStream, Diagnostic> {
87 parser::reset_attrs_used();
88 let mut item = syn::parse2::<syn::ImplItemFn>(input)?;
89 let opts: ClassMarker = syn::parse2(attr)?;
90
91 let mut program = ast::Program::default();
92 item.macro_parse(&mut program, &opts)?;
93
94 let mut tokens = proc_macro2::TokenStream::new();
106 tokens.append_all(
107 item.attrs
108 .iter()
109 .filter(|attr| matches!(attr.style, syn::AttrStyle::Outer)),
110 );
111 item.vis.to_tokens(&mut tokens);
112 item.sig.to_tokens(&mut tokens);
113 let mut err = None;
114 item.block.brace_token.surround(&mut tokens, |tokens| {
115 if let Err(e) = program.try_to_tokens(tokens) {
116 err = Some(e);
117 }
118 parser::check_unused_attrs(tokens); tokens.append_all(
120 item.attrs
121 .iter()
122 .filter(|attr| matches!(attr.style, syn::AttrStyle::Inner(_))),
123 );
124 tokens.append_all(&item.block.stmts);
125 });
126
127 if let Some(err) = err {
128 return Err(err);
129 }
130
131 Ok(tokens)
132}
133
134struct ClassMarker {
135 class: syn::Ident,
136 js_class: String,
137 js_namespace: Option<Vec<String>>,
138 wasm_bindgen: syn::Path,
139 wasm_bindgen_futures: syn::Path,
140 js_sys: syn::Path,
141}
142
143impl Parse for ClassMarker {
144 fn parse(input: ParseStream) -> SynResult<Self> {
145 let class = input.parse::<syn::Ident>()?;
146 input.parse::<Token![=]>()?;
147 let mut js_class = input.parse::<syn::LitStr>()?.value();
148 js_class = js_class
149 .strip_prefix("r#")
150 .map(String::from)
151 .unwrap_or(js_class);
152
153 let mut js_namespace: Option<Vec<String>> = None;
154 let mut wasm_bindgen = None;
155 let mut wasm_bindgen_futures = None;
156 let mut js_sys = None;
157
158 loop {
159 if input.parse::<Option<Token![,]>>()?.is_some() {
160 let ident = input.parse::<syn::Ident>()?;
161
162 if ident == "js_namespace" {
163 if js_namespace.is_some() {
164 return Err(syn::Error::new(
165 ident.span(),
166 "found duplicate `js_namespace`",
167 ));
168 }
169 input.parse::<Token![=]>()?;
170 let content;
171 syn::bracketed!(content in input);
172 let segs: syn::punctuated::Punctuated<syn::LitStr, Token![,]> = content
173 .parse_terminated(|p: ParseStream| p.parse::<syn::LitStr>(), Token![,])?;
174 js_namespace = Some(segs.into_iter().map(|s| s.value()).collect());
175 } else if ident == "wasm_bindgen" {
176 if wasm_bindgen.is_some() {
177 return Err(syn::Error::new(
178 ident.span(),
179 "found duplicate `wasm_bindgen`",
180 ));
181 }
182
183 input.parse::<Token![=]>()?;
184 wasm_bindgen = Some(input.parse::<syn::Path>()?);
185 } else if ident == "wasm_bindgen_futures" {
186 if wasm_bindgen_futures.is_some() {
187 return Err(syn::Error::new(
188 ident.span(),
189 "found duplicate `wasm_bindgen_futures`",
190 ));
191 }
192
193 input.parse::<Token![=]>()?;
194 wasm_bindgen_futures = Some(input.parse::<syn::Path>()?);
195 } else if ident == "js_sys" {
196 if js_sys.is_some() {
197 return Err(syn::Error::new(ident.span(), "found duplicate `js_sys`"));
198 }
199
200 input.parse::<Token![=]>()?;
201 js_sys = Some(input.parse::<syn::Path>()?);
202 } else {
203 return Err(syn::Error::new(
204 ident.span(),
205 "expected `js_namespace`, `wasm_bindgen`, `wasm_bindgen_futures`, or `js_sys`",
206 ));
207 }
208 } else {
209 break;
210 }
211 }
212
213 Ok(ClassMarker {
214 class,
215 js_class,
216 js_namespace,
217 wasm_bindgen: wasm_bindgen.unwrap_or_else(|| syn::parse_quote! { wasm_bindgen }),
218 wasm_bindgen_futures: wasm_bindgen_futures
219 .unwrap_or_else(|| syn::parse_quote! { wasm_bindgen_futures }),
220 js_sys: js_sys.unwrap_or_else(|| syn::parse_quote! { js_sys }),
221 })
222 }
223}
224
225pub fn expand_struct_marker(item: TokenStream) -> Result<TokenStream, Diagnostic> {
226 parser::reset_attrs_used();
227
228 let mut s: syn::ItemStruct = syn::parse2(item)?;
229
230 let mut program = ast::Program::default();
231 program.structs.push((&mut s).convert(&program)?);
232
233 let mut tokens = proc_macro2::TokenStream::new();
234 program.try_to_tokens(&mut tokens)?;
235
236 parser::check_unused_attrs(&mut tokens);
237
238 Ok(tokens)
239}