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::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
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 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
202/// Parse the source
203fn 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            // Try to normalize the path to make the error message more understandable when
216            // the path is not correct. Fallback to the original path if normalization fails
217            // (probably return an error somewhere else).
218            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                // If no `path` is explicitly specified still parse the default
234                // `wit` directory if it exists. Don't require its existence,
235                // however, as `inline` can be used in lieu of a folder. Test
236                // whether it exists and only if there is it parsed.
237                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 a magical `WIT_BINDGEN_DEBUG` environment variable is set then
264        // place a formatted version of the expanded code into a file. This file
265        // will then show up in rustc error messages for any codegen issues and can
266        // be inspected manually.
267        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            // optimistically format the code but don't require success
275            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        // Include a dummy `include_bytes!` for any files we read so rustc knows that
286        // we depend on the contents of those files.
287        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    // Parse as paths so we can take the concrete types/macro names rather than raw strings
397    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            // the `path` supports two forms:
421            // * path: "xxx"
422            // * path: ["aaa", "bbb"]
423            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    // It's not possible for the segments of a path to be empty
608    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
648/// Format a valid Rust string
649fn fmt(input: &str) -> Result<String> {
650    let syntax_tree = syn::parse_file(input)?;
651    Ok(prettyplease::unparse(&syntax_tree))
652}