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