1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2 // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3 // Ana Hobden (@hoverbear) <operator@hoverbear.org>
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.
11 // This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13 // MIT/Apache 2.0 license.
16 attrs
::{Attrs, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING}
,
18 utils
::{inner_type, is_simple_ty, sub_type, Sp, Ty}
,
21 use proc_macro2
::{Ident, Span, TokenStream}
;
22 use proc_macro_error
::{abort, abort_call_site}
;
23 use quote
::{format_ident, quote, quote_spanned}
;
25 punctuated
::Punctuated
, spanned
::Spanned
, token
::Comma
, Attribute
, Data
, DataStruct
,
26 DeriveInput
, Field
, Fields
, Generics
, Type
,
29 pub fn derive_args(input
: &DeriveInput
) -> TokenStream
{
30 let ident
= &input
.ident
;
35 Data
::Struct(DataStruct
{
36 fields
: Fields
::Named(ref fields
),
38 }) => gen_for_struct(ident
, &input
.generics
, &fields
.named
, &input
.attrs
),
39 Data
::Struct(DataStruct
{
45 &Punctuated
::<Field
, Comma
>::new(),
48 _
=> abort_call_site
!("`#[derive(Args)]` only supports non-tuple structs"),
52 pub fn gen_for_struct(
55 fields
: &Punctuated
<Field
, Comma
>,
58 let from_arg_matches
= gen_from_arg_matches_for_struct(struct_name
, generics
, fields
, attrs
);
60 let attrs
= Attrs
::from_struct(
63 Name
::Derived(struct_name
.clone()),
64 Sp
::call_site(DEFAULT_CASING
),
65 Sp
::call_site(DEFAULT_ENV_CASING
),
67 let app_var
= Ident
::new("__clap_app", Span
::call_site());
68 let augmentation
= gen_augment(fields
, &app_var
, &attrs
, false);
69 let augmentation_update
= gen_augment(fields
, &app_var
, &attrs
, true);
71 let (impl_generics
, ty_generics
, where_clause
) = generics
.split_for_impl();
76 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
86 clippy
::suspicious_else_formatting
,
88 #[deny(clippy::correctness)]
89 impl #impl_generics clap::Args for #struct_name #ty_generics #where_clause {
90 fn augment_args
<'b
>(#app_var: clap::Command<'b>) -> clap::Command<'b> {
93 fn augment_args_for_update
<'b
>(#app_var: clap::Command<'b>) -> clap::Command<'b> {
100 pub fn gen_from_arg_matches_for_struct(
103 fields
: &Punctuated
<Field
, Comma
>,
106 let attrs
= Attrs
::from_struct(
109 Name
::Derived(struct_name
.clone()),
110 Sp
::call_site(DEFAULT_CASING
),
111 Sp
::call_site(DEFAULT_ENV_CASING
),
114 let constructor
= gen_constructor(fields
, &attrs
);
115 let updater
= gen_updater(fields
, &attrs
, true);
116 let raw_deprecated
= raw_deprecated();
118 let (impl_generics
, ty_generics
, where_clause
) = generics
.split_for_impl();
121 #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
131 clippy
::suspicious_else_formatting
,
133 #[deny(clippy::correctness)]
134 impl #impl_generics clap::FromArgMatches for #struct_name #ty_generics #where_clause {
135 fn from_arg_matches(__clap_arg_matches
: &clap
::ArgMatches
) -> ::std
::result
::Result
<Self, clap
::Error
> {
136 Self::from_arg_matches_mut(&mut __clap_arg_matches
.clone())
139 fn from_arg_matches_mut(__clap_arg_matches
: &mut clap
::ArgMatches
) -> ::std
::result
::Result
<Self, clap
::Error
> {
141 let v
= #struct_name #constructor;
142 ::std
::result
::Result
::Ok(v
)
145 fn update_from_arg_matches(&mut self, __clap_arg_matches
: &clap
::ArgMatches
) -> ::std
::result
::Result
<(), clap
::Error
> {
146 self.update_from_arg_matches_mut(&mut __clap_arg_matches
.clone())
149 fn update_from_arg_matches_mut(&mut self, __clap_arg_matches
: &mut clap
::ArgMatches
) -> ::std
::result
::Result
<(), clap
::Error
> {
152 ::std
::result
::Result
::Ok(())
158 /// Generate a block of code to add arguments/subcommands corresponding to
159 /// the `fields` to an cmd.
161 fields
: &Punctuated
<Field
, Comma
>,
163 parent_attribute
: &Attrs
,
164 override_required
: bool
,
166 let mut subcmds
= fields
.iter().filter_map(|field
| {
167 let attrs
= Attrs
::from_field(
169 parent_attribute
.casing(),
170 parent_attribute
.env_casing(),
172 let kind
= attrs
.kind();
173 if let Kind
::Subcommand(ty
) = &*kind
{
174 let subcmd_type
= match (**ty
, sub_type(&field
.ty
)) {
175 (Ty
::Option
, Some(sub_type
)) => sub_type
,
178 let required
= if **ty
== Ty
::Option
{
181 quote_spanned
! { kind
.span()=>
183 let #app_var = #app_var.setting(
184 clap
::AppSettings
::SubcommandRequiredElseHelp
189 let span
= field
.span();
190 let ts
= if override_required
{
192 let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands_for_update( #app_var );
196 let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
205 let subcmd
= subcmds
.next().map(|(_
, ts
)| ts
);
206 if let Some((span
, _
)) = subcmds
.next() {
209 "multiple subcommand sets are not allowed, that's the second"
213 let args
= fields
.iter().filter_map(|field
| {
214 let attrs
= Attrs
::from_field(
216 parent_attribute
.casing(),
217 parent_attribute
.env_casing(),
219 let kind
= attrs
.kind();
223 | Kind
::FromGlobal(_
)
224 | Kind
::ExternalSubcommand
=> None
,
227 let old_heading_var
= format_ident
!("__clap_old_heading");
228 let next_help_heading
= attrs
.next_help_heading();
229 let next_display_order
= attrs
.next_display_order();
230 if override_required
{
231 Some(quote_spanned
! { kind
.span()=>
232 let #old_heading_var = #app_var.get_next_help_heading();
233 let #app_var = #app_var #next_help_heading #next_display_order;
234 let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var);
235 let #app_var = #app_var.next_help_heading(#old_heading_var);
238 Some(quote_spanned
! { kind
.span()=>
239 let #old_heading_var = #app_var.get_next_help_heading();
240 let #app_var = #app_var #next_help_heading #next_display_order;
241 let #app_var = <#ty as clap::Args>::augment_args(#app_var);
242 let #app_var = #app_var.next_help_heading(#old_heading_var);
247 let convert_type
= inner_type(&field
.ty
);
249 let parser
= attrs
.parser(&field
.ty
);
251 let value_parser
= attrs
.value_parser(&field
.ty
);
252 let action
= attrs
.action(&field
.ty
);
253 let func
= &parser
.func
;
255 let mut occurrences
= false;
256 let mut flag
= false;
257 let validator
= match *parser
.kind
{
258 _
if attrs
.ignore_parser() || attrs
.is_enum() => quote
!(),
259 ParserKind
::TryFromStr
=> quote_spanned
! { func
.span()=>
262 .map(|_
: #convert_type| ())
265 ParserKind
::TryFromOsStr
=> quote_spanned
! { func
.span()=>
266 .validator_os(|s
| #func(s).map(|_: #convert_type| ()))
268 ParserKind
::FromStr
| ParserKind
::FromOsStr
=> quote
!(),
269 ParserKind
::FromFlag
=> {
273 ParserKind
::FromOccurrences
=> {
278 let parse_deprecation
= match *parser
.kind
{
279 _
if !attrs
.explicit_parser() || cfg
!(not(feature
= "deprecated")) => quote
!(),
280 ParserKind
::FromStr
=> quote_spanned
! { func
.span()=>
281 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]`")]
282 fn parse_from_str() {
286 ParserKind
::TryFromStr
=> quote_spanned
! { func
.span()=>
287 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]`")]
288 fn parse_try_from_str() {
290 parse_try_from_str();
292 ParserKind
::FromOsStr
=> quote_spanned
! { func
.span()=>
293 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser)]` for `PathBuf` or `#[clap(value_parser = ...)]` with a custom `TypedValueParser`")]
294 fn parse_from_os_str() {
298 ParserKind
::TryFromOsStr
=> quote_spanned
! { func
.span()=>
299 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(value_parser = ...)]` with a custom `TypedValueParser`")]
300 fn parse_try_from_os_str() {
302 parse_try_from_os_str();
304 ParserKind
::FromFlag
=> quote_spanned
! { func
.span()=>
305 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(action = ArgAction::SetTrue)]`")]
306 fn parse_from_flag() {
310 ParserKind
::FromOccurrences
=> quote_spanned
! { func
.span()=>
311 #[deprecated(since = "3.2.0", note = "Replaced with `#[clap(action = ArgAction::Count)]` with a field type of `u8`")]
312 fn parse_from_occurrences() {
314 parse_from_occurrences();
318 let value_name
= attrs
.value_name();
319 let possible_values
= if attrs
.is_enum() && !attrs
.ignore_parser() {
320 gen_value_enum_possible_values(convert_type
)
325 let implicit_methods
= match **ty
{
327 quote_spanned
! { ty
.span()=>
329 .value_name(#value_name)
337 Ty
::OptionOption
=> quote_spanned
! { ty
.span()=>
339 .value_name(#value_name)
342 .multiple_values(false)
350 if attrs
.ignore_parser() {
351 if attrs
.is_positional() {
352 quote_spanned
! { ty
.span()=>
354 .value_name(#value_name)
355 .multiple_values(true) // action won't be sufficient for getting multiple
362 quote_spanned
! { ty
.span()=>
364 .value_name(#value_name)
372 quote_spanned
! { ty
.span()=>
374 .value_name(#value_name)
375 .multiple_occurrences(true)
385 if attrs
.ignore_parser() {
386 if attrs
.is_positional() {
387 quote_spanned
! { ty
.span()=>
389 .value_name(#value_name)
390 .multiple_values(true) // action won't be sufficient for getting multiple
397 quote_spanned
! { ty
.span()=>
399 .value_name(#value_name)
407 quote_spanned
! { ty
.span()=>
409 .value_name(#value_name)
410 .multiple_occurrences(true)
419 Ty
::Other
if occurrences
=> quote_spanned
! { ty
.span()=>
420 .multiple_occurrences(true)
423 Ty
::Other
if flag
=> quote_spanned
! { ty
.span()=>
428 let required
= attrs
.find_default_method().is_none() && !override_required
;
429 // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be
430 // set though that won't always be true but this should be good enough,
431 // otherwise we'll report an "arg required" error when unwrapping.
432 let action_value
= action
.args();
433 quote_spanned
! { ty
.span()=>
435 .value_name(#value_name)
436 .required(#required && #action_value.takes_values())
446 let explicit_methods
= attrs
.field_methods(true);
448 Some(quote_spanned
! { field
.span()=>
449 let #app_var = #app_var.arg({
453 let arg
= clap
::Arg
::new(#id)
465 let initial_app_methods
= parent_attribute
.initial_top_level_methods();
466 let final_app_methods
= parent_attribute
.final_top_level_methods();
468 let #app_var = #app_var #initial_app_methods;
471 #app_var #final_app_methods
475 fn gen_value_enum_possible_values(ty
: &Type
) -> TokenStream
{
476 quote_spanned
! { ty
.span()=>
477 .possible_values(<#ty as clap::ValueEnum>::value_variants().iter().filter_map(clap::ValueEnum::to_possible_value))
481 pub fn gen_constructor(fields
: &Punctuated
<Field
, Comma
>, parent_attribute
: &Attrs
) -> TokenStream
{
482 let fields
= fields
.iter().map(|field
| {
483 let attrs
= Attrs
::from_field(
485 parent_attribute
.casing(),
486 parent_attribute
.env_casing(),
488 let field_name
= field
.ident
.as_ref().unwrap();
489 let kind
= attrs
.kind();
490 let arg_matches
= format_ident
!("__clap_arg_matches");
492 Kind
::ExternalSubcommand
=> {
493 abort
! { kind
.span(),
494 "`external_subcommand` can be used only on enum variants"
497 Kind
::Subcommand(ty
) => {
498 let subcmd_type
= match (**ty
, sub_type(&field
.ty
)) {
499 (Ty
::Option
, Some(sub_type
)) => sub_type
,
504 quote_spanned
! { kind
.span()=>
506 if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
507 Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
515 quote_spanned
! { kind
.span()=>
517 <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
524 Kind
::Flatten
=> quote_spanned
! { kind
.span()=>
525 #field_name: clap::FromArgMatches::from_arg_matches_mut(#arg_matches)?
528 Kind
::Skip(val
) => match val
{
529 None
=> quote_spanned
!(kind
.span()=> #field_name: Default::default()),
530 Some(val
) => quote_spanned
!(kind
.span()=> #field_name: (#val).into()),
533 Kind
::Arg(ty
) | Kind
::FromGlobal(ty
) => {
534 gen_parsers(&attrs
, ty
, field_name
, field
, None
)
545 fields
: &Punctuated
<Field
, Comma
>,
546 parent_attribute
: &Attrs
,
549 let fields
= fields
.iter().map(|field
| {
550 let attrs
= Attrs
::from_field(
552 parent_attribute
.casing(),
553 parent_attribute
.env_casing(),
555 let field_name
= field
.ident
.as_ref().unwrap();
556 let kind
= attrs
.kind();
558 let access
= if use_self
{
560 #[allow(non_snake_case)]
561 let #field_name = &mut self.#field_name;
566 let arg_matches
= format_ident
!("__clap_arg_matches");
569 Kind
::ExternalSubcommand
=> {
570 abort
! { kind
.span(),
571 "`external_subcommand` can be used only on enum variants"
574 Kind
::Subcommand(ty
) => {
575 let subcmd_type
= match (**ty
, sub_type(&field
.ty
)) {
576 (Ty
::Option
, Some(sub_type
)) => sub_type
,
580 let updater
= quote_spanned
! { ty
.span()=>
581 <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
584 let updater
= match **ty
{
585 Ty
::Option
=> quote_spanned
! { kind
.span()=>
586 if let Some(#field_name) = #field_name.as_mut() {
589 *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
594 _
=> quote_spanned
! { kind
.span()=>
599 quote_spanned
! { kind
.span()=>
607 Kind
::Flatten
=> quote_spanned
! { kind
.span()=> {
609 clap
::FromArgMatches
::update_from_arg_matches_mut(#field_name, #arg_matches)?;
613 Kind
::Skip(_
) => quote
!(),
615 Kind
::Arg(ty
) | Kind
::FromGlobal(ty
) => gen_parsers(&attrs
, ty
, field_name
, field
, Some(&access
)),
629 update
: Option
<&TokenStream
>,
631 use self::ParserKind
::*;
633 let parser
= attrs
.parser(&field
.ty
);
634 let func
= &parser
.func
;
635 let span
= parser
.kind
.span();
636 let convert_type
= inner_type(&field
.ty
);
638 let mut flag
= false;
639 let mut occurrences
= false;
640 let (get_one
, get_many
, deref
, mut parse
) = match *parser
.kind
{
641 _
if attrs
.ignore_parser() => (
642 quote_spanned
!(span
=> remove_one
::<#convert_type>),
643 quote_spanned
!(span
=> remove_many
::<#convert_type>),
645 quote_spanned
!(func
.span()=> |s
| ::std
::result
::Result
::Ok
::<_
, clap
::Error
>(s
)),
650 quote_spanned
!(span
=> occurrences_of
),
652 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
661 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
666 quote_spanned
!(span
=> get_one
::<String
>),
667 quote_spanned
!(span
=> get_many
::<String
>),
668 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
669 quote_spanned
!(func
.span()=> |s
| ::std
::result
::Result
::Ok
::<_
, clap
::Error
>(#func(s))),
672 quote_spanned
!(span
=> get_one
::<String
>),
673 quote_spanned
!(span
=> get_many
::<String
>),
674 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
675 quote_spanned
!(func
.span()=> |s
| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
678 quote_spanned
!(span
=> get_one
::<::std
::ffi
::OsString
>),
679 quote_spanned
!(span
=> get_many
::<::std
::ffi
::OsString
>),
680 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
681 quote_spanned
!(func
.span()=> |s
| ::std
::result
::Result
::Ok
::<_
, clap
::Error
>(#func(s))),
684 quote_spanned
!(span
=> get_one
::<::std
::ffi
::OsString
>),
685 quote_spanned
!(span
=> get_many
::<::std
::ffi
::OsString
>),
686 quote
!(|s
| ::std
::ops
::Deref
::deref(s
)),
687 quote_spanned
!(func
.span()=> |s
| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
690 if attrs
.is_enum() && !attrs
.ignore_parser() {
691 let ci
= attrs
.ignore_case();
693 parse
= quote_spanned
! { convert_type
.span()=>
694 |s
| <#convert_type as clap::ValueEnum>::from_str(s, #ci).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))
698 // Give this identifier the same hygiene
699 // as the `arg_matches` parameter definition. This
700 // allows us to refer to `arg_matches` within a `quote_spanned` block
701 let arg_matches
= format_ident
!("__clap_arg_matches");
703 let field_value
= match **ty
{
705 quote_spanned
! { ty
.span()=>
706 #arg_matches.#get_one(#id)
713 Ty
::OptionOption
=> quote_spanned
! { ty
.span()=>
714 if #arg_matches.contains_id(#id) {
716 #arg_matches.#get_one(#id)
718 .map(#parse).transpose()?
725 Ty
::OptionVec
=> quote_spanned
! { ty
.span()=>
726 if #arg_matches.contains_id(#id) {
727 Some(#arg_matches.#get_many(#id)
728 .map(|v
| v
.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
730 .unwrap_or_else(Vec
::new
))
737 quote_spanned
! { ty
.span()=>
738 #arg_matches.#get_many(#id)
739 .map(|v
| v
.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
741 .unwrap_or_else(Vec
::new
)
745 Ty
::Other
if occurrences
=> quote_spanned
! { ty
.span()=>
747 #arg_matches.#get_one(#id)
751 Ty
::Other
if flag
=> {
752 if update
.is_some() && is_simple_ty(&field
.ty
, "bool") {
753 quote_spanned
! { ty
.span()=>
754 *#field_name || #arg_matches.is_present(#id)
757 quote_spanned
! { ty
.span()=>
758 #parse(#arg_matches.is_present(#id))
764 quote_spanned
! { ty
.span()=>
765 #arg_matches.#get_one(#id)
767 .ok_or_else(|| clap
::Error
::raw(clap
::ErrorKind
::MissingRequiredArgument
, format
!("The following required argument was not provided: {}", #id)))
773 if let Some(access
) = update
{
774 quote_spanned
! { field
.span()=>
775 if #arg_matches.contains_id(#id) {
777 *#field_name = #field_value
781 quote_spanned
!(field
.span()=> #field_name: #field_value )
785 #[cfg(feature = "raw-deprecated")]
786 pub fn raw_deprecated() -> TokenStream
{
790 #[cfg(not(feature = "raw-deprecated"))]
791 pub fn raw_deprecated() -> TokenStream
{
793 #![allow(deprecated)] // Assuming any deprecation in here will be related to a deprecation in `Args`