wai_bindgen_wasmer_impl/
lib.rs1use std::path::{Path, PathBuf};
2
3use proc_macro::TokenStream;
4use syn::parse::{Error, Parse, ParseStream, Result};
5use syn::punctuated::Punctuated;
6use syn::{token, Token};
7use wai_bindgen_gen_core::{wai_parser::Interface, Direction, Files, Generator};
8use wai_bindgen_gen_wasmer::Async;
9
10#[proc_macro]
13pub fn import(input: TokenStream) -> TokenStream {
14 run(input, Direction::Import)
15}
16
17#[proc_macro]
20pub fn export(input: TokenStream) -> TokenStream {
21 run(input, Direction::Export)
22}
23
24fn run(input: TokenStream, dir: Direction) -> TokenStream {
25 let input = syn::parse_macro_input!(input as Opts);
26 let mut gen = input.opts.build();
27 let mut files = Files::default();
28 let (imports, exports) = match dir {
29 Direction::Import => (input.interfaces, vec![]),
30 Direction::Export => (vec![], input.interfaces),
31 };
32 gen.generate_all(&imports, &exports, &mut files);
33
34 let (_, contents) = files.iter().next().unwrap();
35
36 let contents = std::str::from_utf8(contents).unwrap();
37 let mut contents = contents.parse::<TokenStream>().unwrap();
38
39 let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap();
42 for file in input.files.iter() {
43 contents.extend(
44 format!(
45 "const _: &str = include_str!(r#\"{}\"#);\n",
46 Path::new(&cwd).join(file).display()
47 )
48 .parse::<TokenStream>()
49 .unwrap(),
50 );
51 }
52
53 contents
54}
55
56struct Opts {
57 opts: wai_bindgen_gen_wasmer::Opts,
58 interfaces: Vec<Interface>,
59 files: Vec<String>,
60}
61
62mod kw {
63 syn::custom_keyword!(src);
64 syn::custom_keyword!(paths);
65 syn::custom_keyword!(custom_error);
66}
67
68impl Parse for Opts {
69 fn parse(input: ParseStream<'_>) -> Result<Opts> {
70 let call_site = proc_macro2::Span::call_site();
71 let mut opts = wai_bindgen_gen_wasmer::Opts::default();
72 let mut files = Vec::new();
73 opts.tracing = cfg!(feature = "tracing");
74
75 let interfaces = if input.peek(token::Brace) {
76 let content;
77 syn::braced!(content in input);
78 let mut interfaces = Vec::new();
79 let fields = Punctuated::<ConfigField, Token![,]>::parse_terminated(&content)?;
80 for field in fields.into_pairs() {
81 match field.into_value() {
82 ConfigField::Interfaces(v) => interfaces = v,
83 ConfigField::Async(v) => opts.async_ = v,
84 ConfigField::CustomError(v) => opts.custom_error = v,
85 }
86 }
87 if interfaces.is_empty() {
88 return Err(Error::new(
89 call_site,
90 "must either specify `src` or `paths` keys",
91 ));
92 }
93 interfaces
94 } else {
95 while !input.is_empty() {
96 let s = input.parse::<syn::LitStr>()?;
97 files.push(s.value());
98 }
99 let mut interfaces = Vec::new();
100 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
101 for path in files.iter() {
102 let path = manifest_dir.join(path);
103 let iface = Interface::parse_file(path).map_err(|e| Error::new(call_site, e))?;
104 interfaces.push(iface);
105 }
106 interfaces
107 };
108 Ok(Opts {
109 opts,
110 interfaces,
111 files,
112 })
113 }
114}
115
116enum ConfigField {
117 Interfaces(Vec<Interface>),
118 Async(wai_bindgen_gen_wasmer::Async),
119 CustomError(bool),
120}
121
122impl Parse for ConfigField {
123 fn parse(input: ParseStream<'_>) -> Result<Self> {
124 let l = input.lookahead1();
125 if l.peek(kw::src) {
126 input.parse::<kw::src>()?;
127 let name;
128 syn::bracketed!(name in input);
129 let name = name.parse::<syn::LitStr>()?;
130 input.parse::<Token![:]>()?;
131 let s = input.parse::<syn::LitStr>()?;
132 let interface =
133 Interface::parse(&name.value(), &s.value()).map_err(|e| Error::new(s.span(), e))?;
134 Ok(ConfigField::Interfaces(vec![interface]))
135 } else if l.peek(kw::paths) {
136 input.parse::<kw::paths>()?;
137 input.parse::<Token![:]>()?;
138 let paths;
139 let bracket = syn::bracketed!(paths in input);
140 let paths = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&paths)?;
141 let values = paths.iter().map(|s| s.value()).collect::<Vec<_>>();
142 let mut interfaces = Vec::new();
143 for value in &values {
144 let interface =
145 Interface::parse_file(value).map_err(|e| Error::new(bracket.span, e))?;
146 interfaces.push(interface);
147 }
148 Ok(ConfigField::Interfaces(interfaces))
149 } else if l.peek(token::Async) {
150 if !cfg!(feature = "async") {
151 return Err(
152 input.error("async support not enabled in the `wai-bindgen-wasmer` crate")
153 );
154 }
155 input.parse::<token::Async>()?;
156 input.parse::<Token![:]>()?;
157 let val = if input.parse::<Option<Token![*]>>()?.is_some() {
158 Async::All
159 } else {
160 let names;
161 syn::bracketed!(names in input);
162 let paths = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&names)?;
163 let values = paths.iter().map(|s| s.value()).collect();
164 Async::Only(values)
165 };
166 Ok(ConfigField::Async(val))
167 } else if l.peek(kw::custom_error) {
168 input.parse::<kw::custom_error>()?;
169 input.parse::<Token![:]>()?;
170 Ok(ConfigField::CustomError(
171 input.parse::<syn::LitBool>()?.value,
172 ))
173 } else {
174 Err(l.error())
175 }
176 }
177}