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