snafu_derive/
parse.rs

1use std::collections::BTreeSet;
2
3use crate::{ModuleName, ProvideKind, SnafuAttribute};
4use proc_macro2::TokenStream;
5use quote::{format_ident, ToTokens};
6use syn::{
7    parenthesized,
8    parse::{Parse, ParseStream, Result},
9    punctuated::Punctuated,
10    token, Expr, Ident, Lit, LitBool, LitStr, Path, Type,
11};
12
13mod kw {
14    use syn::custom_keyword;
15
16    custom_keyword!(backtrace);
17    custom_keyword!(context);
18    custom_keyword!(crate_root);
19    custom_keyword!(display);
20    custom_keyword!(implicit);
21    custom_keyword!(module);
22    custom_keyword!(provide);
23    custom_keyword!(source);
24    custom_keyword!(transparent);
25    custom_keyword!(visibility);
26    custom_keyword!(whatever);
27
28    custom_keyword!(from);
29
30    custom_keyword!(suffix);
31
32    custom_keyword!(chain);
33    custom_keyword!(opt);
34    custom_keyword!(priority);
35}
36
37pub(crate) fn attributes_from_syn(
38    attrs: Vec<syn::Attribute>,
39) -> super::MultiSynResult<Vec<SnafuAttribute>> {
40    let mut ours = Vec::new();
41    let mut errs = Vec::new();
42
43    for attr in attrs {
44        if attr.path().is_ident("snafu") {
45            let attr_list = Punctuated::<Attribute, token::Comma>::parse_terminated;
46
47            match attr.parse_args_with(attr_list) {
48                Ok(attrs) => {
49                    ours.extend(attrs.into_iter().map(Into::into));
50                }
51                Err(e) => errs.push(e),
52            }
53        } else if attr.path().is_ident("doc") {
54            // Ignore any errors that occur while parsing the doc
55            // comment. This isn't our attribute so we shouldn't
56            // assume that we know what values are acceptable.
57            if let Ok(comment) = syn::parse2::<DocComment>(attr.meta.to_token_stream()) {
58                ours.push(comment.into());
59            }
60        }
61    }
62
63    for attr in &ours {
64        if let SnafuAttribute::Provide(tt, ProvideKind::Expression(p)) = attr {
65            if p.is_chain && !p.is_ref {
66                errs.push(syn::Error::new_spanned(
67                    tt,
68                    "May only chain to references; please add `ref` flag",
69                ));
70            }
71        }
72    }
73
74    if errs.is_empty() {
75        Ok(ours)
76    } else {
77        Err(errs)
78    }
79}
80
81enum Attribute {
82    Backtrace(Backtrace),
83    Context(Context),
84    CrateRoot(CrateRoot),
85    Display(Display),
86    Implicit(Implicit),
87    Module(Module),
88    Provide(Provide),
89    Source(Source),
90    Transparent(Transparent),
91    Visibility(Visibility),
92    Whatever(Whatever),
93}
94
95impl From<Attribute> for SnafuAttribute {
96    fn from(other: Attribute) -> Self {
97        use self::Attribute::*;
98
99        match other {
100            Backtrace(b) => SnafuAttribute::Backtrace(b.to_token_stream(), b.into_bool()),
101            Context(c) => SnafuAttribute::Context(c.to_token_stream(), c.into_component()),
102            CrateRoot(cr) => SnafuAttribute::CrateRoot(cr.to_token_stream(), cr.into_arbitrary()),
103            Display(d) => SnafuAttribute::Display(d.to_token_stream(), d.into_display()),
104            Implicit(d) => SnafuAttribute::Implicit(d.to_token_stream(), d.into_bool()),
105            Module(v) => SnafuAttribute::Module(v.to_token_stream(), v.into_value()),
106            Provide(v) => SnafuAttribute::Provide(v.to_token_stream(), v.into_value()),
107            Source(s) => SnafuAttribute::Source(s.to_token_stream(), s.into_components()),
108            Transparent(t) => SnafuAttribute::Transparent(t.to_token_stream(), t.into_bool()),
109            Visibility(v) => SnafuAttribute::Visibility(v.to_token_stream(), v.into_arbitrary()),
110            Whatever(o) => SnafuAttribute::Whatever(o.to_token_stream()),
111        }
112    }
113}
114
115impl Parse for Attribute {
116    fn parse(input: ParseStream) -> Result<Self> {
117        let lookahead = input.lookahead1();
118        if lookahead.peek(kw::backtrace) {
119            input.parse().map(Attribute::Backtrace)
120        } else if lookahead.peek(kw::context) {
121            input.parse().map(Attribute::Context)
122        } else if lookahead.peek(kw::crate_root) {
123            input.parse().map(Attribute::CrateRoot)
124        } else if lookahead.peek(kw::display) {
125            input.parse().map(Attribute::Display)
126        } else if lookahead.peek(kw::implicit) {
127            input.parse().map(Attribute::Implicit)
128        } else if lookahead.peek(kw::module) {
129            input.parse().map(Attribute::Module)
130        } else if lookahead.peek(kw::provide) {
131            input.parse().map(Attribute::Provide)
132        } else if lookahead.peek(kw::source) {
133            input.parse().map(Attribute::Source)
134        } else if lookahead.peek(kw::transparent) {
135            input.parse().map(Attribute::Transparent)
136        } else if lookahead.peek(kw::visibility) {
137            input.parse().map(Attribute::Visibility)
138        } else if lookahead.peek(kw::whatever) {
139            input.parse().map(Attribute::Whatever)
140        } else {
141            Err(lookahead.error())
142        }
143    }
144}
145
146struct Backtrace {
147    backtrace_token: kw::backtrace,
148    arg: MaybeArg<LitBool>,
149}
150
151impl Backtrace {
152    fn into_bool(self) -> bool {
153        self.arg.into_option().map_or(true, |a| a.value)
154    }
155}
156
157impl Parse for Backtrace {
158    fn parse(input: ParseStream) -> Result<Self> {
159        Ok(Self {
160            backtrace_token: input.parse()?,
161            arg: input.parse()?,
162        })
163    }
164}
165
166impl ToTokens for Backtrace {
167    fn to_tokens(&self, tokens: &mut TokenStream) {
168        self.backtrace_token.to_tokens(tokens);
169        self.arg.to_tokens(tokens);
170    }
171}
172
173struct Context {
174    context_token: kw::context,
175    arg: MaybeArg<ContextArg>,
176}
177
178impl Context {
179    fn into_component(self) -> super::Context {
180        use super::{Context::*, SuffixKind};
181
182        match self.arg.into_option() {
183            None => Flag(true),
184            Some(arg) => match arg {
185                ContextArg::Flag { value } => Flag(value.value),
186                ContextArg::Suffix {
187                    suffix:
188                        SuffixArg::Flag {
189                            value: LitBool { value: true, .. },
190                        },
191                    ..
192                } => Suffix(SuffixKind::Default),
193                ContextArg::Suffix {
194                    suffix:
195                        SuffixArg::Flag {
196                            value: LitBool { value: false, .. },
197                        },
198                    ..
199                } => Suffix(SuffixKind::None),
200                ContextArg::Suffix {
201                    suffix: SuffixArg::Suffix { suffix, .. },
202                    ..
203                } => Suffix(SuffixKind::Some(suffix)),
204            },
205        }
206    }
207}
208
209impl Parse for Context {
210    fn parse(input: ParseStream) -> Result<Self> {
211        Ok(Self {
212            context_token: input.parse()?,
213            arg: input.parse()?,
214        })
215    }
216}
217
218impl ToTokens for Context {
219    fn to_tokens(&self, tokens: &mut TokenStream) {
220        self.context_token.to_tokens(tokens);
221        self.arg.to_tokens(tokens);
222    }
223}
224
225enum ContextArg {
226    Flag {
227        value: LitBool,
228    },
229    Suffix {
230        suffix_token: kw::suffix,
231        paren_token: token::Paren,
232        suffix: SuffixArg,
233    },
234}
235
236impl Parse for ContextArg {
237    fn parse(input: ParseStream) -> Result<Self> {
238        let lookahead = input.lookahead1();
239        if lookahead.peek(LitBool) {
240            Ok(ContextArg::Flag {
241                value: input.parse()?,
242            })
243        } else if lookahead.peek(kw::suffix) {
244            let content;
245            Ok(ContextArg::Suffix {
246                suffix_token: input.parse()?,
247                paren_token: parenthesized!(content in input),
248                suffix: content.parse()?,
249            })
250        } else {
251            Err(lookahead.error())
252        }
253    }
254}
255
256impl ToTokens for ContextArg {
257    fn to_tokens(&self, tokens: &mut TokenStream) {
258        match self {
259            ContextArg::Flag { value } => {
260                value.to_tokens(tokens);
261            }
262            ContextArg::Suffix {
263                suffix_token,
264                paren_token,
265                suffix,
266            } => {
267                suffix_token.to_tokens(tokens);
268                paren_token.surround(tokens, |tokens| {
269                    suffix.to_tokens(tokens);
270                })
271            }
272        }
273    }
274}
275
276enum SuffixArg {
277    Flag { value: LitBool },
278    Suffix { suffix: Ident },
279}
280
281impl Parse for SuffixArg {
282    fn parse(input: ParseStream) -> Result<Self> {
283        let lookahead = input.lookahead1();
284        if lookahead.peek(LitBool) {
285            Ok(SuffixArg::Flag {
286                value: input.parse()?,
287            })
288        } else if lookahead.peek(syn::Ident) {
289            Ok(SuffixArg::Suffix {
290                suffix: input.parse()?,
291            })
292        } else {
293            Err(lookahead.error())
294        }
295    }
296}
297
298impl ToTokens for SuffixArg {
299    fn to_tokens(&self, tokens: &mut TokenStream) {
300        match self {
301            SuffixArg::Flag { value } => {
302                value.to_tokens(tokens);
303            }
304            SuffixArg::Suffix { suffix } => {
305                suffix.to_tokens(tokens);
306            }
307        }
308    }
309}
310
311struct CrateRoot {
312    crate_root_token: kw::crate_root,
313    paren_token: token::Paren,
314    arg: Path,
315}
316
317impl CrateRoot {
318    // TODO: Remove boxed trait object
319    fn into_arbitrary(self) -> Box<dyn ToTokens> {
320        Box::new(self.arg)
321    }
322}
323
324impl Parse for CrateRoot {
325    fn parse(input: ParseStream) -> Result<Self> {
326        let content;
327        Ok(Self {
328            crate_root_token: input.parse()?,
329            paren_token: parenthesized!(content in input),
330            arg: content.parse()?,
331        })
332    }
333}
334
335impl ToTokens for CrateRoot {
336    fn to_tokens(&self, tokens: &mut TokenStream) {
337        self.crate_root_token.to_tokens(tokens);
338        self.paren_token.surround(tokens, |tokens| {
339            self.arg.to_tokens(tokens);
340        });
341    }
342}
343
344struct Display {
345    display_token: kw::display,
346    paren_token: token::Paren,
347    args: Punctuated<Expr, token::Comma>,
348}
349
350impl Display {
351    fn into_display(self) -> crate::Display {
352        let exprs: Vec<_> = self.args.into_iter().collect();
353        let mut shorthand_names = BTreeSet::new();
354        let mut assigned_names = BTreeSet::new();
355
356        // Do a best-effort parsing here; if we fail, the compiler
357        // will likely spit out something more useful when it tries to
358        // parse it.
359        if let Some((Expr::Lit(l), args)) = exprs.split_first() {
360            if let Lit::Str(s) = &l.lit {
361                let format_str = s.value();
362                let names = extract_field_names(&format_str).map(|n| format_ident!("{}", n));
363                shorthand_names.extend(names);
364            }
365
366            for arg in args {
367                if let Expr::Assign(a) = arg {
368                    if let Expr::Path(p) = &*a.left {
369                        assigned_names.extend(p.path.get_ident().cloned());
370                    }
371                }
372            }
373        }
374
375        crate::Display {
376            exprs,
377            shorthand_names,
378            assigned_names,
379        }
380    }
381}
382
383pub(crate) fn extract_field_names(mut s: &str) -> impl Iterator<Item = &str> {
384    std::iter::from_fn(move || loop {
385        let open_curly = s.find('{')?;
386        s = &s[open_curly + '{'.len_utf8()..];
387
388        if s.starts_with('{') {
389            s = &s['{'.len_utf8()..];
390            continue;
391        }
392
393        let end_curly = s.find('}')?;
394        let format_contents = &s[..end_curly];
395
396        let name = match format_contents.find(':') {
397            Some(idx) => &format_contents[..idx],
398            None => format_contents,
399        };
400
401        if name.is_empty() {
402            continue;
403        }
404
405        return Some(name);
406    })
407}
408
409impl Parse for Display {
410    fn parse(input: ParseStream) -> Result<Self> {
411        let content;
412        Ok(Self {
413            display_token: input.parse()?,
414            paren_token: parenthesized!(content in input),
415            args: Punctuated::parse_terminated(&content)?,
416        })
417    }
418}
419
420impl ToTokens for Display {
421    fn to_tokens(&self, tokens: &mut TokenStream) {
422        self.display_token.to_tokens(tokens);
423        self.paren_token.surround(tokens, |tokens| {
424            self.args.to_tokens(tokens);
425        });
426    }
427}
428
429struct DocComment {
430    doc_ident: Ident,
431    eq_token: token::Eq,
432    str: LitStr,
433}
434
435impl DocComment {
436    fn into_value(self) -> String {
437        self.str.value()
438    }
439}
440
441impl From<DocComment> for SnafuAttribute {
442    fn from(other: DocComment) -> Self {
443        SnafuAttribute::DocComment(other.to_token_stream(), other.into_value())
444    }
445}
446
447impl Parse for DocComment {
448    fn parse(input: ParseStream) -> Result<Self> {
449        Ok(Self {
450            doc_ident: input.parse()?,
451            eq_token: input.parse()?,
452            str: input.parse()?,
453        })
454    }
455}
456
457impl ToTokens for DocComment {
458    fn to_tokens(&self, tokens: &mut TokenStream) {
459        self.doc_ident.to_tokens(tokens);
460        self.eq_token.to_tokens(tokens);
461        self.str.to_tokens(tokens);
462    }
463}
464
465struct Implicit {
466    implicit_token: kw::implicit,
467    arg: MaybeArg<LitBool>,
468}
469
470impl Implicit {
471    fn into_bool(self) -> bool {
472        self.arg.into_option().map_or(true, |a| a.value)
473    }
474}
475
476impl Parse for Implicit {
477    fn parse(input: ParseStream) -> Result<Self> {
478        Ok(Self {
479            implicit_token: input.parse()?,
480            arg: input.parse()?,
481        })
482    }
483}
484
485impl ToTokens for Implicit {
486    fn to_tokens(&self, tokens: &mut TokenStream) {
487        self.implicit_token.to_tokens(tokens);
488        self.arg.to_tokens(tokens);
489    }
490}
491
492struct Module {
493    module_token: kw::module,
494    arg: MaybeArg<Ident>,
495}
496
497impl Module {
498    fn into_value(self) -> ModuleName {
499        match self.arg.into_option() {
500            None => ModuleName::Default,
501            Some(name) => ModuleName::Custom(name),
502        }
503    }
504}
505
506impl Parse for Module {
507    fn parse(input: ParseStream) -> Result<Self> {
508        Ok(Self {
509            module_token: input.parse()?,
510            arg: input.parse()?,
511        })
512    }
513}
514
515impl ToTokens for Module {
516    fn to_tokens(&self, tokens: &mut TokenStream) {
517        self.module_token.to_tokens(tokens);
518        self.arg.to_tokens(tokens);
519    }
520}
521
522struct Provide {
523    provide_token: kw::provide,
524    arg: MaybeArg<ProvideArg>,
525}
526
527impl Provide {
528    fn into_value(self) -> ProvideKind {
529        match self.arg.into_option() {
530            None => ProvideKind::Flag(true),
531            Some(ProvideArg::Flag { value }) => ProvideKind::Flag(value.value),
532            Some(ProvideArg::Expression {
533                flags,
534                ty,
535                arrow: _,
536                expr,
537            }) => ProvideKind::Expression(crate::Provide {
538                is_chain: flags.is_chain(),
539                is_opt: flags.is_opt(),
540                is_priority: flags.is_priority(),
541                is_ref: flags.is_ref(),
542                ty,
543                expr,
544            }),
545        }
546    }
547}
548
549impl Parse for Provide {
550    fn parse(input: ParseStream) -> Result<Self> {
551        Ok(Self {
552            provide_token: input.parse()?,
553            arg: input.parse()?,
554        })
555    }
556}
557
558impl ToTokens for Provide {
559    fn to_tokens(&self, tokens: &mut TokenStream) {
560        self.provide_token.to_tokens(tokens);
561        self.arg.to_tokens(tokens);
562    }
563}
564
565enum ProvideArg {
566    Flag {
567        value: LitBool,
568    },
569    Expression {
570        flags: ProvideFlags,
571        ty: Type,
572        arrow: token::FatArrow,
573        expr: Expr,
574    },
575}
576
577impl Parse for ProvideArg {
578    fn parse(input: ParseStream) -> Result<Self> {
579        if input.peek(LitBool) {
580            Ok(ProvideArg::Flag {
581                value: input.parse()?,
582            })
583        } else {
584            Ok(ProvideArg::Expression {
585                flags: input.parse()?,
586                ty: input.parse()?,
587                arrow: input.parse()?,
588                expr: input.parse()?,
589            })
590        }
591    }
592}
593
594impl ToTokens for ProvideArg {
595    fn to_tokens(&self, tokens: &mut TokenStream) {
596        match self {
597            ProvideArg::Flag { value } => {
598                value.to_tokens(tokens);
599            }
600            ProvideArg::Expression {
601                flags,
602                ty,
603                arrow,
604                expr,
605            } => {
606                flags.to_tokens(tokens);
607                ty.to_tokens(tokens);
608                arrow.to_tokens(tokens);
609                expr.to_tokens(tokens);
610            }
611        }
612    }
613}
614
615struct ProvideFlags(Punctuated<ProvideFlag, token::Comma>);
616
617impl ProvideFlags {
618    fn is_chain(&self) -> bool {
619        self.0.iter().any(ProvideFlag::is_chain)
620    }
621
622    fn is_opt(&self) -> bool {
623        self.0.iter().any(ProvideFlag::is_opt)
624    }
625
626    fn is_priority(&self) -> bool {
627        self.0.iter().any(ProvideFlag::is_priority)
628    }
629
630    fn is_ref(&self) -> bool {
631        self.0.iter().any(ProvideFlag::is_ref)
632    }
633}
634
635impl Parse for ProvideFlags {
636    fn parse(input: ParseStream) -> Result<Self> {
637        let mut flags = Punctuated::new();
638
639        while ProvideFlag::peek(input) {
640            flags.push_value(input.parse()?);
641            flags.push_punct(input.parse()?);
642        }
643
644        Ok(Self(flags))
645    }
646}
647
648impl ToTokens for ProvideFlags {
649    fn to_tokens(&self, tokens: &mut TokenStream) {
650        self.0.to_tokens(tokens)
651    }
652}
653
654enum ProvideFlag {
655    Chain(kw::chain),
656    Opt(kw::opt),
657    Priority(kw::priority),
658    Ref(token::Ref),
659}
660
661impl ProvideFlag {
662    fn peek(input: ParseStream) -> bool {
663        input.peek(kw::chain)
664            || input.peek(kw::opt)
665            || input.peek(kw::priority)
666            || input.peek(token::Ref)
667    }
668
669    fn is_chain(&self) -> bool {
670        matches!(self, ProvideFlag::Chain(_))
671    }
672
673    fn is_opt(&self) -> bool {
674        matches!(self, ProvideFlag::Opt(_))
675    }
676
677    fn is_priority(&self) -> bool {
678        matches!(self, ProvideFlag::Priority(_))
679    }
680
681    fn is_ref(&self) -> bool {
682        matches!(self, ProvideFlag::Ref(_))
683    }
684}
685
686impl Parse for ProvideFlag {
687    fn parse(input: ParseStream) -> Result<Self> {
688        let lookahead = input.lookahead1();
689
690        if lookahead.peek(kw::chain) {
691            input.parse().map(ProvideFlag::Chain)
692        } else if lookahead.peek(kw::opt) {
693            input.parse().map(ProvideFlag::Opt)
694        } else if lookahead.peek(kw::priority) {
695            input.parse().map(ProvideFlag::Priority)
696        } else if lookahead.peek(token::Ref) {
697            input.parse().map(ProvideFlag::Ref)
698        } else {
699            Err(lookahead.error())
700        }
701    }
702}
703
704impl ToTokens for ProvideFlag {
705    fn to_tokens(&self, tokens: &mut TokenStream) {
706        match self {
707            ProvideFlag::Chain(v) => v.to_tokens(tokens),
708            ProvideFlag::Opt(v) => v.to_tokens(tokens),
709            ProvideFlag::Priority(v) => v.to_tokens(tokens),
710            ProvideFlag::Ref(v) => v.to_tokens(tokens),
711        }
712    }
713}
714
715struct Source {
716    source_token: kw::source,
717    args: MaybeArg<Punctuated<SourceArg, token::Comma>>,
718}
719
720impl Source {
721    fn into_components(self) -> Vec<super::Source> {
722        match self.args.into_option() {
723            None => vec![super::Source::Flag(true)],
724            Some(args) => args
725                .into_iter()
726                .map(|sa| match sa {
727                    SourceArg::Flag { value } => super::Source::Flag(value.value),
728                    SourceArg::From { r#type, expr, .. } => super::Source::From(r#type, expr),
729                })
730                .collect(),
731        }
732    }
733}
734
735impl Parse for Source {
736    fn parse(input: ParseStream) -> Result<Self> {
737        Ok(Self {
738            source_token: input.parse()?,
739            args: MaybeArg::parse_with(input, Punctuated::parse_terminated)?,
740        })
741    }
742}
743
744impl ToTokens for Source {
745    fn to_tokens(&self, tokens: &mut TokenStream) {
746        self.source_token.to_tokens(tokens);
747        self.args.to_tokens(tokens);
748    }
749}
750
751enum SourceArg {
752    Flag {
753        value: LitBool,
754    },
755    From {
756        from_token: kw::from,
757        paren_token: token::Paren,
758        r#type: Type,
759        comma_token: token::Comma,
760        expr: Expr,
761    },
762}
763
764impl Parse for SourceArg {
765    fn parse(input: ParseStream) -> Result<Self> {
766        let lookahead = input.lookahead1();
767        if lookahead.peek(LitBool) {
768            Ok(SourceArg::Flag {
769                value: input.parse()?,
770            })
771        } else if lookahead.peek(kw::from) {
772            let content;
773            Ok(SourceArg::From {
774                from_token: input.parse()?,
775                paren_token: parenthesized!(content in input),
776                r#type: content.parse()?,
777                comma_token: content.parse()?,
778                expr: content.parse()?,
779            })
780        } else {
781            Err(lookahead.error())
782        }
783    }
784}
785
786impl ToTokens for SourceArg {
787    fn to_tokens(&self, tokens: &mut TokenStream) {
788        match self {
789            SourceArg::Flag { value } => {
790                value.to_tokens(tokens);
791            }
792            SourceArg::From {
793                from_token,
794                paren_token,
795                r#type,
796                comma_token,
797                expr,
798            } => {
799                from_token.to_tokens(tokens);
800                paren_token.surround(tokens, |tokens| {
801                    r#type.to_tokens(tokens);
802                    comma_token.to_tokens(tokens);
803                    expr.to_tokens(tokens);
804                })
805            }
806        }
807    }
808}
809
810struct Transparent {
811    transparent_token: kw::transparent,
812    arg: MaybeArg<LitBool>,
813}
814
815impl Transparent {
816    fn into_bool(self) -> bool {
817        self.arg.into_option().map_or(true, |a| a.value)
818    }
819}
820
821impl Parse for Transparent {
822    fn parse(input: ParseStream) -> Result<Self> {
823        Ok(Self {
824            transparent_token: input.parse()?,
825            arg: input.parse()?,
826        })
827    }
828}
829
830impl ToTokens for Transparent {
831    fn to_tokens(&self, tokens: &mut TokenStream) {
832        self.transparent_token.to_tokens(tokens);
833        self.arg.to_tokens(tokens);
834    }
835}
836
837struct Visibility {
838    visibility_token: kw::visibility,
839    visibility: MaybeArg<syn::Visibility>,
840}
841
842impl Visibility {
843    // TODO: Remove boxed trait object
844    fn into_arbitrary(self) -> Box<dyn ToTokens> {
845        // TODO: Move this default value out of parsing
846        self.visibility
847            .into_option()
848            .map_or_else(super::private_visibility, |v| Box::new(v))
849    }
850}
851
852impl Parse for Visibility {
853    fn parse(input: ParseStream) -> Result<Self> {
854        Ok(Self {
855            visibility_token: input.parse()?,
856            visibility: input.parse()?,
857        })
858    }
859}
860
861impl ToTokens for Visibility {
862    fn to_tokens(&self, tokens: &mut TokenStream) {
863        self.visibility_token.to_tokens(tokens);
864        self.visibility.to_tokens(tokens);
865    }
866}
867
868struct Whatever {
869    whatever_token: kw::whatever,
870}
871
872impl Parse for Whatever {
873    fn parse(input: ParseStream) -> Result<Self> {
874        Ok(Self {
875            whatever_token: input.parse()?,
876        })
877    }
878}
879
880impl ToTokens for Whatever {
881    fn to_tokens(&self, tokens: &mut TokenStream) {
882        self.whatever_token.to_tokens(tokens);
883    }
884}
885
886enum MaybeArg<T> {
887    None,
888    Some {
889        paren_token: token::Paren,
890        content: T,
891    },
892}
893
894impl<T> MaybeArg<T> {
895    fn into_option(self) -> Option<T> {
896        match self {
897            MaybeArg::None => None,
898            MaybeArg::Some { content, .. } => Some(content),
899        }
900    }
901
902    fn parse_with<F>(input: ParseStream<'_>, parser: F) -> Result<Self>
903    where
904        F: FnOnce(ParseStream<'_>) -> Result<T>,
905    {
906        let lookahead = input.lookahead1();
907        if lookahead.peek(token::Paren) {
908            let content;
909            Ok(MaybeArg::Some {
910                paren_token: parenthesized!(content in input),
911                content: parser(&content)?,
912            })
913        } else {
914            Ok(MaybeArg::None)
915        }
916    }
917}
918
919impl<T: Parse> Parse for MaybeArg<T> {
920    fn parse(input: ParseStream) -> Result<Self> {
921        Self::parse_with(input, Parse::parse)
922    }
923}
924
925impl<T: ToTokens> ToTokens for MaybeArg<T> {
926    fn to_tokens(&self, tokens: &mut TokenStream) {
927        if let MaybeArg::Some {
928            paren_token,
929            content,
930        } = self
931        {
932            paren_token.surround(tokens, |tokens| {
933                content.to_tokens(tokens);
934            });
935        }
936    }
937}
938
939#[cfg(test)]
940mod test {
941    use super::*;
942
943    fn names(s: &str) -> Vec<&str> {
944        extract_field_names(s).collect::<Vec<_>>()
945    }
946
947    #[test]
948    fn ignores_positional_arguments() {
949        assert_eq!(names("{}"), [] as [&str; 0]);
950    }
951
952    #[test]
953    fn finds_named_argument() {
954        assert_eq!(names("{a}"), ["a"]);
955    }
956
957    #[test]
958    fn finds_multiple_named_arguments() {
959        assert_eq!(names("{a} {b}"), ["a", "b"]);
960    }
961
962    #[test]
963    fn ignores_escaped_braces() {
964        assert_eq!(names("{{a}}"), [] as [&str; 0]);
965    }
966
967    #[test]
968    fn finds_named_arguments_around_escaped() {
969        assert_eq!(names("{a} {{b}} {c}"), ["a", "c"]);
970    }
971
972    #[test]
973    fn ignores_format_spec() {
974        assert_eq!(names("{a:?}"), ["a"]);
975    }
976}