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
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 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
211fn 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 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 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 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 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 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 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 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
647fn 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 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 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
718fn fmt(input: &str) -> Result<String> {
720 let syntax_tree = syn::parse_file(input)?;
721 Ok(prettyplease::unparse(&syntax_tree))
722}