summaryrefslogtreecommitdiffstats
path: root/third_party/rust/clap_derive/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/clap_derive/src
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/clap_derive/src')
-rw-r--r--third_party/rust/clap_derive/src/attr.rs215
-rw-r--r--third_party/rust/clap_derive/src/derives/args.rs763
-rw-r--r--third_party/rust/clap_derive/src/derives/into_app.rs115
-rw-r--r--third_party/rust/clap_derive/src/derives/mod.rs23
-rw-r--r--third_party/rust/clap_derive/src/derives/parser.rs118
-rw-r--r--third_party/rust/clap_derive/src/derives/subcommand.rs672
-rw-r--r--third_party/rust/clap_derive/src/derives/value_enum.rs129
-rw-r--r--third_party/rust/clap_derive/src/dummies.rs99
-rw-r--r--third_party/rust/clap_derive/src/item.rs1468
-rw-r--r--third_party/rust/clap_derive/src/lib.rs116
-rw-r--r--third_party/rust/clap_derive/src/macros.rs21
-rw-r--r--third_party/rust/clap_derive/src/utils/doc_comments.rs126
-rw-r--r--third_party/rust/clap_derive/src/utils/error.rs22
-rw-r--r--third_party/rust/clap_derive/src/utils/mod.rs13
-rw-r--r--third_party/rust/clap_derive/src/utils/spanned.rs89
-rw-r--r--third_party/rust/clap_derive/src/utils/ty.rs165
16 files changed, 4154 insertions, 0 deletions
diff --git a/third_party/rust/clap_derive/src/attr.rs b/third_party/rust/clap_derive/src/attr.rs
new file mode 100644
index 0000000000..3bc9ac0140
--- /dev/null
+++ b/third_party/rust/clap_derive/src/attr.rs
@@ -0,0 +1,215 @@
+use std::iter::FromIterator;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use quote::ToTokens;
+use syn::spanned::Spanned;
+use syn::{
+ parenthesized,
+ parse::{Parse, ParseStream},
+ punctuated::Punctuated,
+ Attribute, Expr, Ident, LitStr, Token,
+};
+
+use crate::utils::Sp;
+
+#[derive(Clone)]
+pub struct ClapAttr {
+ pub kind: Sp<AttrKind>,
+ pub name: Ident,
+ pub magic: Option<MagicAttrName>,
+ pub value: Option<AttrValue>,
+}
+
+impl ClapAttr {
+ pub fn parse_all(all_attrs: &[Attribute]) -> Result<Vec<Self>, syn::Error> {
+ let mut parsed = Vec::new();
+ for attr in all_attrs {
+ let kind = if attr.path().is_ident("clap") {
+ Sp::new(AttrKind::Clap, attr.path().span())
+ } else if attr.path().is_ident("structopt") {
+ Sp::new(AttrKind::StructOpt, attr.path().span())
+ } else if attr.path().is_ident("command") {
+ Sp::new(AttrKind::Command, attr.path().span())
+ } else if attr.path().is_ident("group") {
+ Sp::new(AttrKind::Group, attr.path().span())
+ } else if attr.path().is_ident("arg") {
+ Sp::new(AttrKind::Arg, attr.path().span())
+ } else if attr.path().is_ident("value") {
+ Sp::new(AttrKind::Value, attr.path().span())
+ } else {
+ continue;
+ };
+ for mut attr in
+ attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)?
+ {
+ attr.kind = kind;
+ parsed.push(attr);
+ }
+ }
+ Ok(parsed)
+ }
+
+ pub fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> {
+ self.value
+ .as_ref()
+ .ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name))
+ }
+
+ pub fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> {
+ let value = self.value_or_abort()?;
+ match value {
+ AttrValue::LitStr(tokens) => Ok(tokens),
+ AttrValue::Expr(_) | AttrValue::Call(_) => {
+ abort!(
+ self.name,
+ "attribute `{}` can only accept string literals",
+ self.name
+ )
+ }
+ }
+ }
+}
+
+impl Parse for ClapAttr {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let name: Ident = input.parse()?;
+ let name_str = name.to_string();
+
+ let magic = match name_str.as_str() {
+ "rename_all" => Some(MagicAttrName::RenameAll),
+ "rename_all_env" => Some(MagicAttrName::RenameAllEnv),
+ "skip" => Some(MagicAttrName::Skip),
+ "next_display_order" => Some(MagicAttrName::NextDisplayOrder),
+ "next_help_heading" => Some(MagicAttrName::NextHelpHeading),
+ "default_value_t" => Some(MagicAttrName::DefaultValueT),
+ "default_values_t" => Some(MagicAttrName::DefaultValuesT),
+ "default_value_os_t" => Some(MagicAttrName::DefaultValueOsT),
+ "default_values_os_t" => Some(MagicAttrName::DefaultValuesOsT),
+ "long" => Some(MagicAttrName::Long),
+ "short" => Some(MagicAttrName::Short),
+ "value_parser" => Some(MagicAttrName::ValueParser),
+ "action" => Some(MagicAttrName::Action),
+ "env" => Some(MagicAttrName::Env),
+ "flatten" => Some(MagicAttrName::Flatten),
+ "value_enum" => Some(MagicAttrName::ValueEnum),
+ "from_global" => Some(MagicAttrName::FromGlobal),
+ "subcommand" => Some(MagicAttrName::Subcommand),
+ "external_subcommand" => Some(MagicAttrName::ExternalSubcommand),
+ "verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment),
+ "about" => Some(MagicAttrName::About),
+ "long_about" => Some(MagicAttrName::LongAbout),
+ "long_help" => Some(MagicAttrName::LongHelp),
+ "author" => Some(MagicAttrName::Author),
+ "version" => Some(MagicAttrName::Version),
+ _ => None,
+ };
+
+ let value = if input.peek(Token![=]) {
+ // `name = value` attributes.
+ let assign_token = input.parse::<Token![=]>()?; // skip '='
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ Some(AttrValue::LitStr(lit))
+ } else {
+ match input.parse::<Expr>() {
+ Ok(expr) => Some(AttrValue::Expr(expr)),
+
+ Err(_) => abort! {
+ assign_token,
+ "expected `string literal` or `expression` after `=`"
+ },
+ }
+ }
+ } else if input.peek(syn::token::Paren) {
+ // `name(...)` attributes.
+ let nested;
+ parenthesized!(nested in input);
+
+ let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, Token![,])?;
+ Some(AttrValue::Call(Vec::from_iter(method_args)))
+ } else {
+ None
+ };
+
+ Ok(Self {
+ kind: Sp::new(AttrKind::Clap, name.span()),
+ name,
+ magic,
+ value,
+ })
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum MagicAttrName {
+ Short,
+ Long,
+ ValueParser,
+ Action,
+ Env,
+ Flatten,
+ ValueEnum,
+ FromGlobal,
+ Subcommand,
+ VerbatimDocComment,
+ ExternalSubcommand,
+ About,
+ LongAbout,
+ LongHelp,
+ Author,
+ Version,
+ RenameAllEnv,
+ RenameAll,
+ Skip,
+ DefaultValueT,
+ DefaultValuesT,
+ DefaultValueOsT,
+ DefaultValuesOsT,
+ NextDisplayOrder,
+ NextHelpHeading,
+}
+
+#[derive(Clone)]
+#[allow(clippy::large_enum_variant)]
+pub enum AttrValue {
+ LitStr(LitStr),
+ Expr(Expr),
+ Call(Vec<Expr>),
+}
+
+impl ToTokens for AttrValue {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ Self::LitStr(t) => t.to_tokens(tokens),
+ Self::Expr(t) => t.to_tokens(tokens),
+ Self::Call(t) => {
+ let t = quote!(#(#t),*);
+ t.to_tokens(tokens)
+ }
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum AttrKind {
+ Clap,
+ StructOpt,
+ Command,
+ Group,
+ Arg,
+ Value,
+}
+
+impl AttrKind {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::Clap => "clap",
+ Self::StructOpt => "structopt",
+ Self::Command => "command",
+ Self::Group => "group",
+ Self::Arg => "arg",
+ Self::Value => "value",
+ }
+ }
+}
diff --git a/third_party/rust/clap_derive/src/derives/args.rs b/third_party/rust/clap_derive/src/derives/args.rs
new file mode 100644
index 0000000000..20164ff2e2
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/args.rs
@@ -0,0 +1,763 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+ punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
+ Fields, FieldsNamed, Generics,
+};
+
+use crate::item::{Item, Kind, Name};
+use crate::utils::{inner_type, sub_type, Sp, Ty};
+
+pub fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
+ let ident = &input.ident;
+
+ match input.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref fields),
+ ..
+ }) => {
+ let name = Name::Derived(ident.clone());
+ let item = Item::from_args_struct(input, name)?;
+ let fields = collect_args_fields(&item, fields)?;
+ gen_for_struct(&item, ident, &input.generics, &fields)
+ }
+ Data::Struct(DataStruct {
+ fields: Fields::Unit,
+ ..
+ }) => {
+ let name = Name::Derived(ident.clone());
+ let item = Item::from_args_struct(input, name)?;
+ let fields = Punctuated::<Field, Comma>::new();
+ let fields = fields
+ .iter()
+ .map(|field| {
+ let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
+ Ok((field, item))
+ })
+ .collect::<Result<Vec<_>, syn::Error>>()?;
+ gen_for_struct(&item, ident, &input.generics, &fields)
+ }
+ _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
+ }
+}
+
+pub fn gen_for_struct(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+ fields: &[(&Field, Item)],
+) -> Result<TokenStream, syn::Error> {
+ if !matches!(&*item.kind(), Kind::Command(_)) {
+ abort! { item.kind().span(),
+ "`{}` cannot be used with `command`",
+ item.kind().name(),
+ }
+ }
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let constructor = gen_constructor(fields)?;
+ let updater = gen_updater(fields, true)?;
+ let raw_deprecated = raw_deprecated();
+
+ let app_var = Ident::new("__clap_app", Span::call_site());
+ let augmentation = gen_augment(fields, &app_var, item, false)?;
+ let augmentation_update = gen_augment(fields, &app_var, item, true)?;
+
+ let group_id = if item.skip_group() {
+ quote!(None)
+ } else {
+ let group_id = item.group_id();
+ quote!(Some(clap::Id::from(#group_id)))
+ };
+
+ Ok(quote! {
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
+ fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
+ }
+
+ fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ #raw_deprecated
+ let v = #item_name #constructor;
+ ::std::result::Result::Ok(v)
+ }
+
+ fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+ self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
+ }
+
+ fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+ #raw_deprecated
+ #updater
+ ::std::result::Result::Ok(())
+ }
+ }
+
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::Args for #item_name #ty_generics #where_clause {
+ fn group_id() -> Option<clap::Id> {
+ #group_id
+ }
+ fn augment_args<'b>(#app_var: clap::Command) -> clap::Command {
+ #augmentation
+ }
+ fn augment_args_for_update<'b>(#app_var: clap::Command) -> clap::Command {
+ #augmentation_update
+ }
+ }
+ })
+}
+
+/// Generate a block of code to add arguments/subcommands corresponding to
+/// the `fields` to an cmd.
+pub fn gen_augment(
+ fields: &[(&Field, Item)],
+ app_var: &Ident,
+ parent_item: &Item,
+ override_required: bool,
+) -> Result<TokenStream, syn::Error> {
+ let mut subcommand_specified = false;
+ let mut args = Vec::new();
+ for (field, item) in fields {
+ let kind = item.kind();
+ let genned = match &*kind {
+ Kind::Command(_)
+ | Kind::Value
+ | Kind::Skip(_, _)
+ | Kind::FromGlobal(_)
+ | Kind::ExternalSubcommand => None,
+ Kind::Subcommand(ty) => {
+ if subcommand_specified {
+ abort!(
+ field.span(),
+ "`#[command(subcommand)]` can only be used once per container"
+ );
+ }
+ subcommand_specified = true;
+
+ let subcmd_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+ let implicit_methods = if **ty == Ty::Option {
+ quote!()
+ } else {
+ quote_spanned! { kind.span()=>
+ .subcommand_required(true)
+ .arg_required_else_help(true)
+ }
+ };
+
+ let override_methods = if override_required {
+ quote_spanned! { kind.span()=>
+ .subcommand_required(false)
+ .arg_required_else_help(false)
+ }
+ } else {
+ quote!()
+ };
+
+ Some(quote! {
+ let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
+ let #app_var = #app_var
+ #implicit_methods
+ #override_methods;
+ })
+ }
+ Kind::Flatten(ty) => {
+ let inner_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+
+ let next_help_heading = item.next_help_heading();
+ let next_display_order = item.next_display_order();
+ if override_required {
+ Some(quote_spanned! { kind.span()=>
+ let #app_var = #app_var
+ #next_help_heading
+ #next_display_order;
+ let #app_var = <#inner_type as clap::Args>::augment_args_for_update(#app_var);
+ })
+ } else {
+ Some(quote_spanned! { kind.span()=>
+ let #app_var = #app_var
+ #next_help_heading
+ #next_display_order;
+ let #app_var = <#inner_type as clap::Args>::augment_args(#app_var);
+ })
+ }
+ }
+ Kind::Arg(ty) => {
+ let value_parser = item.value_parser(&field.ty);
+ let action = item.action(&field.ty);
+ let value_name = item.value_name();
+
+ let implicit_methods = match **ty {
+ Ty::Unit => {
+ // Leaving out `value_parser` as it will always fail
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ #action
+ }
+ }
+ Ty::Option => {
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ #value_parser
+ #action
+ }
+ }
+
+ Ty::OptionOption => quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ .num_args(0..=1)
+ #value_parser
+ #action
+ },
+
+ Ty::OptionVec => {
+ if item.is_positional() {
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ .num_args(1..) // action won't be sufficient for getting multiple
+ #value_parser
+ #action
+ }
+ } else {
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ #value_parser
+ #action
+ }
+ }
+ }
+
+ Ty::Vec => {
+ if item.is_positional() {
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ .num_args(1..) // action won't be sufficient for getting multiple
+ #value_parser
+ #action
+ }
+ } else {
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ #value_parser
+ #action
+ }
+ }
+ }
+
+ Ty::VecVec | Ty::OptionVecVec => {
+ quote_spanned! { ty.span() =>
+ .value_name(#value_name)
+ #value_parser
+ #action
+ }
+ }
+
+ Ty::Other => {
+ let required = item.find_default_method().is_none();
+ // `ArgAction::takes_values` is assuming `ArgAction::default_value` will be
+ // set though that won't always be true but this should be good enough,
+ // otherwise we'll report an "arg required" error when unwrapping.
+ let action_value = action.args();
+ quote_spanned! { ty.span()=>
+ .value_name(#value_name)
+ .required(#required && #action_value.takes_values())
+ #value_parser
+ #action
+ }
+ }
+ };
+
+ let id = item.id();
+ let explicit_methods = item.field_methods();
+ let deprecations = if !override_required {
+ item.deprecations()
+ } else {
+ quote!()
+ };
+ let override_methods = if override_required {
+ quote_spanned! { kind.span()=>
+ .required(false)
+ }
+ } else {
+ quote!()
+ };
+
+ Some(quote_spanned! { field.span()=>
+ let #app_var = #app_var.arg({
+ #deprecations
+
+ #[allow(deprecated)]
+ let arg = clap::Arg::new(#id)
+ #implicit_methods;
+
+ let arg = arg
+ #explicit_methods;
+
+ let arg = arg
+ #override_methods;
+
+ arg
+ });
+ })
+ }
+ };
+ args.push(genned);
+ }
+
+ let deprecations = if !override_required {
+ parent_item.deprecations()
+ } else {
+ quote!()
+ };
+ let initial_app_methods = parent_item.initial_top_level_methods();
+ let final_app_methods = parent_item.final_top_level_methods();
+ let group_app_methods = if parent_item.skip_group() {
+ quote!()
+ } else {
+ let group_id = parent_item.group_id();
+ let literal_group_members = fields
+ .iter()
+ .filter_map(|(_field, item)| {
+ let kind = item.kind();
+ if matches!(*kind, Kind::Arg(_)) {
+ Some(item.id())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ let literal_group_members_len = literal_group_members.len();
+ let mut literal_group_members = quote! {{
+ let members: [clap::Id; #literal_group_members_len] = [#( clap::Id::from(#literal_group_members) ),* ];
+ members
+ }};
+ // HACK: Validation isn't ready yet for nested arg groups, so just don't populate the group in
+ // that situation
+ let possible_group_members_len = fields
+ .iter()
+ .filter(|(_field, item)| {
+ let kind = item.kind();
+ matches!(*kind, Kind::Flatten(_))
+ })
+ .count();
+ if 0 < possible_group_members_len {
+ literal_group_members = quote! {{
+ let members: [clap::Id; 0] = [];
+ members
+ }};
+ }
+
+ let group_methods = parent_item.group_methods();
+
+ quote!(
+ .group(
+ clap::ArgGroup::new(#group_id)
+ .multiple(true)
+ #group_methods
+ .args(#literal_group_members)
+ )
+ )
+ };
+ Ok(quote! {{
+ #deprecations
+ let #app_var = #app_var
+ #initial_app_methods
+ #group_app_methods
+ ;
+ #( #args )*
+ #app_var #final_app_methods
+ }})
+}
+
+pub fn gen_constructor(fields: &[(&Field, Item)]) -> Result<TokenStream, syn::Error> {
+ let fields = fields.iter().map(|(field, item)| {
+ let field_name = field.ident.as_ref().unwrap();
+ let kind = item.kind();
+ let arg_matches = format_ident!("__clap_arg_matches");
+ let genned = match &*kind {
+ Kind::Command(_)
+ | Kind::Value
+ | Kind::ExternalSubcommand => {
+ abort! { kind.span(),
+ "`{}` cannot be used with `arg`",
+ kind.name(),
+ }
+ }
+ Kind::Subcommand(ty) => {
+ let subcmd_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+ match **ty {
+ Ty::Option => {
+ quote_spanned! { kind.span()=>
+ #field_name: {
+ if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
+ Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
+ } else {
+ None
+ }
+ }
+ }
+ },
+ Ty::Other => {
+ quote_spanned! { kind.span()=>
+ #field_name: {
+ <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
+ }
+ }
+ },
+ Ty::Unit |
+ Ty::Vec |
+ Ty::OptionOption |
+ Ty::OptionVec |
+ Ty::VecVec |
+ Ty::OptionVecVec => {
+ abort!(
+ ty.span(),
+ "{} types are not supported for subcommand",
+ ty.as_str()
+ );
+ }
+ }
+ }
+
+ Kind::Flatten(ty) => {
+ let inner_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+ match **ty {
+ Ty::Other => {
+ quote_spanned! { kind.span()=>
+ #field_name: <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
+ }
+ },
+ Ty::Option => {
+ quote_spanned! { kind.span()=>
+ #field_name: {
+ let group_id = <#inner_type as clap::Args>::group_id()
+ .expect("`#[arg(flatten)]`ed field type implements `Args::group_id`");
+ if #arg_matches.contains_id(group_id.as_str()) {
+ Some(
+ <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
+ )
+ } else {
+ None
+ }
+ }
+ }
+ },
+ Ty::Unit |
+ Ty::Vec |
+ Ty::OptionOption |
+ Ty::OptionVec |
+ Ty::VecVec |
+ Ty::OptionVecVec => {
+ abort!(
+ ty.span(),
+ "{} types are not supported for flatten",
+ ty.as_str()
+ );
+ }
+ }
+ },
+
+ Kind::Skip(val, _) => match val {
+ None => quote_spanned!(kind.span()=> #field_name: Default::default()),
+ Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
+ },
+
+ Kind::Arg(ty) | Kind::FromGlobal(ty) => {
+ gen_parsers(item, ty, field_name, field, None)?
+ }
+ };
+ Ok(genned)
+ }).collect::<Result<Vec<_>, syn::Error>>()?;
+
+ Ok(quote! {{
+ #( #fields ),*
+ }})
+}
+
+pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> Result<TokenStream, syn::Error> {
+ let mut genned_fields = Vec::new();
+ for (field, item) in fields {
+ let field_name = field.ident.as_ref().unwrap();
+ let kind = item.kind();
+
+ let access = if use_self {
+ quote! {
+ #[allow(non_snake_case)]
+ let #field_name = &mut self.#field_name;
+ }
+ } else {
+ quote!()
+ };
+ let arg_matches = format_ident!("__clap_arg_matches");
+
+ let genned = match &*kind {
+ Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => {
+ abort! { kind.span(),
+ "`{}` cannot be used with `arg`",
+ kind.name(),
+ }
+ }
+ Kind::Subcommand(ty) => {
+ let subcmd_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+
+ let updater = quote_spanned! { ty.span()=>
+ <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
+ };
+
+ let updater = match **ty {
+ Ty::Option => quote_spanned! { kind.span()=>
+ if let Some(#field_name) = #field_name.as_mut() {
+ #updater
+ } else {
+ *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
+ #arg_matches
+ )?);
+ }
+ },
+ _ => quote_spanned! { kind.span()=>
+ #updater
+ },
+ };
+
+ quote_spanned! { kind.span()=>
+ {
+ #access
+ #updater
+ }
+ }
+ }
+
+ Kind::Flatten(ty) => {
+ let inner_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+
+ let updater = quote_spanned! { ty.span()=>
+ <#inner_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
+ };
+
+ let updater = match **ty {
+ Ty::Option => quote_spanned! { kind.span()=>
+ if let Some(#field_name) = #field_name.as_mut() {
+ #updater
+ } else {
+ *#field_name = Some(<#inner_type as clap::FromArgMatches>::from_arg_matches_mut(
+ #arg_matches
+ )?);
+ }
+ },
+ _ => quote_spanned! { kind.span()=>
+ #updater
+ },
+ };
+
+ quote_spanned! { kind.span()=>
+ {
+ #access
+ #updater
+ }
+ }
+ }
+
+ Kind::Skip(_, _) => quote!(),
+
+ Kind::Arg(ty) | Kind::FromGlobal(ty) => {
+ gen_parsers(item, ty, field_name, field, Some(&access))?
+ }
+ };
+ genned_fields.push(genned);
+ }
+
+ Ok(quote! {
+ #( #genned_fields )*
+ })
+}
+
+fn gen_parsers(
+ item: &Item,
+ ty: &Sp<Ty>,
+ field_name: &Ident,
+ field: &Field,
+ update: Option<&TokenStream>,
+) -> Result<TokenStream, syn::Error> {
+ let span = ty.span();
+ let convert_type = inner_type(&field.ty);
+ let id = item.id();
+ let get_one = quote_spanned!(span=> remove_one::<#convert_type>);
+ let get_many = quote_spanned!(span=> remove_many::<#convert_type>);
+ let get_occurrences = quote_spanned!(span=> remove_occurrences::<#convert_type>);
+
+ // Give this identifier the same hygiene
+ // as the `arg_matches` parameter definition. This
+ // allows us to refer to `arg_matches` within a `quote_spanned` block
+ let arg_matches = format_ident!("__clap_arg_matches");
+
+ let field_value = match **ty {
+ Ty::Unit => {
+ quote_spanned! { ty.span()=>
+ ()
+ }
+ }
+
+ Ty::Option => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#get_one(#id)
+ }
+ }
+
+ Ty::OptionOption => quote_spanned! { ty.span()=>
+ if #arg_matches.contains_id(#id) {
+ Some(
+ #arg_matches.#get_one(#id)
+ )
+ } else {
+ None
+ }
+ },
+
+ Ty::OptionVec => quote_spanned! { ty.span()=>
+ if #arg_matches.contains_id(#id) {
+ Some(#arg_matches.#get_many(#id)
+ .map(|v| v.collect::<Vec<_>>())
+ .unwrap_or_else(Vec::new))
+ } else {
+ None
+ }
+ },
+
+ Ty::Vec => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#get_many(#id)
+ .map(|v| v.collect::<Vec<_>>())
+ .unwrap_or_else(Vec::new)
+ }
+ }
+
+ Ty::VecVec => quote_spanned! { ty.span()=>
+ #arg_matches.#get_occurrences(#id)
+ .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
+ .unwrap_or_else(Vec::new)
+ },
+
+ Ty::OptionVecVec => quote_spanned! { ty.span()=>
+ #arg_matches.#get_occurrences(#id)
+ .map(|g| g.map(::std::iter::Iterator::collect).collect::<Vec<Vec<_>>>())
+ },
+
+ Ty::Other => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#get_one(#id)
+ .ok_or_else(|| clap::Error::raw(clap::error::ErrorKind::MissingRequiredArgument, concat!("The following required argument was not provided: ", #id)))?
+ }
+ }
+ };
+
+ let genned = if let Some(access) = update {
+ quote_spanned! { field.span()=>
+ if #arg_matches.contains_id(#id) {
+ #access
+ *#field_name = #field_value
+ }
+ }
+ } else {
+ quote_spanned!(field.span()=> #field_name: #field_value )
+ };
+ Ok(genned)
+}
+
+#[cfg(feature = "raw-deprecated")]
+pub fn raw_deprecated() -> TokenStream {
+ quote! {}
+}
+
+#[cfg(not(feature = "raw-deprecated"))]
+pub fn raw_deprecated() -> TokenStream {
+ quote! {
+ #![allow(deprecated)] // Assuming any deprecation in here will be related to a deprecation in `Args`
+
+ }
+}
+
+pub fn collect_args_fields<'a>(
+ item: &'a Item,
+ fields: &'a FieldsNamed,
+) -> Result<Vec<(&'a Field, Item)>, syn::Error> {
+ fields
+ .named
+ .iter()
+ .map(|field| {
+ let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
+ Ok((field, item))
+ })
+ .collect()
+}
diff --git a/third_party/rust/clap_derive/src/derives/into_app.rs b/third_party/rust/clap_derive/src/derives/into_app.rs
new file mode 100644
index 0000000000..0bd636245a
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/into_app.rs
@@ -0,0 +1,115 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{Generics, Ident};
+
+use crate::item::Item;
+
+pub fn gen_for_struct(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+) -> Result<TokenStream, syn::Error> {
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let name = item.cased_name();
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ let tokens = quote! {
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
+ fn command<'b>() -> clap::Command {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Args>::augment_args(#app_var)
+ }
+
+ fn command_for_update<'b>() -> clap::Command {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Args>::augment_args_for_update(#app_var)
+ }
+ }
+ };
+
+ Ok(tokens)
+}
+
+pub fn gen_for_enum(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+) -> Result<TokenStream, syn::Error> {
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let name = item.cased_name();
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ Ok(quote! {
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
+ fn command<'b>() -> clap::Command {
+ let #app_var = clap::Command::new(#name)
+ .subcommand_required(true)
+ .arg_required_else_help(true);
+ <Self as clap::Subcommand>::augment_subcommands(#app_var)
+ }
+
+ fn command_for_update<'b>() -> clap::Command {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Subcommand>::augment_subcommands_for_update(#app_var)
+ .subcommand_required(false)
+ .arg_required_else_help(false)
+ }
+ }
+ })
+}
diff --git a/third_party/rust/clap_derive/src/derives/mod.rs b/third_party/rust/clap_derive/src/derives/mod.rs
new file mode 100644
index 0000000000..3deeb91f9d
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/mod.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+mod args;
+mod into_app;
+mod parser;
+mod subcommand;
+mod value_enum;
+
+pub use self::parser::derive_parser;
+pub use args::derive_args;
+pub use subcommand::derive_subcommand;
+pub use value_enum::derive_value_enum;
diff --git a/third_party/rust/clap_derive/src/derives/parser.rs b/third_party/rust/clap_derive/src/derives/parser.rs
new file mode 100644
index 0000000000..272948b63a
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/parser.rs
@@ -0,0 +1,118 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::Ident;
+use syn::Variant;
+use syn::{
+ self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
+ Generics,
+};
+
+use crate::derives::args::collect_args_fields;
+use crate::derives::{args, into_app, subcommand};
+use crate::item::Item;
+use crate::item::Name;
+
+pub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
+ let ident = &input.ident;
+ let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
+
+ match input.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref fields),
+ ..
+ }) => {
+ let name = Name::Assigned(quote!(#pkg_name));
+ let item = Item::from_args_struct(input, name)?;
+ let fields = collect_args_fields(&item, fields)?;
+ gen_for_struct(&item, ident, &input.generics, &fields)
+ }
+ Data::Struct(DataStruct {
+ fields: Fields::Unit,
+ ..
+ }) => {
+ let name = Name::Assigned(quote!(#pkg_name));
+ let item = Item::from_args_struct(input, name)?;
+ let fields = Punctuated::<Field, Comma>::new();
+ let fields = fields
+ .iter()
+ .map(|field| {
+ let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
+ Ok((field, item))
+ })
+ .collect::<Result<Vec<_>, syn::Error>>()?;
+ gen_for_struct(&item, ident, &input.generics, &fields)
+ }
+ Data::Enum(ref e) => {
+ let name = Name::Assigned(quote!(#pkg_name));
+ let item = Item::from_subcommand_enum(input, name)?;
+ let variants = e
+ .variants
+ .iter()
+ .map(|variant| {
+ let item =
+ Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
+ Ok((variant, item))
+ })
+ .collect::<Result<Vec<_>, syn::Error>>()?;
+ gen_for_enum(&item, ident, &input.generics, &variants)
+ }
+ _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
+ }
+}
+
+fn gen_for_struct(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+ fields: &[(&Field, Item)],
+) -> Result<TokenStream, syn::Error> {
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let into_app = into_app::gen_for_struct(item, item_name, generics)?;
+ let args = args::gen_for_struct(item, item_name, generics, fields)?;
+
+ Ok(quote! {
+ #[automatically_derived]
+ #[allow(
+ unused_qualifications,
+ )]
+ impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
+
+ #into_app
+ #args
+ })
+}
+
+fn gen_for_enum(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+ variants: &[(&Variant, Item)],
+) -> Result<TokenStream, syn::Error> {
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let into_app = into_app::gen_for_enum(item, item_name, generics)?;
+ let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?;
+
+ Ok(quote! {
+ #[automatically_derived]
+ impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
+
+ #into_app
+ #subcommand
+ })
+}
diff --git a/third_party/rust/clap_derive/src/derives/subcommand.rs b/third_party/rust/clap_derive/src/derives/subcommand.rs
new file mode 100644
index 0000000000..125a40f610
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/subcommand.rs
@@ -0,0 +1,672 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
+
+use crate::derives::args;
+use crate::derives::args::collect_args_fields;
+use crate::item::{Item, Kind, Name};
+use crate::utils::{is_simple_ty, subty_if_name};
+
+pub fn derive_subcommand(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
+ let ident = &input.ident;
+
+ match input.data {
+ Data::Enum(ref e) => {
+ let name = Name::Derived(ident.clone());
+ let item = Item::from_subcommand_enum(input, name)?;
+ let variants = e
+ .variants
+ .iter()
+ .map(|variant| {
+ let item =
+ Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
+ Ok((variant, item))
+ })
+ .collect::<Result<Vec<_>, syn::Error>>()?;
+ gen_for_enum(&item, ident, &input.generics, &variants)
+ }
+ _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
+ }
+}
+
+pub fn gen_for_enum(
+ item: &Item,
+ item_name: &Ident,
+ generics: &Generics,
+ variants: &[(&Variant, Item)],
+) -> Result<TokenStream, syn::Error> {
+ if !matches!(&*item.kind(), Kind::Command(_)) {
+ abort! { item.kind().span(),
+ "`{}` cannot be used with `command`",
+ item.kind().name(),
+ }
+ }
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let from_arg_matches = gen_from_arg_matches(variants)?;
+ let update_from_arg_matches = gen_update_from_arg_matches(variants)?;
+
+ let augmentation = gen_augment(variants, item, false)?;
+ let augmentation_update = gen_augment(variants, item, true)?;
+ let has_subcommand = gen_has_subcommand(variants)?;
+
+ Ok(quote! {
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
+ fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
+ }
+
+ #from_arg_matches
+
+ fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+ self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
+ }
+ #update_from_arg_matches
+ }
+
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause {
+ fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command {
+ #augmentation
+ }
+ fn augment_subcommands_for_update <'b>(__clap_app: clap::Command) -> clap::Command {
+ #augmentation_update
+ }
+ fn has_subcommand(__clap_name: &str) -> bool {
+ #has_subcommand
+ }
+ }
+ })
+}
+
+fn gen_augment(
+ variants: &[(&Variant, Item)],
+ parent_item: &Item,
+ override_required: bool,
+) -> Result<TokenStream, syn::Error> {
+ use syn::Fields::*;
+
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ let mut subcommands = Vec::new();
+ for (variant, item) in variants {
+ let kind = item.kind();
+
+ let genned = match &*kind {
+ Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
+
+ Kind::ExternalSubcommand => {
+ let ty = match variant.fields {
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
+
+ _ => abort!(
+ variant,
+ "The enum variant marked with `external_subcommand` must be \
+ a single-typed tuple, and the type must be either `Vec<String>` \
+ or `Vec<OsString>`."
+ ),
+ };
+ let deprecations = if !override_required {
+ item.deprecations()
+ } else {
+ quote!()
+ };
+ let subty = subty_if_name(ty, "Vec").ok_or_else(|| {
+ format_err!(
+ ty.span(),
+ "The type must be `Vec<_>` \
+ to be used with `external_subcommand`."
+ )
+ })?;
+ let subcommand = quote_spanned! { kind.span()=>
+ #deprecations
+ let #app_var = #app_var
+ .external_subcommand_value_parser(clap::value_parser!(#subty));
+ };
+ Some(subcommand)
+ }
+
+ Kind::Flatten(_) => match variant.fields {
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ let ty = &unnamed[0].ty;
+ let deprecations = if !override_required {
+ item.deprecations()
+ } else {
+ quote!()
+ };
+ let next_help_heading = item.next_help_heading();
+ let next_display_order = item.next_display_order();
+ let subcommand = if override_required {
+ quote! {
+ #deprecations
+ let #app_var = #app_var
+ #next_help_heading
+ #next_display_order;
+ let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
+ }
+ } else {
+ quote! {
+ #deprecations
+ let #app_var = #app_var
+ #next_help_heading
+ #next_display_order;
+ let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
+ }
+ };
+ Some(subcommand)
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ },
+
+ Kind::Subcommand(_) => {
+ let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
+ let arg_block = match variant.fields {
+ Named(_) => {
+ abort!(variant, "non single-typed tuple enums are not supported")
+ }
+ Unit => quote!( #subcommand_var ),
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ let ty = &unnamed[0].ty;
+ if override_required {
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
+ }
+ }
+ } else {
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
+ }
+ }
+ }
+ }
+ Unnamed(..) => {
+ abort!(variant, "non single-typed tuple enums are not supported")
+ }
+ };
+
+ let name = item.cased_name();
+ let deprecations = if !override_required {
+ item.deprecations()
+ } else {
+ quote!()
+ };
+ let initial_app_methods = item.initial_top_level_methods();
+ let final_from_attrs = item.final_top_level_methods();
+ let override_methods = if override_required {
+ quote_spanned! { kind.span()=>
+ .subcommand_required(false)
+ .arg_required_else_help(false)
+ }
+ } else {
+ quote!()
+ };
+ let subcommand = quote! {
+ let #app_var = #app_var.subcommand({
+ #deprecations;
+ let #subcommand_var = clap::Command::new(#name);
+ let #subcommand_var = #subcommand_var
+ .subcommand_required(true)
+ .arg_required_else_help(true);
+ let #subcommand_var = #subcommand_var #initial_app_methods;
+ let #subcommand_var = #arg_block;
+ #subcommand_var #final_from_attrs #override_methods
+ });
+ };
+ Some(subcommand)
+ }
+
+ Kind::Command(_) => {
+ let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
+ let sub_augment = match variant.fields {
+ Named(ref fields) => {
+ // Defer to `gen_augment` for adding cmd methods
+ let fields = collect_args_fields(item, fields)?;
+ args::gen_augment(&fields, &subcommand_var, item, override_required)?
+ }
+ Unit => {
+ let arg_block = quote!( #subcommand_var );
+ let initial_app_methods = item.initial_top_level_methods();
+ let final_from_attrs = item.final_top_level_methods();
+ quote! {
+ let #subcommand_var = #subcommand_var #initial_app_methods;
+ let #subcommand_var = #arg_block;
+ #subcommand_var #final_from_attrs
+ }
+ }
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ let ty = &unnamed[0].ty;
+ let arg_block = if override_required {
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Args>::augment_args_for_update(#subcommand_var)
+ }
+ }
+ } else {
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Args>::augment_args(#subcommand_var)
+ }
+ }
+ };
+ let initial_app_methods = item.initial_top_level_methods();
+ let final_from_attrs = item.final_top_level_methods();
+ quote! {
+ let #subcommand_var = #subcommand_var #initial_app_methods;
+ let #subcommand_var = #arg_block;
+ #subcommand_var #final_from_attrs
+ }
+ }
+ Unnamed(..) => {
+ abort!(variant, "non single-typed tuple enums are not supported")
+ }
+ };
+
+ let deprecations = if !override_required {
+ item.deprecations()
+ } else {
+ quote!()
+ };
+ let name = item.cased_name();
+ let subcommand = quote! {
+ let #app_var = #app_var.subcommand({
+ #deprecations
+ let #subcommand_var = clap::Command::new(#name);
+ #sub_augment
+ });
+ };
+ Some(subcommand)
+ }
+ };
+ subcommands.push(genned);
+ }
+
+ let deprecations = if !override_required {
+ parent_item.deprecations()
+ } else {
+ quote!()
+ };
+ let initial_app_methods = parent_item.initial_top_level_methods();
+ let final_app_methods = parent_item.final_top_level_methods();
+ Ok(quote! {
+ #deprecations;
+ let #app_var = #app_var #initial_app_methods;
+ #( #subcommands )*;
+ #app_var #final_app_methods
+ })
+}
+
+fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
+ use syn::Fields::*;
+
+ let mut ext_subcmd = false;
+
+ let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
+ .iter()
+ .filter_map(|(variant, item)| {
+ let kind = item.kind();
+ match &*kind {
+ Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
+
+ Kind::ExternalSubcommand => {
+ ext_subcmd = true;
+ None
+ }
+ Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
+ }
+ })
+ .partition(|(_, item)| {
+ let kind = item.kind();
+ matches!(&*kind, Kind::Flatten(_))
+ });
+
+ let subcommands = variants.iter().map(|(_variant, item)| {
+ let sub_name = item.cased_name();
+ quote! {
+ if #sub_name == __clap_name {
+ return true
+ }
+ }
+ });
+ let child_subcommands = flatten_variants
+ .iter()
+ .map(|(variant, _attrs)| match variant.fields {
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0].ty;
+ Ok(quote! {
+ if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
+ return true;
+ }
+ })
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ })
+ .collect::<Result<Vec<_>, syn::Error>>()?;
+
+ let genned = if ext_subcmd {
+ quote! { true }
+ } else {
+ quote! {
+ #( #subcommands )*
+
+ #( #child_subcommands )else*
+
+ false
+ }
+ };
+ Ok(genned)
+}
+
+fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
+ use syn::Fields::*;
+
+ let subcommand_name_var = format_ident!("__clap_name");
+ let sub_arg_matches_var = format_ident!("__clap_arg_matches");
+
+ let mut ext_subcmd = None;
+ let mut flatten_variants = Vec::new();
+ let mut unflatten_variants = Vec::new();
+ for (variant, item) in variants {
+ let kind = item.kind();
+ match &*kind {
+ Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {}
+
+ Kind::ExternalSubcommand => {
+ if ext_subcmd.is_some() {
+ abort!(
+ item.kind().span(),
+ "Only one variant can be marked with `external_subcommand`, \
+ this is the second"
+ );
+ }
+
+ let ty = match variant.fields {
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
+
+ _ => abort!(
+ variant,
+ "The enum variant marked with `external_subcommand` must be \
+ a single-typed tuple, and the type must be either `Vec<String>` \
+ or `Vec<OsString>`."
+ ),
+ };
+
+ let (span, str_ty) = match subty_if_name(ty, "Vec") {
+ Some(subty) => {
+ if is_simple_ty(subty, "String") {
+ (subty.span(), quote!(::std::string::String))
+ } else if is_simple_ty(subty, "OsString") {
+ (subty.span(), quote!(::std::ffi::OsString))
+ } else {
+ abort!(
+ ty.span(),
+ "The type must be either `Vec<String>` or `Vec<OsString>` \
+ to be used with `external_subcommand`."
+ );
+ }
+ }
+
+ None => abort!(
+ ty.span(),
+ "The type must be either `Vec<String>` or `Vec<OsString>` \
+ to be used with `external_subcommand`."
+ ),
+ };
+
+ ext_subcmd = Some((span, &variant.ident, str_ty));
+ }
+ Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => {
+ if matches!(&*item.kind(), Kind::Flatten(_)) {
+ flatten_variants.push((variant, item));
+ } else {
+ unflatten_variants.push((variant, item));
+ }
+ }
+ }
+ }
+
+ let subcommands = unflatten_variants.iter().map(|(variant, item)| {
+ let sub_name = item.cased_name();
+ let variant_name = &variant.ident;
+ let constructor_block = match variant.fields {
+ Named(ref fields) => {
+ let fields = collect_args_fields(item, fields)?;
+ args::gen_constructor(&fields)?
+ },
+ Unit => quote!(),
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0].ty;
+ quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) )
+ }
+ Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
+ };
+
+ Ok(quote! {
+ if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") {
+ return ::std::result::Result::Ok(Self :: #variant_name #constructor_block)
+ }
+ })
+ }).collect::<Result<Vec<_>, syn::Error>>()?;
+ let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
+ let variant_name = &variant.ident;
+ match variant.fields {
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0].ty;
+ Ok(quote! {
+ if __clap_arg_matches
+ .subcommand_name()
+ .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
+ .unwrap_or_default()
+ {
+ let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
+ return ::std::result::Result::Ok(Self :: #variant_name (__clap_res));
+ }
+ })
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ }
+ }).collect::<Result<Vec<_>, syn::Error>>()?;
+
+ let wildcard = match ext_subcmd {
+ Some((span, var_name, str_ty)) => quote_spanned! { span=>
+ ::std::result::Result::Ok(Self::#var_name(
+ ::std::iter::once(#str_ty::from(#subcommand_name_var))
+ .chain(
+ #sub_arg_matches_var
+ .remove_many::<#str_ty>("")
+ .unwrap()
+ .map(#str_ty::from)
+ )
+ .collect::<::std::vec::Vec<_>>()
+ ))
+ },
+
+ None => quote! {
+ ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::InvalidSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var)))
+ },
+ };
+
+ let raw_deprecated = args::raw_deprecated();
+ Ok(quote! {
+ fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ #raw_deprecated
+
+ #( #child_subcommands )else*
+
+ if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() {
+ let #sub_arg_matches_var = &mut __clap_arg_sub_matches;
+ #( #subcommands )*
+
+ #wildcard
+ } else {
+ ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
+ }
+ }
+ })
+}
+
+fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
+ use syn::Fields::*;
+
+ let (flatten, variants): (Vec<_>, Vec<_>) = variants
+ .iter()
+ .filter_map(|(variant, item)| {
+ let kind = item.kind();
+ match &*kind {
+ // Fallback to `from_arg_matches_mut`
+ Kind::Skip(_, _)
+ | Kind::Arg(_)
+ | Kind::FromGlobal(_)
+ | Kind::Value
+ | Kind::ExternalSubcommand => None,
+ Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
+ }
+ })
+ .partition(|(_, item)| {
+ let kind = item.kind();
+ matches!(&*kind, Kind::Flatten(_))
+ });
+
+ let subcommands = variants.iter().map(|(variant, item)| {
+ let sub_name = item.cased_name();
+ let variant_name = &variant.ident;
+ let (pattern, updater) = match variant.fields {
+ Named(ref fields) => {
+ let field_names = fields.named.iter().map(|field| {
+ field.ident.as_ref().unwrap()
+ }).collect::<Vec<_>>();
+ let fields = collect_args_fields(item, fields)?;
+ let update = args::gen_updater(&fields, false)?;
+ (quote!( { #( #field_names, )* }), quote!( { #update } ))
+ }
+ Unit => (quote!(), quote!({})),
+ Unnamed(ref fields) => {
+ if fields.unnamed.len() == 1 {
+ (
+ quote!((ref mut __clap_arg)),
+ quote!(clap::FromArgMatches::update_from_arg_matches_mut(
+ __clap_arg,
+ __clap_arg_matches
+ )?),
+ )
+ } else {
+ abort_call_site!("{}: tuple enums are not supported", variant.ident)
+ }
+ }
+ };
+
+ Ok(quote! {
+ Self :: #variant_name #pattern if #sub_name == __clap_name => {
+ let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap();
+ let __clap_arg_matches = &mut __clap_arg_sub_matches;
+ #updater
+ }
+ })
+ }).collect::<Result<Vec<_>, _>>()?;
+
+ let child_subcommands = flatten.iter().map(|(variant, _attrs)| {
+ let variant_name = &variant.ident;
+ match variant.fields {
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0].ty;
+ Ok(quote! {
+ if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
+ if let Self :: #variant_name (child) = s {
+ <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
+ return ::std::result::Result::Ok(());
+ }
+ }
+ })
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ }
+ }).collect::<Result<Vec<_>, _>>()?;
+
+ let raw_deprecated = args::raw_deprecated();
+ Ok(quote! {
+ fn update_from_arg_matches_mut<'b>(
+ &mut self,
+ __clap_arg_matches: &mut clap::ArgMatches,
+ ) -> ::std::result::Result<(), clap::Error> {
+ #raw_deprecated
+
+ if let Some(__clap_name) = __clap_arg_matches.subcommand_name() {
+ match self {
+ #( #subcommands ),*
+ s => {
+ #( #child_subcommands )*
+ *s = <Self as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
+ }
+ }
+ }
+ ::std::result::Result::Ok(())
+ }
+ })
+}
diff --git a/third_party/rust/clap_derive/src/derives/value_enum.rs b/third_party/rust/clap_derive/src/derives/value_enum.rs
new file mode 100644
index 0000000000..397eb33238
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/value_enum.rs
@@ -0,0 +1,129 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use quote::quote_spanned;
+use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
+
+use crate::item::{Item, Kind, Name};
+
+pub fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
+ let ident = &input.ident;
+
+ match input.data {
+ Data::Enum(ref e) => {
+ let name = Name::Derived(ident.clone());
+ let item = Item::from_value_enum(input, name)?;
+ let mut variants = Vec::new();
+ for variant in &e.variants {
+ let item =
+ Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?;
+ variants.push((variant, item));
+ }
+ gen_for_enum(&item, ident, &variants)
+ }
+ _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
+ }
+}
+
+pub fn gen_for_enum(
+ item: &Item,
+ item_name: &Ident,
+ variants: &[(&Variant, Item)],
+) -> Result<TokenStream, syn::Error> {
+ if !matches!(&*item.kind(), Kind::Value) {
+ abort! { item.kind().span(),
+ "`{}` cannot be used with `value`",
+ item.kind().name(),
+ }
+ }
+
+ let lits = lits(variants)?;
+ let value_variants = gen_value_variants(&lits);
+ let to_possible_value = gen_to_possible_value(item, &lits);
+
+ Ok(quote! {
+ #[allow(
+ dead_code,
+ unreachable_code,
+ unused_variables,
+ unused_braces,
+ unused_qualifications,
+ )]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ clippy::almost_swapped,
+ )]
+ #[automatically_derived]
+ impl clap::ValueEnum for #item_name {
+ #value_variants
+ #to_possible_value
+ }
+ })
+}
+
+fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
+ let mut genned = Vec::new();
+ for (variant, item) in variants {
+ if let Kind::Skip(_, _) = &*item.kind() {
+ continue;
+ }
+ if !matches!(variant.fields, Fields::Unit) {
+ abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
+ }
+ let fields = item.field_methods();
+ let deprecations = item.deprecations();
+ let name = item.cased_name();
+ genned.push((
+ quote_spanned! { variant.span()=> {
+ #deprecations
+ clap::builder::PossibleValue::new(#name)
+ #fields
+ }},
+ variant.ident.clone(),
+ ));
+ }
+ Ok(genned)
+}
+
+fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
+ let lit = lits.iter().map(|l| &l.1).collect::<Vec<_>>();
+
+ quote! {
+ fn value_variants<'a>() -> &'a [Self]{
+ &[#(Self::#lit),*]
+ }
+ }
+}
+
+fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
+ let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
+
+ let deprecations = item.deprecations();
+
+ quote! {
+ fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
+ #deprecations
+ match self {
+ #(Self::#variant => Some(#lit),)*
+ _ => None
+ }
+ }
+ }
+}
diff --git a/third_party/rust/clap_derive/src/dummies.rs b/third_party/rust/clap_derive/src/dummies.rs
new file mode 100644
index 0000000000..3a1581b23b
--- /dev/null
+++ b/third_party/rust/clap_derive/src/dummies.rs
@@ -0,0 +1,99 @@
+//! Dummy implementations that we emit along with an error.
+
+use proc_macro2::Ident;
+use quote::quote;
+
+#[must_use]
+pub fn parser(name: &Ident) -> proc_macro2::TokenStream {
+ let into_app = into_app(name);
+ quote!(
+ #[automatically_derived]
+ impl clap::Parser for #name {}
+ #into_app
+ )
+}
+
+#[must_use]
+pub fn into_app(name: &Ident) -> proc_macro2::TokenStream {
+ quote! {
+ #[automatically_derived]
+ impl clap::CommandFactory for #name {
+ fn command<'b>() -> clap::Command {
+ unimplemented!()
+ }
+ fn command_for_update<'b>() -> clap::Command {
+ unimplemented!()
+ }
+ }
+ }
+}
+
+#[must_use]
+pub fn from_arg_matches(name: &Ident) -> proc_macro2::TokenStream {
+ quote! {
+ #[automatically_derived]
+ impl clap::FromArgMatches for #name {
+ fn from_arg_matches(_m: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ unimplemented!()
+ }
+ fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error>{
+ unimplemented!()
+ }
+ }
+ }
+}
+
+#[must_use]
+pub fn subcommand(name: &Ident) -> proc_macro2::TokenStream {
+ let from_arg_matches = from_arg_matches(name);
+ quote! {
+ #[automatically_derived]
+ impl clap::Subcommand for #name {
+ fn augment_subcommands(_cmd: clap::Command) -> clap::Command {
+ unimplemented!()
+ }
+ fn augment_subcommands_for_update(_cmd: clap::Command) -> clap::Command {
+ unimplemented!()
+ }
+ fn has_subcommand(name: &str) -> bool {
+ unimplemented!()
+ }
+ }
+ #from_arg_matches
+ }
+}
+
+#[must_use]
+pub fn args(name: &Ident) -> proc_macro2::TokenStream {
+ let from_arg_matches = from_arg_matches(name);
+ quote! {
+ #[automatically_derived]
+ impl clap::Args for #name {
+ fn augment_args(_cmd: clap::Command) -> clap::Command {
+ unimplemented!()
+ }
+ fn augment_args_for_update(_cmd: clap::Command) -> clap::Command {
+ unimplemented!()
+ }
+ }
+ #from_arg_matches
+ }
+}
+
+#[must_use]
+pub fn value_enum(name: &Ident) -> proc_macro2::TokenStream {
+ quote! {
+ #[automatically_derived]
+ impl clap::ValueEnum for #name {
+ fn value_variants<'a>() -> &'a [Self]{
+ unimplemented!()
+ }
+ fn from_str(_input: &str, _ignore_case: bool) -> ::std::result::Result<Self, String> {
+ unimplemented!()
+ }
+ fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue>{
+ unimplemented!()
+ }
+ }
+ }
+}
diff --git a/third_party/rust/clap_derive/src/item.rs b/third_party/rust/clap_derive/src/item.rs
new file mode 100644
index 0000000000..114849f695
--- /dev/null
+++ b/third_party/rust/clap_derive/src/item.rs
@@ -0,0 +1,1468 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+use std::env;
+
+use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use proc_macro2::{self, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned, ToTokens};
+use syn::DeriveInput;
+use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
+
+use crate::attr::*;
+use crate::utils::{extract_doc_comment, format_doc_comment, inner_type, is_simple_ty, Sp, Ty};
+
+/// Default casing style for generated arguments.
+pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
+
+/// Default casing style for environment variables
+pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
+
+#[derive(Clone)]
+pub struct Item {
+ name: Name,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ty: Option<Type>,
+ doc_comment: Vec<Method>,
+ methods: Vec<Method>,
+ deprecations: Vec<Deprecation>,
+ value_parser: Option<ValueParser>,
+ action: Option<Action>,
+ verbatim_doc_comment: bool,
+ force_long_help: bool,
+ next_display_order: Option<Method>,
+ next_help_heading: Option<Method>,
+ is_enum: bool,
+ is_positional: bool,
+ skip_group: bool,
+ group_id: Name,
+ group_methods: Vec<Method>,
+ kind: Sp<Kind>,
+}
+
+impl Item {
+ pub fn from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
+ let ident = input.ident.clone();
+ let span = input.ident.span();
+ let attrs = &input.attrs;
+ let argument_casing = Sp::new(DEFAULT_CASING, span);
+ let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
+ let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
+
+ let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
+ let parsed_attrs = ClapAttr::parse_all(attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ res.push_doc_comment(attrs, "about", Some("long_about"));
+
+ Ok(res)
+ }
+
+ pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
+ let ident = input.ident.clone();
+ let span = input.ident.span();
+ let attrs = &input.attrs;
+ let argument_casing = Sp::new(DEFAULT_CASING, span);
+ let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
+ let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
+
+ let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
+ let parsed_attrs = ClapAttr::parse_all(attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ res.push_doc_comment(attrs, "about", Some("long_about"));
+
+ Ok(res)
+ }
+
+ pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
+ let ident = input.ident.clone();
+ let span = input.ident.span();
+ let attrs = &input.attrs;
+ let argument_casing = Sp::new(DEFAULT_CASING, span);
+ let env_casing = Sp::new(DEFAULT_ENV_CASING, span);
+ let kind = Sp::new(Kind::Value, span);
+
+ let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
+ let parsed_attrs = ClapAttr::parse_all(attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
+ // to
+
+ if res.has_explicit_methods() {
+ abort!(
+ res.methods[0].name.span(),
+ "{} doesn't exist for `ValueEnum` enums",
+ res.methods[0].name
+ );
+ }
+
+ Ok(res)
+ }
+
+ pub fn from_subcommand_variant(
+ variant: &Variant,
+ struct_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Result<Self, syn::Error> {
+ let name = variant.ident.clone();
+ let ident = variant.ident.clone();
+ let span = variant.span();
+ let ty = match variant.fields {
+ syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ Ty::from_syn_ty(&unnamed[0].ty)
+ }
+ syn::Fields::Named(_) | syn::Fields::Unnamed(..) | syn::Fields::Unit => {
+ Sp::new(Ty::Other, span)
+ }
+ };
+ let kind = Sp::new(Kind::Command(ty), span);
+ let mut res = Self::new(
+ Name::Derived(name),
+ ident,
+ None,
+ struct_casing,
+ env_casing,
+ kind,
+ );
+ let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
+ res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
+ }
+
+ match &*res.kind {
+ Kind::Flatten(_) => {
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods are not allowed for flattened entry"
+ );
+ }
+ }
+
+ Kind::Subcommand(_)
+ | Kind::ExternalSubcommand
+ | Kind::FromGlobal(_)
+ | Kind::Skip(_, _)
+ | Kind::Command(_)
+ | Kind::Value
+ | Kind::Arg(_) => (),
+ }
+
+ Ok(res)
+ }
+
+ pub fn from_value_enum_variant(
+ variant: &Variant,
+ argument_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Result<Self, syn::Error> {
+ let ident = variant.ident.clone();
+ let span = variant.span();
+ let kind = Sp::new(Kind::Value, span);
+ let mut res = Self::new(
+ Name::Derived(variant.ident.clone()),
+ ident,
+ None,
+ argument_casing,
+ env_casing,
+ kind,
+ );
+ let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ if matches!(&*res.kind, Kind::Value) {
+ res.push_doc_comment(&variant.attrs, "help", None);
+ }
+
+ Ok(res)
+ }
+
+ pub fn from_args_field(
+ field: &Field,
+ struct_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Result<Self, syn::Error> {
+ let name = field.ident.clone().unwrap();
+ let ident = field.ident.clone().unwrap();
+ let span = field.span();
+ let ty = Ty::from_syn_ty(&field.ty);
+ let kind = Sp::new(Kind::Arg(ty), span);
+ let mut res = Self::new(
+ Name::Derived(name),
+ ident,
+ Some(field.ty.clone()),
+ struct_casing,
+ env_casing,
+ kind,
+ );
+ let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
+ res.infer_kind(&parsed_attrs)?;
+ res.push_attrs(&parsed_attrs)?;
+ if matches!(&*res.kind, Kind::Arg(_)) {
+ res.push_doc_comment(&field.attrs, "help", Some("long_help"));
+ }
+
+ match &*res.kind {
+ Kind::Flatten(_) => {
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods are not allowed for flattened entry"
+ );
+ }
+ }
+
+ Kind::Subcommand(_) => {
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods in attributes are not allowed for subcommand"
+ );
+ }
+ }
+ Kind::Skip(_, _)
+ | Kind::FromGlobal(_)
+ | Kind::Arg(_)
+ | Kind::Command(_)
+ | Kind::Value
+ | Kind::ExternalSubcommand => {}
+ }
+
+ Ok(res)
+ }
+
+ fn new(
+ name: Name,
+ ident: Ident,
+ ty: Option<Type>,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ kind: Sp<Kind>,
+ ) -> Self {
+ let group_id = Name::Derived(ident);
+ Self {
+ name,
+ ty,
+ casing,
+ env_casing,
+ doc_comment: vec![],
+ methods: vec![],
+ deprecations: vec![],
+ value_parser: None,
+ action: None,
+ verbatim_doc_comment: false,
+ force_long_help: false,
+ next_display_order: None,
+ next_help_heading: None,
+ is_enum: false,
+ is_positional: true,
+ skip_group: false,
+ group_id,
+ group_methods: vec![],
+ kind,
+ }
+ }
+
+ fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
+ self.push_method_(kind, name, arg.to_token_stream());
+ }
+
+ fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) {
+ if name == "id" {
+ match kind {
+ AttrKind::Command | AttrKind::Value => {
+ self.deprecations.push(Deprecation {
+ span: name.span(),
+ id: "id_is_only_for_arg",
+ version: "4.0.0",
+ description: format!(
+ "`#[{}(id)] was allowed by mistake, instead use `#[{}(name)]`",
+ kind.as_str(),
+ kind.as_str()
+ ),
+ });
+ self.name = Name::Assigned(arg);
+ }
+ AttrKind::Group => {
+ self.group_id = Name::Assigned(arg);
+ }
+ AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {
+ self.name = Name::Assigned(arg);
+ }
+ }
+ } else if name == "name" {
+ match kind {
+ AttrKind::Arg => {
+ self.deprecations.push(Deprecation {
+ span: name.span(),
+ id: "id_is_only_for_arg",
+ version: "4.0.0",
+ description: format!(
+ "`#[{}(name)] was allowed by mistake, instead use `#[{}(id)]` or `#[{}(value_name)]`",
+ kind.as_str(),
+ kind.as_str(),
+ kind.as_str()
+ ),
+ });
+ self.name = Name::Assigned(arg);
+ }
+ AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
+ AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => {
+ self.name = Name::Assigned(arg);
+ }
+ }
+ } else if name == "value_parser" {
+ self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg)));
+ } else if name == "action" {
+ self.action = Some(Action::Explicit(Method::new(name, arg)));
+ } else {
+ if name == "short" || name == "long" {
+ self.is_positional = false;
+ }
+ match kind {
+ AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
+ _ => self.methods.push(Method::new(name, arg)),
+ };
+ }
+ }
+
+ fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
+ for attr in attrs {
+ if let Some(AttrValue::Call(_)) = &attr.value {
+ continue;
+ }
+
+ let actual_attr_kind = *attr.kind.get();
+ let kind = match &attr.magic {
+ Some(MagicAttrName::FromGlobal) => {
+ if attr.value.is_some() {
+ let expr = attr.value_or_abort()?;
+ abort!(expr, "attribute `{}` does not accept a value", attr.name);
+ }
+ let ty = self
+ .kind()
+ .ty()
+ .cloned()
+ .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
+ let kind = Sp::new(Kind::FromGlobal(ty), attr.name.clone().span());
+ Some(kind)
+ }
+ Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
+ if attr.value.is_some() {
+ let expr = attr.value_or_abort()?;
+ abort!(expr, "attribute `{}` does not accept a value", attr.name);
+ }
+ let ty = self
+ .kind()
+ .ty()
+ .cloned()
+ .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
+ let kind = Sp::new(Kind::Subcommand(ty), attr.name.clone().span());
+ Some(kind)
+ }
+ Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
+ if attr.value.is_some() {
+ let expr = attr.value_or_abort()?;
+ abort!(expr, "attribute `{}` does not accept a value", attr.name);
+ }
+ let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
+ Some(kind)
+ }
+ Some(MagicAttrName::Flatten) if attr.value.is_none() => {
+ if attr.value.is_some() {
+ let expr = attr.value_or_abort()?;
+ abort!(expr, "attribute `{}` does not accept a value", attr.name);
+ }
+ let ty = self
+ .kind()
+ .ty()
+ .cloned()
+ .unwrap_or_else(|| Sp::new(Ty::Other, self.kind.span()));
+ let kind = Sp::new(Kind::Flatten(ty), attr.name.clone().span());
+ Some(kind)
+ }
+ Some(MagicAttrName::Skip) if actual_attr_kind != AttrKind::Group => {
+ let expr = attr.value.clone();
+ let kind = Sp::new(
+ Kind::Skip(expr, self.kind.attr_kind()),
+ attr.name.clone().span(),
+ );
+ Some(kind)
+ }
+ _ => None,
+ };
+
+ if let Some(kind) = kind {
+ self.set_kind(kind)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
+ for attr in attrs {
+ let actual_attr_kind = *attr.kind.get();
+ let expected_attr_kind = self.kind.attr_kind();
+ match (actual_attr_kind, expected_attr_kind) {
+ (AttrKind::Clap, _) | (AttrKind::StructOpt, _) => {
+ self.deprecations.push(Deprecation::attribute(
+ "4.0.0",
+ actual_attr_kind,
+ expected_attr_kind,
+ attr.kind.span(),
+ ));
+ }
+
+ (AttrKind::Group, AttrKind::Command) => {}
+
+ _ if attr.kind != expected_attr_kind => {
+ abort!(
+ attr.kind.span(),
+ "Expected `{}` attribute instead of `{}`",
+ expected_attr_kind.as_str(),
+ actual_attr_kind.as_str()
+ );
+ }
+
+ _ => {}
+ }
+
+ if let Some(AttrValue::Call(tokens)) = &attr.value {
+ // Force raw mode with method call syntax
+ self.push_method(*attr.kind.get(), attr.name.clone(), quote!(#(#tokens),*));
+ continue;
+ }
+
+ match &attr.magic {
+ Some(MagicAttrName::Short) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.push_method(
+ *attr.kind.get(),
+ attr.name.clone(),
+ self.name.clone().translate_char(*self.casing),
+ );
+ }
+
+ Some(MagicAttrName::Long) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
+ }
+
+ Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.deprecations.push(Deprecation {
+ span: attr.name.span(),
+ id: "bare_value_parser",
+ version: "4.0.0",
+ description: "`#[arg(value_parser)]` is now the default and is no longer needed`".to_owned(),
+ });
+ self.value_parser = Some(ValueParser::Implicit(attr.name.clone()));
+ }
+
+ Some(MagicAttrName::Action) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.deprecations.push(Deprecation {
+ span: attr.name.span(),
+ id: "bare_action",
+ version: "4.0.0",
+ description: "`#[arg(action)]` is now the default and is no longer needed`".to_owned(),
+ });
+ self.action = Some(Action::Implicit(attr.name.clone()));
+ }
+
+ Some(MagicAttrName::Env) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.push_method(
+ *attr.kind.get(),
+ attr.name.clone(),
+ self.name.clone().translate(*self.env_casing),
+ );
+ }
+
+ Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.is_enum = true
+ }
+
+ Some(MagicAttrName::VerbatimDocComment) if attr.value.is_none() => {
+ self.verbatim_doc_comment = true
+ }
+
+ Some(MagicAttrName::About) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ if let Some(method) =
+ Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")?
+ {
+ self.methods.push(method);
+ }
+ }
+
+ Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ self.force_long_help = true;
+ }
+
+ Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ self.force_long_help = true;
+ }
+
+ Some(MagicAttrName::Author) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? {
+ self.methods.push(method);
+ }
+ }
+
+ Some(MagicAttrName::Version) if attr.value.is_none() => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? {
+ self.methods.push(method);
+ }
+ }
+
+ Some(MagicAttrName::DefaultValueT) => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_value_t)] (without an argument) can be used \
+ only on field level\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+
+ let val = if let Some(expr) = &attr.value {
+ quote!(#expr)
+ } else {
+ quote!(<#ty as ::std::default::Default>::default())
+ };
+
+ let val = if attrs
+ .iter()
+ .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
+ {
+ quote_spanned!(attr.name.clone().span()=> {
+ static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
+ let s = DEFAULT_VALUE.get_or_init(|| {
+ let val: #ty = #val;
+ clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
+ });
+ let s: &'static str = &*s;
+ s
+ })
+ } else {
+ quote_spanned!(attr.name.clone().span()=> {
+ static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
+ let s = DEFAULT_VALUE.get_or_init(|| {
+ let val: #ty = #val;
+ ::std::string::ToString::to_string(&val)
+ });
+ let s: &'static str = &*s;
+ s
+ })
+ };
+
+ let raw_ident = Ident::new("default_value", attr.name.clone().span());
+ self.methods.push(Method::new(raw_ident, val));
+ }
+
+ Some(MagicAttrName::DefaultValuesT) => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_values_t)] (without an argument) can be used \
+ only on field level\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+ let expr = attr.value_or_abort()?;
+
+ let container_type = Ty::from_syn_ty(ty);
+ if *container_type != Ty::Vec {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ }
+ let inner_type = inner_type(ty);
+
+ // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
+ // `Vec<#inner_type>`.
+ let val = if attrs
+ .iter()
+ .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
+ {
+ quote_spanned!(attr.name.clone().span()=> {
+ {
+ fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
+ where
+ T: ::std::borrow::Borrow<#inner_type>
+ {
+ iterable
+ .into_iter()
+ .map(|val| {
+ clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned()
+ })
+ }
+
+ static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
+ static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
+ DEFAULT_VALUES.get_or_init(|| {
+ DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
+ }).iter().copied()
+ }
+ })
+ } else {
+ quote_spanned!(attr.name.clone().span()=> {
+ {
+ fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=String>
+ where
+ T: ::std::borrow::Borrow<#inner_type>
+ {
+ iterable.into_iter().map(|val| val.borrow().to_string())
+ }
+
+ static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
+ static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&str>> = ::std::sync::OnceLock::new();
+ DEFAULT_VALUES.get_or_init(|| {
+ DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::string::String::as_str).collect()
+ }).iter().copied()
+ }
+ })
+ };
+
+ self.methods.push(Method::new(
+ Ident::new("default_values", attr.name.clone().span()),
+ val,
+ ));
+ }
+
+ Some(MagicAttrName::DefaultValueOsT) => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_value_os_t)] (without an argument) can be used \
+ only on field level\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+
+ let val = if let Some(expr) = &attr.value {
+ quote!(#expr)
+ } else {
+ quote!(<#ty as ::std::default::Default>::default())
+ };
+
+ let val = if attrs
+ .iter()
+ .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
+ {
+ quote_spanned!(attr.name.clone().span()=> {
+ static DEFAULT_VALUE: ::std::sync::OnceLock<String> = ::std::sync::OnceLock::new();
+ let s = DEFAULT_VALUE.get_or_init(|| {
+ let val: #ty = #val;
+ clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
+ });
+ let s: &'static str = &*s;
+ s
+ })
+ } else {
+ quote_spanned!(attr.name.clone().span()=> {
+ static DEFAULT_VALUE: ::std::sync::OnceLock<::std::ffi::OsString> = ::std::sync::OnceLock::new();
+ let s = DEFAULT_VALUE.get_or_init(|| {
+ let val: #ty = #val;
+ ::std::ffi::OsString::from(val)
+ });
+ let s: &'static ::std::ffi::OsStr = &*s;
+ s
+ })
+ };
+
+ let raw_ident = Ident::new("default_value", attr.name.clone().span());
+ self.methods.push(Method::new(raw_ident, val));
+ }
+
+ Some(MagicAttrName::DefaultValuesOsT) => {
+ assert_attr_kind(attr, &[AttrKind::Arg])?;
+
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_values_os_t)] (without an argument) can be used \
+ only on field level\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+ let expr = attr.value_or_abort()?;
+
+ let container_type = Ty::from_syn_ty(ty);
+ if *container_type != Ty::Vec {
+ abort!(
+ attr.name.clone(),
+ "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ }
+ let inner_type = inner_type(ty);
+
+ // Use `Borrow<#inner_type>` so we accept `&Vec<#inner_type>` and
+ // `Vec<#inner_type>`.
+ let val = if attrs
+ .iter()
+ .any(|a| a.magic == Some(MagicAttrName::ValueEnum))
+ {
+ quote_spanned!(attr.name.clone().span()=> {
+ {
+ fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
+ where
+ T: ::std::borrow::Borrow<#inner_type>
+ {
+ iterable
+ .into_iter()
+ .map(|val| {
+ clap::ValueEnum::to_possible_value(val.borrow()).unwrap().get_name().to_owned().into()
+ })
+ }
+
+ static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
+ static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
+ DEFAULT_VALUES.get_or_init(|| {
+ DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
+ }).iter().copied()
+ }
+ })
+ } else {
+ quote_spanned!(attr.name.clone().span()=> {
+ {
+ fn iter_to_vals<T>(iterable: impl IntoIterator<Item = T>) -> impl Iterator<Item=::std::ffi::OsString>
+ where
+ T: ::std::borrow::Borrow<#inner_type>
+ {
+ iterable.into_iter().map(|val| val.borrow().into())
+ }
+
+ static DEFAULT_STRINGS: ::std::sync::OnceLock<Vec<::std::ffi::OsString>> = ::std::sync::OnceLock::new();
+ static DEFAULT_VALUES: ::std::sync::OnceLock<Vec<&::std::ffi::OsStr>> = ::std::sync::OnceLock::new();
+ DEFAULT_VALUES.get_or_init(|| {
+ DEFAULT_STRINGS.get_or_init(|| iter_to_vals(#expr).collect()).iter().map(::std::ffi::OsString::as_os_str).collect()
+ }).iter().copied()
+ }
+ })
+ };
+
+ self.methods.push(Method::new(
+ Ident::new("default_values", attr.name.clone().span()),
+ val,
+ ));
+ }
+
+ Some(MagicAttrName::NextDisplayOrder) => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ let expr = attr.value_or_abort()?;
+ self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
+ }
+
+ Some(MagicAttrName::NextHelpHeading) => {
+ assert_attr_kind(attr, &[AttrKind::Command])?;
+
+ let expr = attr.value_or_abort()?;
+ self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
+ }
+
+ Some(MagicAttrName::RenameAll) => {
+ let lit = attr.lit_str_or_abort()?;
+ self.casing = CasingStyle::from_lit(lit)?;
+ }
+
+ Some(MagicAttrName::RenameAllEnv) => {
+ assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
+
+ let lit = attr.lit_str_or_abort()?;
+ self.env_casing = CasingStyle::from_lit(lit)?;
+ }
+
+ Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
+ self.skip_group = true;
+ }
+
+ None
+ // Magic only for the default, otherwise just forward to the builder
+ | Some(MagicAttrName::Short)
+ | Some(MagicAttrName::Long)
+ | Some(MagicAttrName::Env)
+ | Some(MagicAttrName::About)
+ | Some(MagicAttrName::LongAbout)
+ | Some(MagicAttrName::LongHelp)
+ | Some(MagicAttrName::Author)
+ | Some(MagicAttrName::Version)
+ => {
+ let expr = attr.value_or_abort()?;
+ self.push_method(*attr.kind.get(), attr.name.clone(), expr);
+ }
+
+ // Magic only for the default, otherwise just forward to the builder
+ Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
+ let expr = attr.value_or_abort()?;
+ self.push_method(*attr.kind.get(), attr.name.clone(), expr);
+ }
+
+ // Directives that never receive a value
+ Some(MagicAttrName::ValueEnum)
+ | Some(MagicAttrName::VerbatimDocComment) => {
+ let expr = attr.value_or_abort()?;
+ abort!(expr, "attribute `{}` does not accept a value", attr.name);
+ }
+
+ // Kinds
+ Some(MagicAttrName::FromGlobal)
+ | Some(MagicAttrName::Subcommand)
+ | Some(MagicAttrName::ExternalSubcommand)
+ | Some(MagicAttrName::Flatten)
+ | Some(MagicAttrName::Skip) => {
+ }
+ }
+ }
+
+ if self.has_explicit_methods() {
+ if let Kind::Skip(_, attr) = &*self.kind {
+ abort!(
+ self.methods[0].name.span(),
+ "`{}` cannot be used with `#[{}(skip)]",
+ self.methods[0].name,
+ attr.as_str(),
+ );
+ }
+ if let Kind::FromGlobal(_) = &*self.kind {
+ abort!(
+ self.methods[0].name.span(),
+ "`{}` cannot be used with `#[arg(from_global)]",
+ self.methods[0].name,
+ );
+ }
+ }
+
+ Ok(())
+ }
+
+ fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
+ let lines = extract_doc_comment(attrs);
+
+ if !lines.is_empty() {
+ let (short_help, long_help) =
+ format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help);
+ let short_name = format_ident!("{short_name}");
+ let short = Method::new(
+ short_name,
+ short_help
+ .map(|h| quote!(#h))
+ .unwrap_or_else(|| quote!(None)),
+ );
+ self.doc_comment.push(short);
+ if let Some(long_name) = long_name {
+ let long_name = format_ident!("{long_name}");
+ let long = Method::new(
+ long_name,
+ long_help
+ .map(|h| quote!(#h))
+ .unwrap_or_else(|| quote!(None)),
+ );
+ self.doc_comment.push(long);
+ }
+ }
+ }
+
+ fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
+ match (self.kind.get(), kind.get()) {
+ (Kind::Arg(_), Kind::FromGlobal(_))
+ | (Kind::Arg(_), Kind::Subcommand(_))
+ | (Kind::Arg(_), Kind::Flatten(_))
+ | (Kind::Arg(_), Kind::Skip(_, _))
+ | (Kind::Command(_), Kind::Subcommand(_))
+ | (Kind::Command(_), Kind::Flatten(_))
+ | (Kind::Command(_), Kind::Skip(_, _))
+ | (Kind::Command(_), Kind::ExternalSubcommand)
+ | (Kind::Value, Kind::Skip(_, _)) => {
+ self.kind = kind;
+ }
+
+ (_, _) => {
+ let old = self.kind.name();
+ let new = kind.name();
+ abort!(kind.span(), "`{new}` cannot be used with `{old}`");
+ }
+ }
+ Ok(())
+ }
+
+ pub fn find_default_method(&self) -> Option<&Method> {
+ self.methods
+ .iter()
+ .find(|m| m.name == "default_value" || m.name == "default_value_os")
+ }
+
+ /// generate methods from attributes on top of struct or enum
+ pub fn initial_top_level_methods(&self) -> TokenStream {
+ let next_display_order = self.next_display_order.as_ref().into_iter();
+ let next_help_heading = self.next_help_heading.as_ref().into_iter();
+ quote!(
+ #(#next_display_order)*
+ #(#next_help_heading)*
+ )
+ }
+
+ pub fn final_top_level_methods(&self) -> TokenStream {
+ let methods = &self.methods;
+ let doc_comment = &self.doc_comment;
+
+ quote!( #(#doc_comment)* #(#methods)*)
+ }
+
+ /// generate methods on top of a field
+ pub fn field_methods(&self) -> proc_macro2::TokenStream {
+ let methods = &self.methods;
+ let doc_comment = &self.doc_comment;
+ quote!( #(#doc_comment)* #(#methods)* )
+ }
+
+ pub fn group_id(&self) -> TokenStream {
+ self.group_id.clone().raw()
+ }
+
+ pub fn group_methods(&self) -> TokenStream {
+ let group_methods = &self.group_methods;
+ quote!( #(#group_methods)* )
+ }
+
+ pub fn deprecations(&self) -> proc_macro2::TokenStream {
+ let deprecations = &self.deprecations;
+ quote!( #(#deprecations)* )
+ }
+
+ pub fn next_display_order(&self) -> TokenStream {
+ let next_display_order = self.next_display_order.as_ref().into_iter();
+ quote!( #(#next_display_order)* )
+ }
+
+ pub fn next_help_heading(&self) -> TokenStream {
+ let next_help_heading = self.next_help_heading.as_ref().into_iter();
+ quote!( #(#next_help_heading)* )
+ }
+
+ pub fn id(&self) -> TokenStream {
+ self.name.clone().raw()
+ }
+
+ pub fn cased_name(&self) -> TokenStream {
+ self.name.clone().translate(*self.casing)
+ }
+
+ pub fn value_name(&self) -> TokenStream {
+ self.name.clone().translate(CasingStyle::ScreamingSnake)
+ }
+
+ pub fn value_parser(&self, field_type: &Type) -> Method {
+ self.value_parser
+ .clone()
+ .map(|p| {
+ let inner_type = inner_type(field_type);
+ p.resolve(inner_type)
+ })
+ .unwrap_or_else(|| {
+ let inner_type = inner_type(field_type);
+ if let Some(action) = self.action.as_ref() {
+ let span = action.span();
+ default_value_parser(inner_type, span)
+ } else {
+ let span = self
+ .action
+ .as_ref()
+ .map(|a| a.span())
+ .unwrap_or_else(|| self.kind.span());
+ default_value_parser(inner_type, span)
+ }
+ })
+ }
+
+ pub fn action(&self, field_type: &Type) -> Method {
+ self.action
+ .clone()
+ .map(|p| p.resolve(field_type))
+ .unwrap_or_else(|| {
+ if let Some(value_parser) = self.value_parser.as_ref() {
+ let span = value_parser.span();
+ default_action(field_type, span)
+ } else {
+ let span = self
+ .value_parser
+ .as_ref()
+ .map(|a| a.span())
+ .unwrap_or_else(|| self.kind.span());
+ default_action(field_type, span)
+ }
+ })
+ }
+
+ pub fn kind(&self) -> Sp<Kind> {
+ self.kind.clone()
+ }
+
+ pub fn is_positional(&self) -> bool {
+ self.is_positional
+ }
+
+ pub fn casing(&self) -> Sp<CasingStyle> {
+ self.casing
+ }
+
+ pub fn env_casing(&self) -> Sp<CasingStyle> {
+ self.env_casing
+ }
+
+ pub fn has_explicit_methods(&self) -> bool {
+ self.methods
+ .iter()
+ .any(|m| m.name != "help" && m.name != "long_help")
+ }
+
+ pub fn skip_group(&self) -> bool {
+ self.skip_group
+ }
+}
+
+#[derive(Clone)]
+enum ValueParser {
+ Explicit(Method),
+ Implicit(Ident),
+}
+
+impl ValueParser {
+ fn resolve(self, _inner_type: &Type) -> Method {
+ match self {
+ Self::Explicit(method) => method,
+ Self::Implicit(ident) => default_value_parser(_inner_type, ident.span()),
+ }
+ }
+
+ fn span(&self) -> Span {
+ match self {
+ Self::Explicit(method) => method.name.span(),
+ Self::Implicit(ident) => ident.span(),
+ }
+ }
+}
+
+fn default_value_parser(inner_type: &Type, span: Span) -> Method {
+ let func = Ident::new("value_parser", span);
+ Method::new(
+ func,
+ quote_spanned! { span=>
+ clap::value_parser!(#inner_type)
+ },
+ )
+}
+
+#[derive(Clone)]
+pub enum Action {
+ Explicit(Method),
+ Implicit(Ident),
+}
+
+impl Action {
+ pub fn resolve(self, _field_type: &Type) -> Method {
+ match self {
+ Self::Explicit(method) => method,
+ Self::Implicit(ident) => default_action(_field_type, ident.span()),
+ }
+ }
+
+ pub fn span(&self) -> Span {
+ match self {
+ Self::Explicit(method) => method.name.span(),
+ Self::Implicit(ident) => ident.span(),
+ }
+ }
+}
+
+fn default_action(field_type: &Type, span: Span) -> Method {
+ let ty = Ty::from_syn_ty(field_type);
+ let args = match *ty {
+ Ty::Vec | Ty::OptionVec | Ty::VecVec | Ty::OptionVecVec => {
+ quote_spanned! { span=>
+ clap::ArgAction::Append
+ }
+ }
+ Ty::Option | Ty::OptionOption => {
+ quote_spanned! { span=>
+ clap::ArgAction::Set
+ }
+ }
+ _ => {
+ if is_simple_ty(field_type, "bool") {
+ quote_spanned! { span=>
+ clap::ArgAction::SetTrue
+ }
+ } else {
+ quote_spanned! { span=>
+ clap::ArgAction::Set
+ }
+ }
+ }
+ };
+
+ let func = Ident::new("action", span);
+ Method::new(func, args)
+}
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Clone)]
+pub enum Kind {
+ Arg(Sp<Ty>),
+ Command(Sp<Ty>),
+ Value,
+ FromGlobal(Sp<Ty>),
+ Subcommand(Sp<Ty>),
+ Flatten(Sp<Ty>),
+ Skip(Option<AttrValue>, AttrKind),
+ ExternalSubcommand,
+}
+
+impl Kind {
+ pub fn name(&self) -> &'static str {
+ match self {
+ Self::Arg(_) => "arg",
+ Self::Command(_) => "command",
+ Self::Value => "value",
+ Self::FromGlobal(_) => "from_global",
+ Self::Subcommand(_) => "subcommand",
+ Self::Flatten(_) => "flatten",
+ Self::Skip(_, _) => "skip",
+ Self::ExternalSubcommand => "external_subcommand",
+ }
+ }
+
+ pub fn attr_kind(&self) -> AttrKind {
+ match self {
+ Self::Arg(_) => AttrKind::Arg,
+ Self::Command(_) => AttrKind::Command,
+ Self::Value => AttrKind::Value,
+ Self::FromGlobal(_) => AttrKind::Arg,
+ Self::Subcommand(_) => AttrKind::Command,
+ Self::Flatten(_) => AttrKind::Command,
+ Self::Skip(_, kind) => *kind,
+ Self::ExternalSubcommand => AttrKind::Command,
+ }
+ }
+
+ pub fn ty(&self) -> Option<&Sp<Ty>> {
+ match self {
+ Self::Arg(ty)
+ | Self::Command(ty)
+ | Self::Flatten(ty)
+ | Self::FromGlobal(ty)
+ | Self::Subcommand(ty) => Some(ty),
+ Self::Value | Self::Skip(_, _) | Self::ExternalSubcommand => None,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct Method {
+ name: Ident,
+ args: TokenStream,
+}
+
+impl Method {
+ pub fn new(name: Ident, args: TokenStream) -> Self {
+ Method { name, args }
+ }
+
+ fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
+ let mut lit = match env::var(env_var) {
+ Ok(val) => {
+ if val.is_empty() {
+ return Ok(None);
+ }
+ LitStr::new(&val, ident.span())
+ }
+ Err(_) => {
+ abort!(
+ ident,
+ "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
+ ident,
+ note = format_args!("`{env_var}` environment variable is not set"),
+ help = format_args!("use `{ident} = \"...\"` to set {ident} manually")
+ );
+ }
+ };
+
+ if ident == "author" {
+ let edited = process_author_str(&lit.value());
+ lit = LitStr::new(&edited, lit.span());
+ }
+
+ Ok(Some(Method::new(ident, quote!(#lit))))
+ }
+
+ pub(crate) fn args(&self) -> &TokenStream {
+ &self.args
+ }
+}
+
+impl ToTokens for Method {
+ fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
+ let Method { ref name, ref args } = self;
+
+ let tokens = quote!( .#name(#args) );
+
+ tokens.to_tokens(ts);
+ }
+}
+
+#[derive(Clone)]
+pub struct Deprecation {
+ pub span: Span,
+ pub id: &'static str,
+ pub version: &'static str,
+ pub description: String,
+}
+
+impl Deprecation {
+ fn attribute(version: &'static str, old: AttrKind, new: AttrKind, span: Span) -> Self {
+ Self {
+ span,
+ id: "old_attribute",
+ version,
+ description: format!(
+ "Attribute `#[{}(...)]` has been deprecated in favor of `#[{}(...)]`",
+ old.as_str(),
+ new.as_str()
+ ),
+ }
+ }
+}
+
+impl ToTokens for Deprecation {
+ fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
+ let tokens = if cfg!(feature = "deprecated") {
+ let Deprecation {
+ span,
+ id,
+ version,
+ description,
+ } = self;
+ let span = *span;
+ let id = Ident::new(id, span);
+
+ quote_spanned!(span=> {
+ #[deprecated(since = #version, note = #description)]
+ fn #id() {}
+ #id();
+ })
+ } else {
+ quote!()
+ };
+
+ tokens.to_tokens(ts);
+ }
+}
+
+fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
+ if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
+ // deprecated
+ } else if !possible_kind.contains(attr.kind.get()) {
+ let options = possible_kind
+ .iter()
+ .map(|k| format!("`#[{}({})]`", k.as_str(), attr.name))
+ .collect::<Vec<_>>();
+ abort!(
+ attr.name,
+ "Unknown `#[{}({})]` attribute ({} exists)",
+ attr.kind.as_str(),
+ attr.name,
+ options.join(", ")
+ );
+ }
+ Ok(())
+}
+
+/// replace all `:` with `, ` when not inside the `<>`
+///
+/// `"author1:author2:author3" => "author1, author2, author3"`
+/// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"
+fn process_author_str(author: &str) -> String {
+ let mut res = String::with_capacity(author.len());
+ let mut inside_angle_braces = 0usize;
+
+ for ch in author.chars() {
+ if inside_angle_braces > 0 && ch == '>' {
+ inside_angle_braces -= 1;
+ res.push(ch);
+ } else if ch == '<' {
+ inside_angle_braces += 1;
+ res.push(ch);
+ } else if inside_angle_braces == 0 && ch == ':' {
+ res.push_str(", ");
+ } else {
+ res.push(ch);
+ }
+ }
+
+ res
+}
+
+/// Defines the casing for the attributes long representation.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum CasingStyle {
+ /// Indicate word boundaries with uppercase letter, excluding the first word.
+ Camel,
+ /// Keep all letters lowercase and indicate word boundaries with hyphens.
+ Kebab,
+ /// Indicate word boundaries with uppercase letter, including the first word.
+ Pascal,
+ /// Keep all letters uppercase and indicate word boundaries with underscores.
+ ScreamingSnake,
+ /// Keep all letters lowercase and indicate word boundaries with underscores.
+ Snake,
+ /// Keep all letters lowercase and remove word boundaries.
+ Lower,
+ /// Keep all letters uppercase and remove word boundaries.
+ Upper,
+ /// Use the original attribute name defined in the code.
+ Verbatim,
+}
+
+impl CasingStyle {
+ fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
+ use self::CasingStyle::*;
+
+ let normalized = name.value().to_upper_camel_case().to_lowercase();
+ let cs = |kind| Sp::new(kind, name.span());
+
+ let s = match normalized.as_ref() {
+ "camel" | "camelcase" => cs(Camel),
+ "kebab" | "kebabcase" => cs(Kebab),
+ "pascal" | "pascalcase" => cs(Pascal),
+ "screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
+ "snake" | "snakecase" => cs(Snake),
+ "lower" | "lowercase" => cs(Lower),
+ "upper" | "uppercase" => cs(Upper),
+ "verbatim" | "verbatimcase" => cs(Verbatim),
+ s => abort!(name, "unsupported casing: `{s}`"),
+ };
+ Ok(s)
+ }
+}
+
+#[derive(Clone)]
+pub enum Name {
+ Derived(Ident),
+ Assigned(TokenStream),
+}
+
+impl Name {
+ pub fn raw(self) -> TokenStream {
+ match self {
+ Name::Assigned(tokens) => tokens,
+ Name::Derived(ident) => {
+ let s = ident.unraw().to_string();
+ quote_spanned!(ident.span()=> #s)
+ }
+ }
+ }
+
+ pub fn translate(self, style: CasingStyle) -> TokenStream {
+ use CasingStyle::*;
+
+ match self {
+ Name::Assigned(tokens) => tokens,
+ Name::Derived(ident) => {
+ let s = ident.unraw().to_string();
+ let s = match style {
+ Pascal => s.to_upper_camel_case(),
+ Kebab => s.to_kebab_case(),
+ Camel => s.to_lower_camel_case(),
+ ScreamingSnake => s.to_shouty_snake_case(),
+ Snake => s.to_snake_case(),
+ Lower => s.to_snake_case().replace('_', ""),
+ Upper => s.to_shouty_snake_case().replace('_', ""),
+ Verbatim => s,
+ };
+ quote_spanned!(ident.span()=> #s)
+ }
+ }
+ }
+
+ pub fn translate_char(self, style: CasingStyle) -> TokenStream {
+ use CasingStyle::*;
+
+ match self {
+ Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
+ Name::Derived(ident) => {
+ let s = ident.unraw().to_string();
+ let s = match style {
+ Pascal => s.to_upper_camel_case(),
+ Kebab => s.to_kebab_case(),
+ Camel => s.to_lower_camel_case(),
+ ScreamingSnake => s.to_shouty_snake_case(),
+ Snake => s.to_snake_case(),
+ Lower => s.to_snake_case(),
+ Upper => s.to_shouty_snake_case(),
+ Verbatim => s,
+ };
+
+ let s = s.chars().next().unwrap();
+ quote_spanned!(ident.span()=> #s)
+ }
+ }
+ }
+}
diff --git a/third_party/rust/clap_derive/src/lib.rs b/third_party/rust/clap_derive/src/lib.rs
new file mode 100644
index 0000000000..d3cae9b65d
--- /dev/null
+++ b/third_party/rust/clap_derive/src/lib.rs
@@ -0,0 +1,116 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
+// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
+// Ana Hobden (@hoverbear) <operator@hoverbear.org>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
+// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
+// MIT/Apache 2.0 license.
+
+#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
+#![doc = include_str!("../README.md")]
+#![forbid(unsafe_code)]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use syn::{parse_macro_input, DeriveInput};
+use syn::{Data, DataStruct, Fields};
+
+#[macro_use]
+mod macros;
+
+mod attr;
+mod derives;
+mod dummies;
+mod item;
+mod utils;
+
+/// Generates the `ValueEnum` impl.
+#[proc_macro_derive(ValueEnum, attributes(clap, value))]
+pub fn value_enum(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_value_enum(&input)
+ .unwrap_or_else(|err| {
+ let dummy = dummies::value_enum(&input.ident);
+ to_compile_error(err, dummy)
+ })
+ .into()
+}
+
+/// Generates the `Parser` implementation.
+///
+/// This is far less verbose than defining the `clap::Command` struct manually,
+/// receiving an instance of `clap::ArgMatches` from conducting parsing, and then
+/// implementing a conversion code to instantiate an instance of the user
+/// context struct.
+#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))]
+pub fn parser(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_parser(&input)
+ .unwrap_or_else(|err| {
+ let specific_dummy = match input.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref _fields),
+ ..
+ }) => Some(dummies::args(&input.ident)),
+ Data::Struct(DataStruct {
+ fields: Fields::Unit,
+ ..
+ }) => Some(dummies::args(&input.ident)),
+ Data::Enum(_) => Some(dummies::subcommand(&input.ident)),
+ _ => None,
+ };
+ let dummy = specific_dummy
+ .map(|specific_dummy| {
+ let parser_dummy = dummies::parser(&input.ident);
+ quote::quote! {
+ #parser_dummy
+ #specific_dummy
+ }
+ })
+ .unwrap_or_else(|| quote::quote!());
+ to_compile_error(err, dummy)
+ })
+ .into()
+}
+
+/// Generates the `Subcommand` impl.
+#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))]
+pub fn subcommand(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_subcommand(&input)
+ .unwrap_or_else(|err| {
+ let dummy = dummies::subcommand(&input.ident);
+ to_compile_error(err, dummy)
+ })
+ .into()
+}
+
+/// Generates the `Args` impl.
+#[proc_macro_derive(Args, attributes(clap, command, arg, group))]
+pub fn args(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_args(&input)
+ .unwrap_or_else(|err| {
+ let dummy = dummies::args(&input.ident);
+ to_compile_error(err, dummy)
+ })
+ .into()
+}
+
+fn to_compile_error(
+ error: syn::Error,
+ dummy: proc_macro2::TokenStream,
+) -> proc_macro2::TokenStream {
+ let compile_errors = error.to_compile_error();
+ quote::quote!(
+ #dummy
+ #compile_errors
+ )
+}
diff --git a/third_party/rust/clap_derive/src/macros.rs b/third_party/rust/clap_derive/src/macros.rs
new file mode 100644
index 0000000000..282048bc17
--- /dev/null
+++ b/third_party/rust/clap_derive/src/macros.rs
@@ -0,0 +1,21 @@
+macro_rules! format_err {
+ ($obj:expr, $($format:tt)+) => {{
+ #[allow(unused_imports)]
+ use $crate::utils::error::*;
+ let msg = format!($($format)+);
+ $obj.EXPECTED_Span_OR_ToTokens(msg)
+ }};
+}
+
+macro_rules! abort {
+ ($obj:expr, $($format:tt)+) => {{
+ return Err(format_err!($obj, $($format)+));
+ }};
+}
+
+macro_rules! abort_call_site {
+ ($($format:tt)+) => {{
+ let span = proc_macro2::Span::call_site();
+ abort!(span, $($format)+)
+ }};
+}
diff --git a/third_party/rust/clap_derive/src/utils/doc_comments.rs b/third_party/rust/clap_derive/src/utils/doc_comments.rs
new file mode 100644
index 0000000000..63c6ad1ef3
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/doc_comments.rs
@@ -0,0 +1,126 @@
+//! The preprocessing we apply to doc comments.
+//!
+//! #[derive(Parser)] works in terms of "paragraphs". Paragraph is a sequence of
+//! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines.
+
+use std::iter;
+
+pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> {
+ // multiline comments (`/** ... */`) may have LFs (`\n`) in them,
+ // we need to split so we could handle the lines correctly
+ //
+ // we also need to remove leading and trailing blank lines
+ let mut lines: Vec<_> = attrs
+ .iter()
+ .filter(|attr| attr.path().is_ident("doc"))
+ .filter_map(|attr| {
+ // non #[doc = "..."] attributes are not our concern
+ // we leave them for rustc to handle
+ match &attr.meta {
+ syn::Meta::NameValue(syn::MetaNameValue {
+ value:
+ syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Str(s),
+ ..
+ }),
+ ..
+ }) => Some(s.value()),
+ _ => None,
+ }
+ })
+ .skip_while(|s| is_blank(s))
+ .flat_map(|s| {
+ let lines = s
+ .split('\n')
+ .map(|s| {
+ // remove one leading space no matter what
+ let s = s.strip_prefix(' ').unwrap_or(s);
+ s.to_owned()
+ })
+ .collect::<Vec<_>>();
+ lines
+ })
+ .collect();
+
+ while let Some(true) = lines.last().map(|s| is_blank(s)) {
+ lines.pop();
+ }
+
+ lines
+}
+
+pub fn format_doc_comment(
+ lines: &[String],
+ preprocess: bool,
+ force_long: bool,
+) -> (Option<String>, Option<String>) {
+ if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) {
+ let (short, long) = if preprocess {
+ let paragraphs = split_paragraphs(lines);
+ let short = paragraphs[0].clone();
+ let long = paragraphs.join("\n\n");
+ (remove_period(short), long)
+ } else {
+ let short = lines[..first_blank].join("\n");
+ let long = lines.join("\n");
+ (short, long)
+ };
+
+ (Some(short), Some(long))
+ } else {
+ let (short, long) = if preprocess {
+ let short = merge_lines(lines);
+ let long = force_long.then(|| short.clone());
+ let short = remove_period(short);
+ (short, long)
+ } else {
+ let short = lines.join("\n");
+ let long = force_long.then(|| short.clone());
+ (short, long)
+ };
+
+ (Some(short), long)
+ }
+}
+
+fn split_paragraphs(lines: &[String]) -> Vec<String> {
+ let mut last_line = 0;
+ iter::from_fn(|| {
+ let slice = &lines[last_line..];
+ let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0);
+
+ let slice = &slice[start..];
+ let len = slice
+ .iter()
+ .position(|s| is_blank(s))
+ .unwrap_or(slice.len());
+
+ last_line += start + len;
+
+ if len != 0 {
+ Some(merge_lines(&slice[..len]))
+ } else {
+ None
+ }
+ })
+ .collect()
+}
+
+fn remove_period(mut s: String) -> String {
+ if s.ends_with('.') && !s.ends_with("..") {
+ s.pop();
+ }
+ s
+}
+
+fn is_blank(s: &str) -> bool {
+ s.trim().is_empty()
+}
+
+fn merge_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String {
+ lines
+ .into_iter()
+ .map(|s| s.as_ref().trim().to_owned())
+ .collect::<Vec<_>>()
+ .join(" ")
+}
diff --git a/third_party/rust/clap_derive/src/utils/error.rs b/third_party/rust/clap_derive/src/utils/error.rs
new file mode 100644
index 0000000000..276e349493
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/error.rs
@@ -0,0 +1,22 @@
+pub trait SpanError {
+ #[allow(non_snake_case)]
+ fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
+}
+
+pub trait ToTokensError {
+ #[allow(non_snake_case)]
+ fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
+}
+
+impl<T: quote::ToTokens> ToTokensError for T {
+ fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
+ // Curb monomorphization from generating too many identical `new_spanned`.
+ syn::Error::new_spanned(self.to_token_stream(), msg)
+ }
+}
+
+impl SpanError for proc_macro2::Span {
+ fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
+ syn::Error::new(*self, msg)
+ }
+}
diff --git a/third_party/rust/clap_derive/src/utils/mod.rs b/third_party/rust/clap_derive/src/utils/mod.rs
new file mode 100644
index 0000000000..13e6e71062
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/mod.rs
@@ -0,0 +1,13 @@
+pub mod error;
+
+mod doc_comments;
+mod spanned;
+mod ty;
+
+pub use doc_comments::extract_doc_comment;
+pub use doc_comments::format_doc_comment;
+
+pub use self::{
+ spanned::Sp,
+ ty::{inner_type, is_simple_ty, sub_type, subty_if_name, Ty},
+};
diff --git a/third_party/rust/clap_derive/src/utils/spanned.rs b/third_party/rust/clap_derive/src/utils/spanned.rs
new file mode 100644
index 0000000000..339a654e6d
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/spanned.rs
@@ -0,0 +1,89 @@
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::ToTokens;
+use syn::LitStr;
+
+use std::ops::{Deref, DerefMut};
+
+/// An entity with a span attached.
+#[derive(Debug, Copy, Clone)]
+pub struct Sp<T> {
+ val: T,
+ span: Span,
+}
+
+impl<T> Sp<T> {
+ pub fn new(val: T, span: Span) -> Self {
+ Sp { val, span }
+ }
+
+ pub fn get(&self) -> &T {
+ &self.val
+ }
+
+ pub fn span(&self) -> Span {
+ self.span
+ }
+}
+
+impl<T> Deref for Sp<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.val
+ }
+}
+
+impl<T> DerefMut for Sp<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.val
+ }
+}
+
+impl From<Ident> for Sp<String> {
+ fn from(ident: Ident) -> Self {
+ Sp {
+ val: ident.to_string(),
+ span: ident.span(),
+ }
+ }
+}
+
+impl From<LitStr> for Sp<String> {
+ fn from(lit: LitStr) -> Self {
+ Sp {
+ val: lit.value(),
+ span: lit.span(),
+ }
+ }
+}
+
+impl<'a> From<Sp<&'a str>> for Sp<String> {
+ fn from(sp: Sp<&'a str>) -> Self {
+ Sp::new(sp.val.into(), sp.span)
+ }
+}
+
+impl<U, T: PartialEq<U>> PartialEq<U> for Sp<T> {
+ fn eq(&self, other: &U) -> bool {
+ self.val == *other
+ }
+}
+
+impl<T: AsRef<str>> AsRef<str> for Sp<T> {
+ fn as_ref(&self) -> &str {
+ self.val.as_ref()
+ }
+}
+
+impl<T: ToTokens> ToTokens for Sp<T> {
+ fn to_tokens(&self, stream: &mut TokenStream) {
+ // this is the simplest way out of correct ones to change span on
+ // arbitrary token tree I could come up with
+ let tt = self.val.to_token_stream().into_iter().map(|mut tt| {
+ tt.set_span(self.span);
+ tt
+ });
+
+ stream.extend(tt);
+ }
+}
diff --git a/third_party/rust/clap_derive/src/utils/ty.rs b/third_party/rust/clap_derive/src/utils/ty.rs
new file mode 100644
index 0000000000..9349bc29db
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/ty.rs
@@ -0,0 +1,165 @@
+//! Special types handling
+
+use super::spanned::Sp;
+
+use syn::{
+ spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
+ PathSegment, Type, TypePath,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Ty {
+ Unit,
+ Vec,
+ VecVec,
+ Option,
+ OptionOption,
+ OptionVec,
+ OptionVecVec,
+ Other,
+}
+
+impl Ty {
+ pub fn from_syn_ty(ty: &syn::Type) -> Sp<Self> {
+ use self::Ty::*;
+ let t = |kind| Sp::new(kind, ty.span());
+
+ if is_unit_ty(ty) {
+ t(Unit)
+ } else if let Some(vt) = get_vec_ty(ty, Vec, VecVec) {
+ t(vt)
+ } else if let Some(subty) = subty_if_name(ty, "Option") {
+ if is_generic_ty(subty, "Option") {
+ t(OptionOption)
+ } else if let Some(vt) = get_vec_ty(subty, OptionVec, OptionVecVec) {
+ t(vt)
+ } else {
+ t(Option)
+ }
+ } else {
+ t(Other)
+ }
+ }
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::Unit => "()",
+ Self::Vec => "Vec<T>",
+ Self::Option => "Option<T>",
+ Self::OptionOption => "Option<Option<T>>",
+ Self::OptionVec => "Option<Vec<T>>",
+ Self::VecVec => "Vec<Vec<T>>",
+ Self::OptionVecVec => "Option<Vec<Vec<T>>>",
+ Self::Other => "...other...",
+ }
+ }
+}
+
+pub fn inner_type(field_ty: &syn::Type) -> &syn::Type {
+ let ty = Ty::from_syn_ty(field_ty);
+ match *ty {
+ Ty::Vec | Ty::Option => sub_type(field_ty).unwrap_or(field_ty),
+ Ty::OptionOption | Ty::OptionVec | Ty::VecVec => {
+ sub_type(field_ty).and_then(sub_type).unwrap_or(field_ty)
+ }
+ Ty::OptionVecVec => sub_type(field_ty)
+ .and_then(sub_type)
+ .and_then(sub_type)
+ .unwrap_or(field_ty),
+ _ => field_ty,
+ }
+}
+
+pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
+ subty_if(ty, |_| true)
+}
+
+fn only_last_segment(mut ty: &syn::Type) -> Option<&PathSegment> {
+ while let syn::Type::Group(syn::TypeGroup { elem, .. }) = ty {
+ ty = elem;
+ }
+ match ty {
+ Type::Path(TypePath {
+ qself: None,
+ path:
+ Path {
+ leading_colon: None,
+ segments,
+ },
+ }) => only_one(segments.iter()),
+
+ _ => None,
+ }
+}
+
+fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type>
+where
+ F: FnOnce(&PathSegment) -> bool,
+{
+ only_last_segment(ty)
+ .filter(|segment| f(segment))
+ .and_then(|segment| {
+ if let AngleBracketed(args) = &segment.arguments {
+ only_one(args.args.iter()).and_then(|genneric| {
+ if let GenericArgument::Type(ty) = genneric {
+ Some(ty)
+ } else {
+ None
+ }
+ })
+ } else {
+ None
+ }
+ })
+}
+
+pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> {
+ subty_if(ty, |seg| seg.ident == name)
+}
+
+pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
+ only_last_segment(ty)
+ .map(|segment| {
+ if let PathArguments::None = segment.arguments {
+ segment.ident == name
+ } else {
+ false
+ }
+ })
+ .unwrap_or(false)
+}
+
+fn is_generic_ty(ty: &syn::Type, name: &str) -> bool {
+ subty_if_name(ty, name).is_some()
+}
+
+fn is_unit_ty(ty: &syn::Type) -> bool {
+ if let syn::Type::Tuple(tuple) = ty {
+ tuple.elems.is_empty()
+ } else {
+ false
+ }
+}
+
+fn only_one<I, T>(mut iter: I) -> Option<T>
+where
+ I: Iterator<Item = T>,
+{
+ iter.next().filter(|_| iter.next().is_none())
+}
+
+#[cfg(feature = "unstable-v5")]
+fn get_vec_ty(ty: &Type, vec_ty: Ty, vecvec_ty: Ty) -> Option<Ty> {
+ subty_if_name(ty, "Vec").map(|subty| {
+ if is_generic_ty(subty, "Vec") {
+ vecvec_ty
+ } else {
+ vec_ty
+ }
+ })
+}
+
+#[cfg(not(feature = "unstable-v5"))]
+fn get_vec_ty(ty: &Type, vec_ty: Ty, _vecvec_ty: Ty) -> Option<Ty> {
+ is_generic_ty(ty, "Vec").then_some(vec_ty)
+}