linera_wit_bindgen_guest_rust_macro/
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 wit_bindgen_core::{wit_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: wit_bindgen_gen_guest_rust::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 syn::custom_keyword!(export_macro);
56 syn::custom_keyword!(types_path);
57 syn::custom_keyword!(reexported_crate_path);
58}
59
60impl Parse for Opts {
61 fn parse(input: ParseStream<'_>) -> Result<Opts> {
62 let mut opts = wit_bindgen_gen_guest_rust::Opts::default();
63 let call_site = proc_macro2::Span::call_site();
64 let mut files = Vec::new();
65 let interfaces = if input.peek(token::Brace) {
66 let content;
67 syn::braced!(content in input);
68 let mut interfaces = Vec::new();
69 let fields = Punctuated::<ConfigField, Token![,]>::parse_terminated(&content)?;
70 for field in fields.into_pairs() {
71 match field.into_value() {
72 ConfigField::Unchecked => opts.unchecked = true,
73 ConfigField::MultiModule => opts.multi_module = true,
74 ConfigField::ExportMacro(name) => opts.export_macro = Some(name),
75 ConfigField::TypesPath(path) => opts.types_path = Some(path),
76 ConfigField::ReexportedCratePath(path) => {
77 opts.reexported_crate_path = Some(path)
78 }
79 ConfigField::Interfaces(v) => interfaces = v,
80 }
81 }
82 if interfaces.is_empty() {
83 return Err(Error::new(
84 call_site,
85 "must either specify `src` or `paths` keys",
86 ));
87 }
88 interfaces
89 } else {
90 while !input.is_empty() {
91 if input.peek(kw::export_macro) {
92 input.parse::<kw::export_macro>()?;
93 input.parse::<token::Eq>()?;
94 let macro_name = input.parse::<syn::LitStr>()?.value();
95 opts.export_macro = Some(macro_name);
96 } else if input.peek(kw::types_path) {
97 input.parse::<kw::types_path>()?;
98 input.parse::<token::Eq>()?;
99 let path = input.parse::<syn::LitStr>()?.value();
100 opts.types_path = Some(path);
101 } else if input.peek(kw::reexported_crate_path) {
102 input.parse::<kw::reexported_crate_path>()?;
103 input.parse::<token::Eq>()?;
104 let path = input.parse::<syn::LitStr>()?.value();
105 opts.reexported_crate_path = Some(path);
106 } else {
107 let s = input.parse::<syn::LitStr>()?;
108 files.push(s.value());
109 }
110 }
111 let mut interfaces = Vec::new();
112 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
113 for path in files.iter() {
114 let path = manifest_dir.join(path);
115 let iface = Interface::parse_file(path).map_err(|e| Error::new(call_site, e))?;
116 interfaces.push(iface);
117 }
118 interfaces
119 };
120 Ok(Opts {
121 files,
122 opts,
123 interfaces,
124 })
125 }
126}
127
128enum ConfigField {
129 Interfaces(Vec<Interface>),
130 Unchecked,
131 MultiModule,
132 ExportMacro(String),
133 TypesPath(String),
134 ReexportedCratePath(String),
135}
136
137impl Parse for ConfigField {
138 fn parse(input: ParseStream<'_>) -> Result<Self> {
139 let l = input.lookahead1();
140 if l.peek(kw::src) {
141 input.parse::<kw::src>()?;
142 let name;
143 syn::bracketed!(name in input);
144 let name = name.parse::<syn::LitStr>()?;
145 input.parse::<Token![:]>()?;
146 let s = input.parse::<syn::LitStr>()?;
147 let interface =
148 Interface::parse(&name.value(), &s.value()).map_err(|e| Error::new(s.span(), e))?;
149 Ok(ConfigField::Interfaces(vec![interface]))
150 } else if l.peek(kw::paths) {
151 input.parse::<kw::paths>()?;
152 input.parse::<Token![:]>()?;
153 let paths;
154 let bracket = syn::bracketed!(paths in input);
155 let paths = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&paths)?;
156 let values = paths.iter().map(|s| s.value()).collect::<Vec<_>>();
157 let mut interfaces = Vec::new();
158 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
159 for value in &values {
160 let value = manifest_dir.join(value);
161 let interface =
162 Interface::parse_file(value).map_err(|e| Error::new(bracket.span, e))?;
163 interfaces.push(interface);
164 }
165 Ok(ConfigField::Interfaces(interfaces))
166 } else if l.peek(kw::unchecked) {
167 input.parse::<kw::unchecked>()?;
168 Ok(ConfigField::Unchecked)
169 } else if l.peek(kw::multi_module) {
170 input.parse::<kw::multi_module>()?;
171 Ok(ConfigField::MultiModule)
172 } else if l.peek(kw::export_macro) {
173 input.parse::<kw::export_macro>()?;
174 input.parse::<token::Eq>()?;
175 let macro_name = input.parse::<syn::LitStr>()?.value();
176 Ok(ConfigField::ExportMacro(macro_name))
177 } else if l.peek(kw::types_path) {
178 input.parse::<kw::types_path>()?;
179 input.parse::<token::Eq>()?;
180 let path = input.parse::<syn::LitStr>()?.value();
181 Ok(ConfigField::TypesPath(path))
182 } else if l.peek(kw::reexported_crate_path) {
183 input.parse::<kw::reexported_crate_path>()?;
184 input.parse::<token::Eq>()?;
185 let path = input.parse::<syn::LitStr>()?.value();
186 Ok(ConfigField::ReexportedCratePath(path))
187 } else {
188 Err(l.error())
189 }
190 }
191}