1use proc_macro2::{Span, TokenStream};
2use quote::ToTokens;
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
6use syn::parse::{Error, Parse, ParseStream, Result};
7use syn::punctuated::Punctuated;
8use syn::spanned::Spanned;
9use syn::{braced, token, LitStr, Token};
10use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId};
11use wit_bindgen_rust::{AsyncConfig, Opts, Ownership, WithOption};
12
13#[proc_macro]
14pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15 syn::parse_macro_input!(input as Config)
16 .expand()
17 .unwrap_or_else(Error::into_compile_error)
18 .into()
19}
20
21fn anyhow_to_syn(span: Span, err: anyhow::Error) -> Error {
22 let err = attach_with_context(err);
23 let mut msg = err.to_string();
24 for cause in err.chain().skip(1) {
25 msg.push_str(&format!("\n\nCaused by:\n {cause}"));
26 }
27 Error::new(span, msg)
28}
29
30fn attach_with_context(err: anyhow::Error) -> anyhow::Error {
31 if let Some(e) = err.downcast_ref::<wit_bindgen_rust::MissingWith>() {
32 let option = e.0.clone();
33 return err.context(format!(
34 "missing one of:\n\
35 * `generate_all` option\n\
36 * `with: {{ \"{option}\": path::to::bindings, }}`\n\
37 * `with: {{ \"{option}\": generate, }}`\
38 "
39 ));
40 }
41 err
42}
43
44struct Config {
45 opts: Opts,
46 resolve: Resolve,
47 world: WorldId,
48 files: Vec<PathBuf>,
49 debug: bool,
50}
51
52enum Source {
54 Paths(Vec<PathBuf>),
56 Inline(String, Option<Vec<PathBuf>>),
58}
59
60impl Parse for Config {
61 fn parse(input: ParseStream<'_>) -> Result<Self> {
62 let call_site = Span::call_site();
63 let mut opts = Opts::default();
64 let mut world = None;
65 let mut source = None;
66 let mut features = Vec::new();
67 let mut async_configured = false;
68 let mut debug = false;
69
70 if input.peek(token::Brace) {
71 let content;
72 syn::braced!(content in input);
73 let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
74 for field in fields.into_pairs() {
75 match field.into_value() {
76 Opt::Path(span, p) => {
77 let paths = p.into_iter().map(|f| PathBuf::from(f.value())).collect();
78
79 source = Some(match source {
80 Some(Source::Paths(_)) | Some(Source::Inline(_, Some(_))) => {
81 return Err(Error::new(span, "cannot specify second source"));
82 }
83 Some(Source::Inline(i, None)) => Source::Inline(i, Some(paths)),
84 None => Source::Paths(paths),
85 })
86 }
87 Opt::World(s) => {
88 if world.is_some() {
89 return Err(Error::new(s.span(), "cannot specify second world"));
90 }
91 world = Some(s.value());
92 }
93 Opt::Inline(s) => {
94 source = Some(match source {
95 Some(Source::Inline(_, _)) => {
96 return Err(Error::new(s.span(), "cannot specify second source"));
97 }
98 Some(Source::Paths(p)) => Source::Inline(s.value(), Some(p)),
99 None => Source::Inline(s.value(), None),
100 })
101 }
102 Opt::UseStdFeature => opts.std_feature = true,
103 Opt::RawStrings => opts.raw_strings = true,
104 Opt::Ownership(ownership) => opts.ownership = ownership,
105 Opt::Skip(list) => opts.skip.extend(list.iter().map(|i| i.value())),
106 Opt::RuntimePath(path) => opts.runtime_path = Some(path.value()),
107 Opt::BitflagsPath(path) => opts.bitflags_path = Some(path.value()),
108 Opt::Stubs => {
109 opts.stubs = true;
110 }
111 Opt::ExportPrefix(prefix) => opts.export_prefix = Some(prefix.value()),
112 Opt::AdditionalDerives(paths) => {
113 opts.additional_derive_attributes = paths
114 .into_iter()
115 .map(|p| p.into_token_stream().to_string())
116 .collect()
117 }
118 Opt::AdditionalDerivesIgnore(list) => {
119 opts.additional_derive_ignore =
120 list.into_iter().map(|i| i.value()).collect()
121 }
122 Opt::With(with) => opts.with.extend(with),
123 Opt::GenerateAll => {
124 opts.generate_all = true;
125 }
126 Opt::TypeSectionSuffix(suffix) => {
127 opts.type_section_suffix = Some(suffix.value());
128 }
129 Opt::DisableRunCtorsOnceWorkaround(enable) => {
130 opts.disable_run_ctors_once_workaround = enable.value();
131 }
132 Opt::DefaultBindingsModule(enable) => {
133 opts.default_bindings_module = Some(enable.value());
134 }
135 Opt::ExportMacroName(name) => {
136 opts.export_macro_name = Some(name.value());
137 }
138 Opt::PubExportMacro(enable) => {
139 opts.pub_export_macro = enable.value();
140 }
141 Opt::GenerateUnusedTypes(enable) => {
142 opts.generate_unused_types = enable.value();
143 }
144 Opt::Features(f) => {
145 features.extend(f.into_iter().map(|f| f.value()));
146 }
147 Opt::DisableCustomSectionLinkHelpers(disable) => {
148 opts.disable_custom_section_link_helpers = disable.value();
149 }
150 Opt::Debug(enable) => {
151 debug = enable.value();
152 }
153 Opt::Async(val, span) => {
154 if async_configured {
155 return Err(Error::new(span, "cannot specify second async config"));
156 }
157 async_configured = true;
158 if !matches!(val, AsyncConfig::None) && !cfg!(feature = "async") {
159 return Err(Error::new(
160 span,
161 "must enable `async` feature to enable async imports and/or exports",
162 ));
163 }
164 opts.async_ = val;
165 }
166 }
167 }
168 } else {
169 world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
170 if input.parse::<Option<syn::token::In>>()?.is_some() {
171 source = Some(Source::Paths(vec![PathBuf::from(
172 input.parse::<syn::LitStr>()?.value(),
173 )]));
174 }
175 }
176 let (resolve, pkgs, files) =
177 parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?;
178 let world = select_world(&resolve, &pkgs, world.as_deref())
179 .map_err(|e| anyhow_to_syn(call_site, e))?;
180 Ok(Config {
181 opts,
182 resolve,
183 world,
184 files,
185 debug,
186 })
187 }
188}
189
190fn select_world(
191 resolve: &Resolve,
192 pkgs: &[PackageId],
193 world: Option<&str>,
194) -> anyhow::Result<WorldId> {
195 if pkgs.len() == 1 {
196 resolve.select_world(pkgs[0], world)
197 } else {
198 assert!(!pkgs.is_empty());
199 match world {
200 Some(name) => {
201 if !name.contains(":") {
202 anyhow::bail!(
203 "with multiple packages a fully qualified \
204 world name must be specified"
205 )
206 }
207
208 resolve.select_world(pkgs[0], world)
211 }
212 None => {
213 let worlds = pkgs
214 .iter()
215 .filter_map(|p| resolve.select_world(*p, None).ok())
216 .collect::<Vec<_>>();
217 match &worlds[..] {
218 [] => anyhow::bail!("no packages have a world"),
219 [world] => Ok(*world),
220 _ => anyhow::bail!("multiple packages have a world, must specify which to use"),
221 }
222 }
223 }
224 }
225}
226
227fn parse_source(
229 source: &Option<Source>,
230 features: &[String],
231) -> anyhow::Result<(Resolve, Vec<PackageId>, Vec<PathBuf>)> {
232 let mut resolve = Resolve::default();
233 resolve.features.extend(features.iter().cloned());
234 let mut files = Vec::new();
235 let mut pkgs = Vec::new();
236 let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
237 let mut parse = |paths: &[PathBuf]| -> anyhow::Result<()> {
238 for path in paths {
239 let p = root.join(path);
240 let normalized_path = match std::fs::canonicalize(&p) {
244 Ok(p) => p,
245 Err(_) => p.to_path_buf(),
246 };
247 let (pkg, sources) = resolve.push_path(normalized_path)?;
248 pkgs.push(pkg);
249 files.extend(sources.paths().map(|p| p.to_owned()));
250 }
251 Ok(())
252 };
253 match source {
254 Some(Source::Inline(s, path)) => {
255 if let Some(p) = path {
256 parse(p)?;
257 }
258 pkgs.push(resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?);
259 }
260 Some(Source::Paths(p)) => parse(p)?,
261 None => parse(&vec![root.join("wit")])?,
262 };
263
264 Ok((resolve, pkgs, files))
265}
266
267impl Config {
268 fn expand(self) -> Result<TokenStream> {
269 let mut files = Default::default();
270 let mut generator = self.opts.build();
271 generator
272 .generate(&self.resolve, self.world, &mut files)
273 .map_err(|e| anyhow_to_syn(Span::call_site(), e))?;
274 let (_, src) = files.iter().next().unwrap();
275 let mut src = std::str::from_utf8(src).unwrap().to_string();
276
277 if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug {
282 static INVOCATION: AtomicUsize = AtomicUsize::new(0);
283 let root = Path::new(env!("DEBUG_OUTPUT_DIR"));
284 let world_name = &self.resolve.worlds[self.world].name;
285 let n = INVOCATION.fetch_add(1, Relaxed);
286 let path = root.join(format!("{world_name}{n}.rs"));
287
288 let contents = match fmt(&src) {
290 Ok(formatted) => formatted,
291 Err(_) => src.clone(),
292 };
293 std::fs::write(&path, contents.as_bytes()).unwrap();
294
295 src = format!("include!({path:?});");
296 }
297 let mut contents = src.parse::<TokenStream>().unwrap();
298
299 for file in self.files.iter() {
302 contents.extend(
303 format!(
304 "const _: &[u8] = include_bytes!(r#\"{}\"#);\n",
305 file.display()
306 )
307 .parse::<TokenStream>()
308 .unwrap(),
309 );
310 }
311
312 Ok(contents)
313 }
314}
315
316mod kw {
317 syn::custom_keyword!(std_feature);
318 syn::custom_keyword!(raw_strings);
319 syn::custom_keyword!(skip);
320 syn::custom_keyword!(world);
321 syn::custom_keyword!(path);
322 syn::custom_keyword!(inline);
323 syn::custom_keyword!(ownership);
324 syn::custom_keyword!(runtime_path);
325 syn::custom_keyword!(bitflags_path);
326 syn::custom_keyword!(exports);
327 syn::custom_keyword!(stubs);
328 syn::custom_keyword!(export_prefix);
329 syn::custom_keyword!(additional_derives);
330 syn::custom_keyword!(additional_derives_ignore);
331 syn::custom_keyword!(with);
332 syn::custom_keyword!(generate_all);
333 syn::custom_keyword!(type_section_suffix);
334 syn::custom_keyword!(disable_run_ctors_once_workaround);
335 syn::custom_keyword!(default_bindings_module);
336 syn::custom_keyword!(export_macro_name);
337 syn::custom_keyword!(pub_export_macro);
338 syn::custom_keyword!(generate_unused_types);
339 syn::custom_keyword!(features);
340 syn::custom_keyword!(disable_custom_section_link_helpers);
341 syn::custom_keyword!(imports);
342 syn::custom_keyword!(debug);
343}
344
345#[derive(Clone)]
346enum ExportKey {
347 World,
348 Name(syn::LitStr),
349}
350
351impl Parse for ExportKey {
352 fn parse(input: ParseStream<'_>) -> Result<Self> {
353 let l = input.lookahead1();
354 Ok(if l.peek(kw::world) {
355 input.parse::<kw::world>()?;
356 Self::World
357 } else {
358 Self::Name(input.parse()?)
359 })
360 }
361}
362
363impl From<ExportKey> for wit_bindgen_rust::ExportKey {
364 fn from(key: ExportKey) -> Self {
365 match key {
366 ExportKey::World => Self::World,
367 ExportKey::Name(s) => Self::Name(s.value()),
368 }
369 }
370}
371
372enum AsyncConfigSomeKind {
373 Imports,
374 Exports,
375}
376
377enum Opt {
378 World(syn::LitStr),
379 Path(Span, Vec<syn::LitStr>),
380 Inline(syn::LitStr),
381 UseStdFeature,
382 RawStrings,
383 Skip(Vec<syn::LitStr>),
384 Ownership(Ownership),
385 RuntimePath(syn::LitStr),
386 BitflagsPath(syn::LitStr),
387 Stubs,
388 ExportPrefix(syn::LitStr),
389 AdditionalDerives(Vec<syn::Path>),
391 AdditionalDerivesIgnore(Vec<syn::LitStr>),
392 With(HashMap<String, WithOption>),
393 GenerateAll,
394 TypeSectionSuffix(syn::LitStr),
395 DisableRunCtorsOnceWorkaround(syn::LitBool),
396 DefaultBindingsModule(syn::LitStr),
397 ExportMacroName(syn::LitStr),
398 PubExportMacro(syn::LitBool),
399 GenerateUnusedTypes(syn::LitBool),
400 Features(Vec<syn::LitStr>),
401 DisableCustomSectionLinkHelpers(syn::LitBool),
402 Async(AsyncConfig, Span),
403 Debug(syn::LitBool),
404}
405
406impl Parse for Opt {
407 fn parse(input: ParseStream<'_>) -> Result<Self> {
408 let l = input.lookahead1();
409 if l.peek(kw::path) {
410 input.parse::<kw::path>()?;
411 input.parse::<Token![:]>()?;
412 if input.peek(token::Bracket) {
416 let contents;
417 syn::bracketed!(contents in input);
418 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
419 Ok(Opt::Path(list.span(), list.into_iter().collect()))
420 } else {
421 let path: LitStr = input.parse()?;
422 Ok(Opt::Path(path.span(), vec![path]))
423 }
424 } else if l.peek(kw::inline) {
425 input.parse::<kw::inline>()?;
426 input.parse::<Token![:]>()?;
427 Ok(Opt::Inline(input.parse()?))
428 } else if l.peek(kw::world) {
429 input.parse::<kw::world>()?;
430 input.parse::<Token![:]>()?;
431 Ok(Opt::World(input.parse()?))
432 } else if l.peek(kw::std_feature) {
433 input.parse::<kw::std_feature>()?;
434 Ok(Opt::UseStdFeature)
435 } else if l.peek(kw::raw_strings) {
436 input.parse::<kw::raw_strings>()?;
437 Ok(Opt::RawStrings)
438 } else if l.peek(kw::ownership) {
439 input.parse::<kw::ownership>()?;
440 input.parse::<Token![:]>()?;
441 let ownership = input.parse::<syn::Ident>()?;
442 Ok(Opt::Ownership(match ownership.to_string().as_str() {
443 "Owning" => Ownership::Owning,
444 "Borrowing" => Ownership::Borrowing {
445 duplicate_if_necessary: {
446 let contents;
447 braced!(contents in input);
448 let field = contents.parse::<syn::Ident>()?;
449 match field.to_string().as_str() {
450 "duplicate_if_necessary" => {
451 contents.parse::<Token![:]>()?;
452 contents.parse::<syn::LitBool>()?.value
453 }
454 name => {
455 return Err(Error::new(
456 field.span(),
457 format!(
458 "unrecognized `Ownership::Borrowing` field: `{name}`; \
459 expected `duplicate_if_necessary`"
460 ),
461 ));
462 }
463 }
464 },
465 },
466 name => {
467 return Err(Error::new(
468 ownership.span(),
469 format!(
470 "unrecognized ownership: `{name}`; \
471 expected `Owning` or `Borrowing`"
472 ),
473 ));
474 }
475 }))
476 } else if l.peek(kw::skip) {
477 input.parse::<kw::skip>()?;
478 input.parse::<Token![:]>()?;
479 let contents;
480 syn::bracketed!(contents in input);
481 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
482 Ok(Opt::Skip(list.iter().cloned().collect()))
483 } else if l.peek(kw::runtime_path) {
484 input.parse::<kw::runtime_path>()?;
485 input.parse::<Token![:]>()?;
486 Ok(Opt::RuntimePath(input.parse()?))
487 } else if l.peek(kw::bitflags_path) {
488 input.parse::<kw::bitflags_path>()?;
489 input.parse::<Token![:]>()?;
490 Ok(Opt::BitflagsPath(input.parse()?))
491 } else if l.peek(kw::stubs) {
492 input.parse::<kw::stubs>()?;
493 Ok(Opt::Stubs)
494 } else if l.peek(kw::export_prefix) {
495 input.parse::<kw::export_prefix>()?;
496 input.parse::<Token![:]>()?;
497 Ok(Opt::ExportPrefix(input.parse()?))
498 } else if l.peek(kw::additional_derives) {
499 input.parse::<kw::additional_derives>()?;
500 input.parse::<Token![:]>()?;
501 let contents;
502 syn::bracketed!(contents in input);
503 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
504 Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
505 } else if l.peek(kw::additional_derives_ignore) {
506 input.parse::<kw::additional_derives_ignore>()?;
507 input.parse::<Token![:]>()?;
508 let contents;
509 syn::bracketed!(contents in input);
510 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
511 Ok(Opt::AdditionalDerivesIgnore(list.iter().cloned().collect()))
512 } else if l.peek(kw::with) {
513 input.parse::<kw::with>()?;
514 input.parse::<Token![:]>()?;
515 let contents;
516 let _lbrace = braced!(contents in input);
517 let fields: Punctuated<_, Token![,]> =
518 contents.parse_terminated(with_field_parse, Token![,])?;
519 Ok(Opt::With(HashMap::from_iter(fields.into_iter())))
520 } else if l.peek(kw::generate_all) {
521 input.parse::<kw::generate_all>()?;
522 Ok(Opt::GenerateAll)
523 } else if l.peek(kw::type_section_suffix) {
524 input.parse::<kw::type_section_suffix>()?;
525 input.parse::<Token![:]>()?;
526 Ok(Opt::TypeSectionSuffix(input.parse()?))
527 } else if l.peek(kw::disable_run_ctors_once_workaround) {
528 input.parse::<kw::disable_run_ctors_once_workaround>()?;
529 input.parse::<Token![:]>()?;
530 Ok(Opt::DisableRunCtorsOnceWorkaround(input.parse()?))
531 } else if l.peek(kw::default_bindings_module) {
532 input.parse::<kw::default_bindings_module>()?;
533 input.parse::<Token![:]>()?;
534 Ok(Opt::DefaultBindingsModule(input.parse()?))
535 } else if l.peek(kw::export_macro_name) {
536 input.parse::<kw::export_macro_name>()?;
537 input.parse::<Token![:]>()?;
538 Ok(Opt::ExportMacroName(input.parse()?))
539 } else if l.peek(kw::pub_export_macro) {
540 input.parse::<kw::pub_export_macro>()?;
541 input.parse::<Token![:]>()?;
542 Ok(Opt::PubExportMacro(input.parse()?))
543 } else if l.peek(kw::generate_unused_types) {
544 input.parse::<kw::generate_unused_types>()?;
545 input.parse::<Token![:]>()?;
546 Ok(Opt::GenerateUnusedTypes(input.parse()?))
547 } else if l.peek(kw::features) {
548 input.parse::<kw::features>()?;
549 input.parse::<Token![:]>()?;
550 let contents;
551 syn::bracketed!(contents in input);
552 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
553 Ok(Opt::Features(list.into_iter().collect()))
554 } else if l.peek(kw::disable_custom_section_link_helpers) {
555 input.parse::<kw::disable_custom_section_link_helpers>()?;
556 input.parse::<Token![:]>()?;
557 Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?))
558 } else if l.peek(kw::debug) {
559 input.parse::<kw::debug>()?;
560 input.parse::<Token![:]>()?;
561 Ok(Opt::Debug(input.parse()?))
562 } else if l.peek(Token![async]) {
563 let span = input.parse::<Token![async]>()?.span;
564 input.parse::<Token![:]>()?;
565 if input.peek(syn::LitBool) {
566 if input.parse::<syn::LitBool>()?.value {
567 Ok(Opt::Async(AsyncConfig::All, span))
568 } else {
569 Ok(Opt::Async(AsyncConfig::None, span))
570 }
571 } else {
572 let mut imports = Vec::new();
573 let mut exports = Vec::new();
574 let contents;
575 syn::braced!(contents in input);
576 for (kind, values) in
577 contents.parse_terminated(parse_async_some_field, Token![,])?
578 {
579 match kind {
580 AsyncConfigSomeKind::Imports => imports = values,
581 AsyncConfigSomeKind::Exports => exports = values,
582 }
583 }
584 Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span))
585 }
586 } else {
587 Err(l.error())
588 }
589 }
590}
591
592fn with_field_parse(input: ParseStream<'_>) -> Result<(String, WithOption)> {
593 let interface = input.parse::<syn::LitStr>()?.value();
594 input.parse::<Token![:]>()?;
595 let start = input.span();
596 let path = input.parse::<syn::Path>()?;
597
598 let span = start
600 .join(path.segments.last().unwrap().ident.span())
601 .unwrap_or(start);
602
603 if path.is_ident("generate") {
604 return Ok((interface, WithOption::Generate));
605 }
606
607 let mut buf = String::new();
608 let append = |buf: &mut String, segment: syn::PathSegment| -> Result<()> {
609 if !segment.arguments.is_none() {
610 return Err(Error::new(
611 span,
612 "Module path must not contain angles or parens",
613 ));
614 }
615
616 buf.push_str(&segment.ident.to_string());
617
618 Ok(())
619 };
620
621 if path.leading_colon.is_some() {
622 buf.push_str("::");
623 }
624
625 let mut segments = path.segments.into_iter();
626
627 if let Some(segment) = segments.next() {
628 append(&mut buf, segment)?;
629 }
630
631 for segment in segments {
632 buf.push_str("::");
633 append(&mut buf, segment)?;
634 }
635
636 Ok((interface, WithOption::Path(buf)))
637}
638
639fn fmt(input: &str) -> Result<String> {
641 let syntax_tree = syn::parse_file(&input)?;
642 Ok(prettyplease::unparse(&syntax_tree))
643}
644
645fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec<String>)> {
646 let lookahead = input.lookahead1();
647 let kind = if lookahead.peek(kw::imports) {
648 input.parse::<kw::imports>()?;
649 input.parse::<Token![:]>()?;
650 AsyncConfigSomeKind::Imports
651 } else if lookahead.peek(kw::exports) {
652 input.parse::<kw::exports>()?;
653 input.parse::<Token![:]>()?;
654 AsyncConfigSomeKind::Exports
655 } else {
656 return Err(lookahead.error());
657 };
658
659 let list;
660 syn::bracketed!(list in input);
661 let fields = list.parse_terminated(Parse::parse, Token![,])?;
662
663 Ok((
664 kind,
665 fields.iter().map(|s: &syn::LitStr| s.value()).collect(),
666 ))
667}