Skip to main content

wit_bindgen_rust_macro/
lib.rs

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::WorldGenerator;
10use wit_bindgen_core::wit_parser::{PackageId, Resolve, WorldId};
11use wit_bindgen_core::{AsyncFilterSet, ChainableMethodFilterSet};
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 render_error(
23    err: &(dyn std::error::Error + 'static),
24    sourcemap: &Option<&wit_bindgen_core::wit_parser::SourceMap>,
25) -> String {
26    if let Some(sourcemap) = sourcemap {
27        if let Some(e) = err.downcast_ref::<wit_bindgen_core::wit_parser::ParseError>() {
28            return e.render(sourcemap);
29        }
30        if let Some(e) = err.downcast_ref::<wit_bindgen_core::wit_parser::ResolveError>() {
31            return e.render(sourcemap);
32        }
33    }
34
35    err.to_string()
36}
37
38fn anyhow_to_syn(
39    span: Span,
40    err: anyhow::Error,
41    sourcemap: Option<&wit_bindgen_core::wit_parser::SourceMap>,
42) -> Error {
43    let err = attach_with_context(err);
44    let mut msg = render_error(err.as_ref(), &sourcemap);
45    for cause in err.chain().skip(1) {
46        let cause = render_error(cause, &sourcemap);
47        msg.push_str(&format!("\n\nCaused by:\n  {cause}"));
48    }
49    Error::new(span, msg)
50}
51
52fn attach_with_context(err: anyhow::Error) -> anyhow::Error {
53    if let Some(e) = err.downcast_ref::<wit_bindgen_rust::MissingWith>() {
54        let option = e.0.clone();
55        return err.context(format!(
56            "missing one of:\n\
57            * `generate_all` option\n\
58            * `with: {{ \"{option}\": path::to::bindings, }}`\n\
59            * `with: {{ \"{option}\": generate, }}`\
60            "
61        ));
62    }
63    err
64}
65
66struct Config {
67    opts: Opts,
68    resolve: Resolve,
69    world: WorldId,
70    files: Vec<PathBuf>,
71    debug: bool,
72}
73
74/// The source of the wit package definition
75enum Source {
76    /// A list of paths to wit directories
77    Paths(Vec<PathBuf>),
78    /// Inline sources have an optional path to a directory of their dependencies
79    Inline(String, Option<Vec<PathBuf>>),
80}
81
82impl Parse for Config {
83    fn parse(input: ParseStream<'_>) -> Result<Self> {
84        let call_site = Span::call_site();
85        let mut opts = Opts::default();
86        let mut world = None;
87        let mut source = None;
88        let mut features = Vec::new();
89        let mut sync_configured = false;
90        let mut method_chaining_configured = false;
91        let mut debug = false;
92
93        if input.peek(token::Brace) {
94            let content;
95            syn::braced!(content in input);
96            let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
97            for field in fields.into_pairs() {
98                match field.into_value() {
99                    Opt::Path(span, p) => {
100                        let paths = p
101                            .into_iter()
102                            .map(|f| f.evaluate_string())
103                            .collect::<Result<Vec<_>>>()?
104                            .into_iter()
105                            .map(PathBuf::from)
106                            .collect();
107
108                        source = Some(match source {
109                            Some(Source::Paths(_)) | Some(Source::Inline(_, Some(_))) => {
110                                return Err(Error::new(span, "cannot specify second source"));
111                            }
112                            Some(Source::Inline(i, None)) => Source::Inline(i, Some(paths)),
113                            None => Source::Paths(paths),
114                        })
115                    }
116                    Opt::World(s) => {
117                        if world.is_some() {
118                            return Err(Error::new(s.span(), "cannot specify second world"));
119                        }
120                        world = Some(s.value());
121                    }
122                    Opt::Inline(s) => {
123                        source = Some(match source {
124                            Some(Source::Inline(_, _)) => {
125                                return Err(Error::new(s.span(), "cannot specify second source"));
126                            }
127                            Some(Source::Paths(p)) => Source::Inline(s.value(), Some(p)),
128                            None => Source::Inline(s.value(), None),
129                        })
130                    }
131                    Opt::UseStdFeature => opts.std_feature = true,
132                    Opt::RawStrings => opts.raw_strings = true,
133                    Opt::Ownership(ownership) => opts.ownership = ownership,
134                    Opt::Skip(list) => opts.skip.extend(list.iter().map(|i| i.value())),
135                    Opt::RuntimePath(path) => opts.runtime_path = Some(path.value()),
136                    Opt::MapType(path) => opts.map_type = Some(path.value()),
137                    Opt::BitflagsPath(path) => opts.bitflags_path = Some(path.value()),
138                    Opt::Stubs => {
139                        opts.stubs = true;
140                    }
141                    Opt::ExportPrefix(prefix) => opts.export_prefix = Some(prefix.value()),
142                    Opt::AdditionalDerives(paths) => {
143                        opts.additional_derive_attributes = paths
144                            .into_iter()
145                            .map(|p| p.into_token_stream().to_string())
146                            .collect()
147                    }
148                    Opt::AdditionalDerivesIgnore(list) => {
149                        opts.additional_derive_ignore =
150                            list.into_iter().map(|i| i.value()).collect()
151                    }
152                    Opt::AdditionalTypeAttributes(list) => opts.additional_type_attributes = list,
153                    Opt::AdditionalMemberAttributes(list) => {
154                        opts.additional_member_attributes = list
155                    }
156                    Opt::With(with) => {
157                        // `HashMap` iteration order is non-deterministic, which can cause
158                        // non-determinism in crates that invoke the macro. Sort by key so the
159                        // output is stable.
160                        let mut with: Vec<_> = with.into_iter().collect();
161                        with.sort_by(|a, b| a.0.cmp(&b.0));
162                        opts.with.extend(with)
163                    }
164                    Opt::GenerateAll => {
165                        opts.generate_all = true;
166                    }
167                    Opt::TypeSectionSuffix(suffix) => {
168                        opts.type_section_suffix = Some(suffix.value());
169                    }
170                    Opt::DisableRunCtorsOnceWorkaround(enable) => {
171                        opts.disable_run_ctors_once_workaround = enable.value();
172                    }
173                    Opt::DefaultBindingsModule(enable) => {
174                        opts.default_bindings_module = Some(enable.value());
175                    }
176                    Opt::ExportMacroName(name) => {
177                        opts.export_macro_name = Some(name.value());
178                    }
179                    Opt::PubExportMacro(enable) => {
180                        opts.pub_export_macro = enable.value();
181                    }
182                    Opt::GenerateUnusedTypes(enable) => {
183                        opts.generate_unused_types = enable.value();
184                    }
185                    Opt::Features(f) => {
186                        features.extend(f.into_iter().map(|f| f.value()));
187                    }
188                    Opt::DisableCustomSectionLinkHelpers(disable) => {
189                        opts.disable_custom_section_link_helpers = disable.value();
190                    }
191                    Opt::Debug(enable) => {
192                        debug = enable.value();
193                    }
194                    Opt::Sync(val, span) => {
195                        if sync_configured {
196                            return Err(Error::new(span, "cannot specify second sync config"));
197                        }
198                        sync_configured = true;
199                        opts.async_ = val;
200                    }
201                    Opt::ChainableMethods(val, span) => {
202                        if method_chaining_configured {
203                            return Err(Error::new(
204                                span,
205                                "cannot specify second method chaining config",
206                            ));
207                        }
208                        method_chaining_configured = true;
209                        opts.chainable_methods = val;
210                    }
211                    Opt::MergeStructurallyEqualTypes(enable) => {
212                        opts.merge_structurally_equal_types = Some(Some(enable.value()))
213                    }
214                }
215            }
216        } else {
217            world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
218            if input.parse::<Option<syn::token::In>>()?.is_some() {
219                source = Some(Source::Paths(vec![PathBuf::from(
220                    input.parse::<syn::LitStr>()?.value(),
221                )]));
222            }
223        }
224        let (resolve, main_packages, files) = parse_source(&source, &features)?;
225        let world = resolve
226            .select_world(&main_packages, world.as_deref())
227            .map_err(|e| anyhow_to_syn(call_site, e, None))?;
228        Ok(Config {
229            opts,
230            resolve,
231            world,
232            files,
233            debug,
234        })
235    }
236}
237
238/// Parse the source
239fn parse_source(
240    source: &Option<Source>,
241    features: &[String],
242) -> syn::Result<(Resolve, Vec<PackageId>, Vec<PathBuf>)> {
243    let mut resolve = Resolve::default();
244    resolve.features.extend(features.iter().cloned());
245    let mut files = Vec::new();
246    let mut pkgs = Vec::new();
247    let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
248
249    let mut try_parse = || -> anyhow::Result<()> {
250        let mut parse = |paths: &[PathBuf]| -> anyhow::Result<()> {
251            for path in paths {
252                let p = root.join(path);
253                // Try to normalize the path to make the error message more understandable when
254                // the path is not correct. Fallback to the original path if normalization fails
255                // (probably return an error somewhere else).
256                let normalized_path = match std::fs::canonicalize(&p) {
257                    Ok(p) => p,
258                    Err(_) => p.to_path_buf(),
259                };
260                let (pkg, sources) = resolve.push_path(normalized_path)?;
261                pkgs.push(pkg);
262                files.extend(sources.paths().map(|p| p.to_owned()));
263            }
264            Ok(())
265        };
266        let default = root.join("wit");
267
268        match source {
269            Some(Source::Inline(s, path)) => {
270                match path {
271                    Some(p) => parse(p)?,
272                    // If no `path` is explicitly specified still parse the default
273                    // `wit` directory if it exists. Don't require its existence,
274                    // however, as `inline` can be used in lieu of a folder. Test
275                    // whether it exists and only if there is it parsed.
276                    None => {
277                        if default.exists() {
278                            parse(&[default])?;
279                        }
280                    }
281                }
282                pkgs.clear();
283                pkgs.push(resolve.push_str("macro-input", s)?);
284                Ok(())
285            }
286            Some(Source::Paths(p)) => parse(p),
287            None => parse(&[default]),
288        }
289    };
290
291    try_parse().map_err(|e| anyhow_to_syn(Span::call_site(), e, Some(&resolve.source_map)))?;
292
293    Ok((resolve, pkgs, files))
294}
295
296impl Config {
297    fn expand(mut self) -> Result<TokenStream> {
298        let mut files = Default::default();
299        let mut generator = self.opts.build();
300        generator
301            .generate(&mut self.resolve, self.world, &mut files)
302            .map_err(|e| anyhow_to_syn(Span::call_site(), e, None))?;
303        let (_, src) = files.iter().next().unwrap();
304        let mut src = std::str::from_utf8(src).unwrap().to_string();
305
306        // If a magical `WIT_BINDGEN_DEBUG` environment variable is set then
307        // place a formatted version of the expanded code into a file. This file
308        // will then show up in rustc error messages for any codegen issues and can
309        // be inspected manually.
310        if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug {
311            static INVOCATION: AtomicUsize = AtomicUsize::new(0);
312            let root = Path::new(env!("DEBUG_OUTPUT_DIR"));
313            let world_name = &self.resolve.worlds[self.world].name;
314            let n = INVOCATION.fetch_add(1, Relaxed);
315            let path = root.join(format!("{world_name}{n}.rs"));
316
317            // optimistically format the code but don't require success
318            let contents = match fmt(&src) {
319                Ok(formatted) => formatted,
320                Err(_) => src.clone(),
321            };
322            std::fs::write(&path, contents.as_bytes()).unwrap();
323
324            src = format!("include!({path:?});");
325        }
326        let mut contents = src.parse::<TokenStream>().unwrap();
327
328        // Include a dummy `include_bytes!` for any files we read so rustc knows that
329        // we depend on the contents of those files.
330        for file in self.files.iter() {
331            contents.extend(
332                format!(
333                    "const _: &[u8] = include_bytes!(r#\"{}\"#);\n",
334                    file.display()
335                )
336                .parse::<TokenStream>()
337                .unwrap(),
338            );
339        }
340
341        Ok(contents)
342    }
343}
344
345mod kw {
346    syn::custom_keyword!(std_feature);
347    syn::custom_keyword!(raw_strings);
348    syn::custom_keyword!(skip);
349    syn::custom_keyword!(world);
350    syn::custom_keyword!(path);
351    syn::custom_keyword!(inline);
352    syn::custom_keyword!(ownership);
353    syn::custom_keyword!(runtime_path);
354    syn::custom_keyword!(map_type);
355    syn::custom_keyword!(bitflags_path);
356    syn::custom_keyword!(exports);
357    syn::custom_keyword!(stubs);
358    syn::custom_keyword!(export_prefix);
359    syn::custom_keyword!(additional_derives);
360    syn::custom_keyword!(additional_derives_ignore);
361    syn::custom_keyword!(additional_type_attributes);
362    syn::custom_keyword!(additional_member_attributes);
363    syn::custom_keyword!(with);
364    syn::custom_keyword!(generate_all);
365    syn::custom_keyword!(type_section_suffix);
366    syn::custom_keyword!(disable_run_ctors_once_workaround);
367    syn::custom_keyword!(default_bindings_module);
368    syn::custom_keyword!(export_macro_name);
369    syn::custom_keyword!(pub_export_macro);
370    syn::custom_keyword!(generate_unused_types);
371    syn::custom_keyword!(features);
372    syn::custom_keyword!(disable_custom_section_link_helpers);
373    syn::custom_keyword!(imports);
374    syn::custom_keyword!(debug);
375    syn::custom_keyword!(chainable_methods);
376    syn::custom_keyword!(sync);
377    syn::custom_keyword!(merge_structurally_equal_types);
378}
379
380#[derive(Clone)]
381enum ExportKey {
382    World,
383    Name(syn::LitStr),
384}
385
386impl Parse for ExportKey {
387    fn parse(input: ParseStream<'_>) -> Result<Self> {
388        let l = input.lookahead1();
389        Ok(if l.peek(kw::world) {
390            input.parse::<kw::world>()?;
391            Self::World
392        } else {
393            Self::Name(input.parse()?)
394        })
395    }
396}
397
398impl From<ExportKey> for wit_bindgen_rust::ExportKey {
399    fn from(key: ExportKey) -> Self {
400        match key {
401            ExportKey::World => Self::World,
402            ExportKey::Name(s) => Self::Name(s.value()),
403        }
404    }
405}
406
407#[cfg(feature = "macro-string")]
408type PathType = macro_string::MacroString;
409#[cfg(not(feature = "macro-string"))]
410type PathType = syn::LitStr;
411
412trait EvaluateString {
413    fn evaluate_string(&self) -> Result<String>;
414}
415
416#[cfg(feature = "macro-string")]
417impl EvaluateString for macro_string::MacroString {
418    fn evaluate_string(&self) -> Result<String> {
419        self.eval()
420    }
421}
422
423#[cfg(not(feature = "macro-string"))]
424impl EvaluateString for syn::LitStr {
425    fn evaluate_string(&self) -> Result<String> {
426        Ok(self.value())
427    }
428}
429
430enum Opt {
431    World(syn::LitStr),
432    Path(Span, Vec<PathType>),
433    Inline(syn::LitStr),
434    UseStdFeature,
435    RawStrings,
436    Skip(Vec<syn::LitStr>),
437    Ownership(Ownership),
438    RuntimePath(syn::LitStr),
439    MapType(syn::LitStr),
440    BitflagsPath(syn::LitStr),
441    Stubs,
442    ExportPrefix(syn::LitStr),
443    // Parse as paths so we can take the concrete types/macro names rather than raw strings
444    AdditionalDerives(Vec<syn::Path>),
445    AdditionalDerivesIgnore(Vec<syn::LitStr>),
446    AdditionalTypeAttributes(Vec<(String, String)>),
447    AdditionalMemberAttributes(Vec<(String, String)>),
448    With(HashMap<String, WithOption>),
449    GenerateAll,
450    TypeSectionSuffix(syn::LitStr),
451    DisableRunCtorsOnceWorkaround(syn::LitBool),
452    DefaultBindingsModule(syn::LitStr),
453    ExportMacroName(syn::LitStr),
454    PubExportMacro(syn::LitBool),
455    GenerateUnusedTypes(syn::LitBool),
456    Features(Vec<syn::LitStr>),
457    DisableCustomSectionLinkHelpers(syn::LitBool),
458    Sync(AsyncFilterSet, Span),
459    Debug(syn::LitBool),
460    ChainableMethods(ChainableMethodFilterSet, Span),
461    MergeStructurallyEqualTypes(syn::LitBool),
462}
463
464impl Parse for Opt {
465    fn parse(input: ParseStream<'_>) -> Result<Self> {
466        let l = input.lookahead1();
467        if l.peek(kw::path) {
468            input.parse::<kw::path>()?;
469            input.parse::<Token![:]>()?;
470            // the `path` supports two forms:
471            // * path: "xxx"
472            // * path: ["aaa", "bbb"]
473            if input.peek(token::Bracket) {
474                let contents;
475                syn::bracketed!(contents in input);
476                let span = input.span();
477                let list = Punctuated::<PathType, Token![,]>::parse_terminated(&contents)?;
478                Ok(Opt::Path(span, list.into_iter().collect()))
479            } else {
480                let span = input.span();
481                let path: PathType = input.parse()?;
482                Ok(Opt::Path(span, vec![path]))
483            }
484        } else if l.peek(kw::inline) {
485            input.parse::<kw::inline>()?;
486            input.parse::<Token![:]>()?;
487            Ok(Opt::Inline(input.parse()?))
488        } else if l.peek(kw::world) {
489            input.parse::<kw::world>()?;
490            input.parse::<Token![:]>()?;
491            Ok(Opt::World(input.parse()?))
492        } else if l.peek(kw::std_feature) {
493            input.parse::<kw::std_feature>()?;
494            Ok(Opt::UseStdFeature)
495        } else if l.peek(kw::raw_strings) {
496            input.parse::<kw::raw_strings>()?;
497            Ok(Opt::RawStrings)
498        } else if l.peek(kw::ownership) {
499            input.parse::<kw::ownership>()?;
500            input.parse::<Token![:]>()?;
501            let ownership = input.parse::<syn::Ident>()?;
502            Ok(Opt::Ownership(match ownership.to_string().as_str() {
503                "Owning" => Ownership::Owning,
504                "Borrowing" => Ownership::Borrowing {
505                    duplicate_if_necessary: {
506                        let contents;
507                        braced!(contents in input);
508                        let field = contents.parse::<syn::Ident>()?;
509                        match field.to_string().as_str() {
510                            "duplicate_if_necessary" => {
511                                contents.parse::<Token![:]>()?;
512                                contents.parse::<syn::LitBool>()?.value
513                            }
514                            name => {
515                                return Err(Error::new(
516                                    field.span(),
517                                    format!(
518                                        "unrecognized `Ownership::Borrowing` field: `{name}`; \
519                                         expected `duplicate_if_necessary`"
520                                    ),
521                                ));
522                            }
523                        }
524                    },
525                },
526                name => {
527                    return Err(Error::new(
528                        ownership.span(),
529                        format!(
530                            "unrecognized ownership: `{name}`; \
531                             expected `Owning` or `Borrowing`"
532                        ),
533                    ));
534                }
535            }))
536        } else if l.peek(kw::skip) {
537            input.parse::<kw::skip>()?;
538            input.parse::<Token![:]>()?;
539            let contents;
540            syn::bracketed!(contents in input);
541            let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
542            Ok(Opt::Skip(list.iter().cloned().collect()))
543        } else if l.peek(kw::runtime_path) {
544            input.parse::<kw::runtime_path>()?;
545            input.parse::<Token![:]>()?;
546            Ok(Opt::RuntimePath(input.parse()?))
547        } else if l.peek(kw::map_type) {
548            input.parse::<kw::map_type>()?;
549            input.parse::<Token![:]>()?;
550            Ok(Opt::MapType(input.parse()?))
551        } else if l.peek(kw::bitflags_path) {
552            input.parse::<kw::bitflags_path>()?;
553            input.parse::<Token![:]>()?;
554            Ok(Opt::BitflagsPath(input.parse()?))
555        } else if l.peek(kw::stubs) {
556            input.parse::<kw::stubs>()?;
557            Ok(Opt::Stubs)
558        } else if l.peek(kw::export_prefix) {
559            input.parse::<kw::export_prefix>()?;
560            input.parse::<Token![:]>()?;
561            Ok(Opt::ExportPrefix(input.parse()?))
562        } else if l.peek(kw::additional_derives) {
563            input.parse::<kw::additional_derives>()?;
564            input.parse::<Token![:]>()?;
565            let contents;
566            syn::bracketed!(contents in input);
567            let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
568            Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
569        } else if l.peek(kw::additional_derives_ignore) {
570            input.parse::<kw::additional_derives_ignore>()?;
571            input.parse::<Token![:]>()?;
572            let contents;
573            syn::bracketed!(contents in input);
574            let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
575            Ok(Opt::AdditionalDerivesIgnore(list.iter().cloned().collect()))
576        } else if l.peek(kw::additional_type_attributes) {
577            input.parse::<kw::additional_type_attributes>()?;
578            input.parse::<Token![:]>()?;
579            let contents;
580            braced!(contents in input);
581            let fields: Punctuated<_, Token![,]> =
582                contents.parse_terminated(attr_map_field_parse, Token![,])?;
583            Ok(Opt::AdditionalTypeAttributes(
584                fields.into_iter().flatten().collect(),
585            ))
586        } else if l.peek(kw::additional_member_attributes) {
587            input.parse::<kw::additional_member_attributes>()?;
588            input.parse::<Token![:]>()?;
589            let contents;
590            braced!(contents in input);
591            let fields: Punctuated<_, Token![,]> =
592                contents.parse_terminated(attr_map_field_parse, Token![,])?;
593            Ok(Opt::AdditionalMemberAttributes(
594                fields.into_iter().flatten().collect(),
595            ))
596        } else if l.peek(kw::with) {
597            input.parse::<kw::with>()?;
598            input.parse::<Token![:]>()?;
599            let contents;
600            let _lbrace = braced!(contents in input);
601            let fields: Punctuated<_, Token![,]> =
602                contents.parse_terminated(with_field_parse, Token![,])?;
603            Ok(Opt::With(HashMap::from_iter(fields)))
604        } else if l.peek(kw::generate_all) {
605            input.parse::<kw::generate_all>()?;
606            Ok(Opt::GenerateAll)
607        } else if l.peek(kw::type_section_suffix) {
608            input.parse::<kw::type_section_suffix>()?;
609            input.parse::<Token![:]>()?;
610            Ok(Opt::TypeSectionSuffix(input.parse()?))
611        } else if l.peek(kw::disable_run_ctors_once_workaround) {
612            input.parse::<kw::disable_run_ctors_once_workaround>()?;
613            input.parse::<Token![:]>()?;
614            Ok(Opt::DisableRunCtorsOnceWorkaround(input.parse()?))
615        } else if l.peek(kw::default_bindings_module) {
616            input.parse::<kw::default_bindings_module>()?;
617            input.parse::<Token![:]>()?;
618            Ok(Opt::DefaultBindingsModule(input.parse()?))
619        } else if l.peek(kw::export_macro_name) {
620            input.parse::<kw::export_macro_name>()?;
621            input.parse::<Token![:]>()?;
622            Ok(Opt::ExportMacroName(input.parse()?))
623        } else if l.peek(kw::pub_export_macro) {
624            input.parse::<kw::pub_export_macro>()?;
625            input.parse::<Token![:]>()?;
626            Ok(Opt::PubExportMacro(input.parse()?))
627        } else if l.peek(kw::generate_unused_types) {
628            input.parse::<kw::generate_unused_types>()?;
629            input.parse::<Token![:]>()?;
630            Ok(Opt::GenerateUnusedTypes(input.parse()?))
631        } else if l.peek(kw::features) {
632            input.parse::<kw::features>()?;
633            input.parse::<Token![:]>()?;
634            let contents;
635            syn::bracketed!(contents in input);
636            let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
637            Ok(Opt::Features(list.into_iter().collect()))
638        } else if l.peek(kw::disable_custom_section_link_helpers) {
639            input.parse::<kw::disable_custom_section_link_helpers>()?;
640            input.parse::<Token![:]>()?;
641            Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?))
642        } else if l.peek(kw::debug) {
643            input.parse::<kw::debug>()?;
644            input.parse::<Token![:]>()?;
645            Ok(Opt::Debug(input.parse()?))
646        } else if l.peek(kw::chainable_methods) {
647            let span = input.parse::<kw::chainable_methods>()?.span;
648            input.parse::<Token![:]>()?;
649
650            let mut set = ChainableMethodFilterSet::default();
651            let contents;
652            syn::bracketed!(contents in input);
653            for val in contents.parse_terminated(|p| p.parse::<syn::LitStr>(), Token![,])? {
654                set.push(&val.value());
655            }
656            Ok(Opt::ChainableMethods(set, span))
657        } else if l.peek(kw::sync) {
658            let span = input.parse::<kw::sync>()?.span;
659            input.parse::<Token![:]>()?;
660            let mut set = AsyncFilterSet::default();
661            if input.peek(syn::LitBool) {
662                if input.parse::<syn::LitBool>()?.value {
663                    set.push("all");
664                }
665            } else {
666                let contents;
667                syn::bracketed!(contents in input);
668                for val in contents.parse_terminated(|p| p.parse::<syn::LitStr>(), Token![,])? {
669                    set.push(&val.value());
670                }
671            }
672            Ok(Opt::Sync(set, span))
673        } else if input.peek(Token![async]) {
674            let span = input.parse::<Token![async]>()?.span;
675            Err(Error::new(
676                span,
677                "the `async` option has been removed: functions that are `async` \
678                 in WIT get async bindings by default, and `sync` opts them out",
679            ))
680        } else if l.peek(kw::merge_structurally_equal_types) {
681            input.parse::<kw::merge_structurally_equal_types>()?;
682            input.parse::<Token![:]>()?;
683            Ok(Opt::MergeStructurallyEqualTypes(input.parse()?))
684        } else {
685            Err(l.error())
686        }
687    }
688}
689
690// Parse one `"selector": [#[attr] ...]` entry into a pair per attribute.
691fn attr_map_field_parse(input: ParseStream<'_>) -> Result<Vec<(String, String)>> {
692    let selector = input.parse::<syn::LitStr>()?;
693    input.parse::<Token![:]>()?;
694    let contents;
695    let bracket = syn::bracketed!(contents in input);
696    let attrs = contents.call(syn::Attribute::parse_outer)?;
697    if !contents.is_empty() {
698        return Err(contents.error("expected an outer attribute, `#[...]`"));
699    }
700    // An empty list would flatten away and escape the unused-selector check.
701    if attrs.is_empty() {
702        return Err(Error::new(
703            bracket.span.join(),
704            "attribute list must not be empty",
705        ));
706    }
707    let selector = selector.value();
708    Ok(attrs
709        .into_iter()
710        .map(|a| (selector.clone(), a.to_token_stream().to_string()))
711        .collect())
712}
713
714fn with_field_parse(input: ParseStream<'_>) -> Result<(String, WithOption)> {
715    let interface = input.parse::<syn::LitStr>()?.value();
716    input.parse::<Token![:]>()?;
717    let start = input.span();
718    let path = input.parse::<syn::Path>()?;
719
720    // It's not possible for the segments of a path to be empty
721    let span = start
722        .join(path.segments.last().unwrap().ident.span())
723        .unwrap_or(start);
724
725    if path.is_ident("generate") {
726        return Ok((interface, WithOption::Generate));
727    }
728
729    let mut buf = String::new();
730    let append = |buf: &mut String, segment: syn::PathSegment| -> Result<()> {
731        if !segment.arguments.is_none() {
732            return Err(Error::new(
733                span,
734                "Module path must not contain angles or parens",
735            ));
736        }
737
738        buf.push_str(&segment.ident.to_string());
739
740        Ok(())
741    };
742
743    if path.leading_colon.is_some() {
744        buf.push_str("::");
745    }
746
747    let mut segments = path.segments.into_iter();
748
749    if let Some(segment) = segments.next() {
750        append(&mut buf, segment)?;
751    }
752
753    for segment in segments {
754        buf.push_str("::");
755        append(&mut buf, segment)?;
756    }
757
758    Ok((interface, WithOption::Path(buf)))
759}
760
761/// Format a valid Rust string
762fn fmt(input: &str) -> Result<String> {
763    let syntax_tree = syn::parse_file(input)?;
764    Ok(prettyplease::unparse(&syntax_tree))
765}