Skip to main content

clap_derive/
attr.rs

1use std::iter::FromIterator;
2
3use proc_macro2::TokenStream;
4use quote::ToTokens;
5use quote::quote;
6use syn::spanned::Spanned;
7use syn::{
8    Attribute, Expr, Ident, LitStr, Token, parenthesized,
9    parse::{Parse, ParseStream},
10    punctuated::Punctuated,
11};
12
13use crate::utils::Sp;
14
15#[derive(#[automatically_derived]
impl ::core::clone::Clone for ClapAttr {
    #[inline]
    fn clone(&self) -> ClapAttr {
        ClapAttr {
            kind: ::core::clone::Clone::clone(&self.kind),
            name: ::core::clone::Clone::clone(&self.name),
            magic: ::core::clone::Clone::clone(&self.magic),
            value: ::core::clone::Clone::clone(&self.value),
        }
    }
}Clone)]
16pub(crate) struct ClapAttr {
17    pub(crate) kind: Sp<AttrKind>,
18    pub(crate) name: Ident,
19    pub(crate) magic: Option<MagicAttrName>,
20    pub(crate) value: Option<AttrValue>,
21}
22
23impl ClapAttr {
24    pub(crate) fn parse_all(all_attrs: &[Attribute]) -> Result<Vec<Self>, syn::Error> {
25        let mut parsed = Vec::new();
26        for attr in all_attrs {
27            let kind = if attr.path().is_ident("clap") {
28                Sp::new(AttrKind::Clap, attr.path().span())
29            } else if attr.path().is_ident("structopt") {
30                Sp::new(AttrKind::StructOpt, attr.path().span())
31            } else if attr.path().is_ident("command") {
32                Sp::new(AttrKind::Command, attr.path().span())
33            } else if attr.path().is_ident("group") {
34                Sp::new(AttrKind::Group, attr.path().span())
35            } else if attr.path().is_ident("arg") {
36                Sp::new(AttrKind::Arg, attr.path().span())
37            } else if attr.path().is_ident("value") {
38                Sp::new(AttrKind::Value, attr.path().span())
39            } else {
40                continue;
41            };
42            for mut attr in
43                attr.parse_args_with(Punctuated::<ClapAttr, ::syn::token::CommaToken![,]>::parse_terminated)?
44            {
45                attr.kind = kind;
46                parsed.push(attr);
47            }
48        }
49        Ok(parsed)
50    }
51
52    pub(crate) fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> {
53        self.value
54            .as_ref()
55            .ok_or_else(|| {
    #[allow(unused_imports)]
    use crate::utils::error::*;
    let msg =
        ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("attribute `{0}` requires a value",
                        self.name))
            });
    self.name.EXPECTED_Span_OR_ToTokens(msg)
}format_err!(self.name, "attribute `{}` requires a value", self.name))
56    }
57
58    pub(crate) fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> {
59        let value = self.value_or_abort()?;
60        match value {
61            AttrValue::LitStr(tokens) => Ok(tokens),
62            AttrValue::Expr(_) | AttrValue::Call(_) => {
63                {
    return Err({
                #[allow(unused_imports)]
                use crate::utils::error::*;
                let msg =
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("attribute `{0}` can only accept string literals",
                                    self.name))
                        });
                self.name.EXPECTED_Span_OR_ToTokens(msg)
            });
}abort!(
64                    self.name,
65                    "attribute `{}` can only accept string literals",
66                    self.name
67                )
68            }
69        }
70    }
71}
72
73impl Parse for ClapAttr {
74    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
75        let name: Ident = input.parse()?;
76        let name_str = name.to_string();
77
78        let magic = match name_str.as_str() {
79            "rename_all" => Some(MagicAttrName::RenameAll),
80            "rename_all_env" => Some(MagicAttrName::RenameAllEnv),
81            "skip" => Some(MagicAttrName::Skip),
82            "next_display_order" => Some(MagicAttrName::NextDisplayOrder),
83            "next_help_heading" => Some(MagicAttrName::NextHelpHeading),
84            "default_value_t" => Some(MagicAttrName::DefaultValueT),
85            "default_values_t" => Some(MagicAttrName::DefaultValuesT),
86            "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT),
87            "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT),
88            "long" => Some(MagicAttrName::Long),
89            "short" => Some(MagicAttrName::Short),
90            "value_parser" => Some(MagicAttrName::ValueParser),
91            "action" => Some(MagicAttrName::Action),
92            "env" => Some(MagicAttrName::Env),
93            "flatten" => Some(MagicAttrName::Flatten),
94            "value_enum" => Some(MagicAttrName::ValueEnum),
95            "from_global" => Some(MagicAttrName::FromGlobal),
96            "subcommand" => Some(MagicAttrName::Subcommand),
97            "external_subcommand" => Some(MagicAttrName::ExternalSubcommand),
98            "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment),
99            "about" => Some(MagicAttrName::About),
100            "long_about" => Some(MagicAttrName::LongAbout),
101            "long_help" => Some(MagicAttrName::LongHelp),
102            "author" => Some(MagicAttrName::Author),
103            "version" => Some(MagicAttrName::Version),
104            _ => None,
105        };
106
107        let value = if input.peek(::syn::token::EqToken![=]) {
108            // `name = value` attributes.
109            let assign_token = input.parse::<::syn::token::EqToken![=]>()?; // skip '='
110            if input.peek(LitStr) {
111                let lit: LitStr = input.parse()?;
112                Some(AttrValue::LitStr(lit))
113            } else {
114                match input.parse::<Expr>() {
115                    Ok(expr) => Some(AttrValue::Expr(expr)),
116
117                    Err(_) => {
    return Err({
                #[allow(unused_imports)]
                use crate::utils::error::*;
                let msg =
                    ::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("expected `string literal` or `expression` after `=`"))
                        });
                assign_token.EXPECTED_Span_OR_ToTokens(msg)
            });
}abort! {
118                        assign_token,
119                        "expected `string literal` or `expression` after `=`"
120                    },
121                }
122            }
123        } else if input.peek(syn::token::Paren) {
124            // `name(...)` attributes.
125            let nested;
126            match ::syn::__private::parse_parens(&input) {
    ::syn::__private::Ok(parens) => {
        nested = parens.content;
        _ = nested;
        parens.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};parenthesized!(nested in input);
127
128            let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, ::syn::token::CommaToken![,])?;
129            Some(AttrValue::Call(Vec::from_iter(method_args)))
130        } else {
131            None
132        };
133
134        Ok(Self {
135            kind: Sp::new(AttrKind::Clap, name.span()),
136            name,
137            magic,
138            value,
139        })
140    }
141}
142
143#[derive(#[automatically_derived]
impl ::core::marker::Copy for MagicAttrName { }Copy, #[automatically_derived]
impl ::core::clone::Clone for MagicAttrName {
    #[inline]
    fn clone(&self) -> MagicAttrName { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for MagicAttrName {
    #[inline]
    fn eq(&self, other: &MagicAttrName) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for MagicAttrName {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
144pub(crate) enum MagicAttrName {
145    Short,
146    Long,
147    ValueParser,
148    Action,
149    Env,
150    Flatten,
151    ValueEnum,
152    FromGlobal,
153    Subcommand,
154    VerbatimDocComment,
155    ExternalSubcommand,
156    About,
157    LongAbout,
158    LongHelp,
159    Author,
160    Version,
161    RenameAllEnv,
162    RenameAll,
163    Skip,
164    DefaultValueT,
165    DefaultValuesT,
166    DefaultValueOsT,
167    DefaultValuesOsT,
168    NextDisplayOrder,
169    NextHelpHeading,
170}
171
172#[derive(#[automatically_derived]
#[allow(clippy::large_enum_variant)]
impl ::core::clone::Clone for AttrValue {
    #[inline]
    fn clone(&self) -> AttrValue {
        match self {
            AttrValue::LitStr(__self_0) =>
                AttrValue::LitStr(::core::clone::Clone::clone(__self_0)),
            AttrValue::Expr(__self_0) =>
                AttrValue::Expr(::core::clone::Clone::clone(__self_0)),
            AttrValue::Call(__self_0) =>
                AttrValue::Call(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone)]
173#[allow(clippy::large_enum_variant)]
174pub(crate) enum AttrValue {
175    LitStr(LitStr),
176    Expr(Expr),
177    Call(Vec<Expr>),
178}
179
180impl ToTokens for AttrValue {
181    fn to_tokens(&self, tokens: &mut TokenStream) {
182        match self {
183            Self::LitStr(t) => t.to_tokens(tokens),
184            Self::Expr(t) => t.to_tokens(tokens),
185            Self::Call(t) => {
186                let t = {
    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 t, i) = t.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let t =
                match t.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&t, &mut _s);
        }
    }
    _s
}quote!(#(#t),*);
187                t.to_tokens(tokens);
188            }
189        }
190    }
191}
192
193#[derive(#[automatically_derived]
impl ::core::marker::Copy for AttrKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for AttrKind {
    #[inline]
    fn clone(&self) -> AttrKind { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for AttrKind {
    #[inline]
    fn eq(&self, other: &AttrKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AttrKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
194pub(crate) enum AttrKind {
195    Clap,
196    StructOpt,
197    Command,
198    Group,
199    Arg,
200    Value,
201}
202
203impl AttrKind {
204    pub(crate) fn as_str(&self) -> &'static str {
205        match self {
206            Self::Clap => "clap",
207            Self::StructOpt => "structopt",
208            Self::Command => "command",
209            Self::Group => "group",
210            Self::Arg => "arg",
211            Self::Value => "value",
212        }
213    }
214}