wai_bindgen_rust_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};
8
9#[proc_macro]
10pub fn import(input: TokenStream) -> TokenStream {
11 run(input, Direction::Import)
12}
13
14#[proc_macro]
15pub fn export(input: TokenStream) -> TokenStream {
16 run(input, Direction::Export)
17}
18
19fn run(input: TokenStream, dir: Direction) -> TokenStream {
20 let input = syn::parse_macro_input!(input as Opts);
21 let mut gen = input.opts.build();
22 let mut files = Files::default();
23 let (imports, exports) = match dir {
24 Direction::Import => (input.interfaces, vec![]),
25 Direction::Export => (vec![], input.interfaces),
26 };
27 gen.generate_all(&imports, &exports, &mut files);
28 let (_, contents) = files.iter().next().unwrap();
29 let mut contents = std::str::from_utf8(contents).unwrap().to_string();
30
31 let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap();
34 for file in input.files.iter() {
35 contents.push_str(&format!(
36 "const _: &str = include_str!(r#\"{}\"#);\n",
37 Path::new(&cwd).join(file).display()
38 ));
39 }
40
41 contents.parse().unwrap()
42}
43
44struct Opts {
45 opts: wai_bindgen_gen_rust_wasm::Opts,
46 interfaces: Vec<Interface>,
47 files: Vec<String>,
48}
49
50mod kw {
51 syn::custom_keyword!(src);
52 syn::custom_keyword!(paths);
53 syn::custom_keyword!(unchecked);
54 syn::custom_keyword!(multi_module);
55}
56
57impl Parse for Opts {
58 fn parse(input: ParseStream<'_>) -> Result<Opts> {
59 let mut opts = wai_bindgen_gen_rust_wasm::Opts::default();
60 let call_site = proc_macro2::Span::call_site();
61 let mut files = Vec::new();
62 let interfaces = if input.peek(token::Brace) {
63 let content;
64 syn::braced!(content in input);
65 let mut interfaces = Vec::new();
66 let fields = Punctuated::<ConfigField, Token![,]>::parse_terminated(&content)?;
67 for field in fields.into_pairs() {
68 match field.into_value() {
69 ConfigField::Unchecked => opts.unchecked = true,
70 ConfigField::MultiModule => opts.multi_module = true,
71 ConfigField::Interfaces(v) => interfaces = v,
72 }
73 }
74 if interfaces.is_empty() {
75 return Err(Error::new(
76 call_site,
77 "must either specify `src` or `paths` keys",
78 ));
79 }
80 interfaces
81 } else {
82 while !input.is_empty() {
83 let s = input.parse::<syn::LitStr>()?;
84 files.push(s.value());
85 }
86 let mut interfaces = Vec::new();
87 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
88 for path in files.iter() {
89 let path = manifest_dir.join(path);
90 let iface = Interface::parse_file(path).map_err(|e| Error::new(call_site, e))?;
91 interfaces.push(iface);
92 }
93 interfaces
94 };
95 Ok(Opts {
96 files,
97 opts,
98 interfaces,
99 })
100 }
101}
102
103enum ConfigField {
104 Interfaces(Vec<Interface>),
105 Unchecked,
106 MultiModule,
107}
108
109impl Parse for ConfigField {
110 fn parse(input: ParseStream<'_>) -> Result<Self> {
111 let l = input.lookahead1();
112 if l.peek(kw::src) {
113 input.parse::<kw::src>()?;
114 let name;
115 syn::bracketed!(name in input);
116 let name = name.parse::<syn::LitStr>()?;
117 input.parse::<Token![:]>()?;
118 let s = input.parse::<syn::LitStr>()?;
119 let interface =
120 Interface::parse(&name.value(), &s.value()).map_err(|e| Error::new(s.span(), e))?;
121 Ok(ConfigField::Interfaces(vec![interface]))
122 } else if l.peek(kw::paths) {
123 input.parse::<kw::paths>()?;
124 input.parse::<Token![:]>()?;
125 let paths;
126 let bracket = syn::bracketed!(paths in input);
127 let paths = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&paths)?;
128 let values = paths.iter().map(|s| s.value()).collect::<Vec<_>>();
129 let mut interfaces = Vec::new();
130 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
131 for value in &values {
132 let value = manifest_dir.join(value);
133 let interface =
134 Interface::parse_file(value).map_err(|e| Error::new(bracket.span, e))?;
135 interfaces.push(interface);
136 }
137 Ok(ConfigField::Interfaces(interfaces))
138 } else if l.peek(kw::unchecked) {
139 input.parse::<kw::unchecked>()?;
140 Ok(ConfigField::Unchecked)
141 } else if l.peek(kw::multi_module) {
142 input.parse::<kw::multi_module>()?;
143 Ok(ConfigField::MultiModule)
144 } else {
145 Err(l.error())
146 }
147 }
148}