Skip to main content

clap_derive/derives/
value_enum.rs

1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3// Ana Hobden (@hoverbear) <operator@hoverbear.org>
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use proc_macro2::TokenStream;
12use quote::quote;
13use quote::quote_spanned;
14use syn::{Data, DeriveInput, Fields, Ident, Variant, spanned::Spanned};
15
16use crate::item::{Item, Kind, Name};
17
18pub(crate) fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
19    let ident = &input.ident;
20
21    match input.data {
22        Data::Enum(ref e) => {
23            let name = Name::Derived(ident.clone());
24            let item = Item::from_value_enum(input, name)?;
25            let mut variants = Vec::new();
26            for variant in &e.variants {
27                let item =
28                    Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?;
29                variants.push((variant, item));
30            }
31            gen_for_enum(&item, ident, &variants)
32        }
33        _ => {
    let span = proc_macro2::Span::call_site();
    {
        return Err({
                    #[allow(unused_imports)]
                    use crate::utils::error::*;
                    let msg =
                        ::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("`#[derive(ValueEnum)]` only supports enums"))
                            });
                    span.EXPECTED_Span_OR_ToTokens(msg)
                });
    }
}abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
34    }
35}
36
37pub(crate) fn gen_for_enum(
38    item: &Item,
39    item_name: &Ident,
40    variants: &[(&Variant, Item)],
41) -> Result<TokenStream, syn::Error> {
42    if !#[allow(non_exhaustive_omitted_patterns)] match &*item.kind() {
    Kind::Value => true,
    _ => false,
}matches!(&*item.kind(), Kind::Value) {
43        {
    return Err({
                #[allow(unused_imports)]
                use crate::utils::error::*;
                let msg =
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("`{0}` cannot be used with `#[value]`",
                                    item.kind().name()))
                        });
                item.kind().span().EXPECTED_Span_OR_ToTokens(msg)
            });
}abort! { item.kind().span(),
44            "`{}` cannot be used with `#[value]`",
45            item.kind().name(),
46        }
47    }
48
49    let lits = lits(variants)?;
50    let value_variants = gen_value_variants(&lits);
51    let to_possible_value = gen_to_possible_value(item, &lits);
52
53    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "dead_code");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "unreachable_code");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "unused_braces");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "unused_qualifications");
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "style");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "complexity");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "pedantic");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "restriction");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "perf");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "deprecated");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "nursery");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "cargo");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "suspicious_else_formatting");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "almost_swapped");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "clippy");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "redundant_locals");
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "automatically_derived");
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::__private::push_ident(&mut _s, "clap");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ValueEnum");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&item_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&value_variants, &mut _s);
            ::quote::ToTokens::to_tokens(&to_possible_value, &mut _s);
            _s
        });
    _s
}quote! {
54        #[allow(
55            dead_code,
56            unreachable_code,
57            unused_variables,
58            unused_braces,
59            unused_qualifications,
60        )]
61        #[allow(
62            clippy::style,
63            clippy::complexity,
64            clippy::pedantic,
65            clippy::restriction,
66            clippy::perf,
67            clippy::deprecated,
68            clippy::nursery,
69            clippy::cargo,
70            clippy::suspicious_else_formatting,
71            clippy::almost_swapped,
72            clippy::redundant_locals,
73        )]
74        #[automatically_derived]
75        impl clap::ValueEnum for #item_name {
76            #value_variants
77            #to_possible_value
78        }
79    })
80}
81
82fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
83    let mut genned = Vec::new();
84    for (variant, item) in variants {
85        if let Kind::Skip(_, _) = &*item.kind() {
86            continue;
87        }
88        if !#[allow(non_exhaustive_omitted_patterns)] match variant.fields {
    Fields::Unit => true,
    _ => false,
}matches!(variant.fields, Fields::Unit) {
89            {
    return Err({
                #[allow(unused_imports)]
                use crate::utils::error::*;
                let msg =
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"))
                        });
                variant.span().EXPECTED_Span_OR_ToTokens(msg)
            });
};abort!(
90                variant.span(),
91                "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"
92            );
93        }
94        let fields = item.field_methods();
95        let deprecations = item.deprecations();
96        let name = item.cased_name();
97        genned.push((
98            {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(variant.span()).__into_span();
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::ToTokens::to_tokens(&deprecations, &mut _s);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "clap");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "builder");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "PossibleValue");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "new");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    _s
                });
            ::quote::ToTokens::to_tokens(&fields, &mut _s);
            _s
        });
    _s
}quote_spanned! { variant.span()=> {
99                #deprecations
100                clap::builder::PossibleValue::new(#name)
101                #fields
102            }},
103            variant.ident.clone(),
104        ));
105    }
106    Ok(genned)
107}
108
109fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
110    let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
111
112    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::__private::push_ident(&mut _s, "value_variants");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'a");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_rarrow(&mut _s);
    ::quote::__private::push_and(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'a");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "Self");
            _s
        });
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut lit, i) = lit.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let lit =
                                match lit.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::__private::push_ident(&mut _s, "Self");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::ToTokens::to_tokens(&lit, &mut _s);
                        }
                    }
                    _s
                });
            _s
        });
    _s
}quote! {
113        fn value_variants<'a>() -> &'a [Self]{
114            &[#(Self::#lit),*]
115        }
116    }
117}
118
119fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
120    let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
121
122    let deprecations = item.deprecations();
123
124    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::__private::push_ident(&mut _s, "to_possible_value");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'a");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_ident(&mut _s, "self");
            _s
        });
    ::quote::__private::push_rarrow(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "std");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "option");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Option");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "clap");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "builder");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "PossibleValue");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&deprecations, &mut _s);
            ::quote::__private::push_ident(&mut _s, "match");
            ::quote::__private::push_ident(&mut _s, "self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut variant, i) = variant.quote_into_iter();
                        let has_iter = has_iter | i;
                        #[allow(unused_mut)]
                        let (mut lit, i) = lit.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let variant =
                                match variant.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            let lit =
                                match lit.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            ::quote::__private::push_ident(&mut _s, "Self");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::ToTokens::to_tokens(&variant, &mut _s);
                            ::quote::__private::push_fat_arrow(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Some");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&lit, &mut _s);
                                    _s
                                });
                            ::quote::__private::push_comma(&mut _s);
                        }
                    }
                    ::quote::__private::push_underscore(&mut _s);
                    ::quote::__private::push_fat_arrow(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "None");
                    _s
                });
            _s
        });
    _s
}quote! {
125        fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
126            #deprecations
127            match self {
128                #(Self::#variant => Some(#lit),)*
129                _ => None
130            }
131        }
132    }
133}