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