summaryrefslogtreecommitdiffstats
path: root/third_party/rust/clap_derive/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/clap_derive/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
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/attrs.rs985
-rw-r--r--third_party/rust/clap_derive/src/derives/arg_enum.rs123
-rw-r--r--third_party/rust/clap_derive/src/derives/args.rs651
-rw-r--r--third_party/rust/clap_derive/src/derives/into_app.rs119
-rw-r--r--third_party/rust/clap_derive/src/derives/mod.rs23
-rw-r--r--third_party/rust/clap_derive/src/derives/parser.rs95
-rw-r--r--third_party/rust/clap_derive/src/derives/subcommand.rs666
-rw-r--r--third_party/rust/clap_derive/src/dummies.rs91
-rw-r--r--third_party/rust/clap_derive/src/lib.rs66
-rw-r--r--third_party/rust/clap_derive/src/parse.rs279
-rw-r--r--third_party/rust/clap_derive/src/utils/doc_comments.rs107
-rw-r--r--third_party/rust/clap_derive/src/utils/mod.rs9
-rw-r--r--third_party/rust/clap_derive/src/utils/spanned.rs92
-rw-r--r--third_party/rust/clap_derive/src/utils/ty.rs121
14 files changed, 3427 insertions, 0 deletions
diff --git a/third_party/rust/clap_derive/src/attrs.rs b/third_party/rust/clap_derive/src/attrs.rs
new file mode 100644
index 0000000000..aaee167e31
--- /dev/null
+++ b/third_party/rust/clap_derive/src/attrs.rs
@@ -0,0 +1,985 @@
+// 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 crate::{
+ parse::*,
+ utils::{process_doc_comment, Sp, Ty},
+};
+
+use std::env;
+
+use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use proc_macro2::{self, Span, TokenStream};
+use proc_macro_error::abort;
+use quote::{quote, quote_spanned, ToTokens};
+use syn::{
+ self, ext::IdentExt, spanned::Spanned, Attribute, Expr, Field, Ident, LitStr, MetaNameValue,
+ Type, Variant,
+};
+
+/// 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 Attrs {
+ name: Name,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ty: Option<Type>,
+ doc_comment: Vec<Method>,
+ methods: Vec<Method>,
+ parser: Sp<Parser>,
+ verbatim_doc_comment: Option<Ident>,
+ next_display_order: Option<Method>,
+ next_help_heading: Option<Method>,
+ help_heading: Option<Method>,
+ is_enum: bool,
+ has_custom_parser: bool,
+ kind: Sp<Kind>,
+}
+
+impl Attrs {
+ pub fn from_struct(
+ span: Span,
+ attrs: &[Attribute],
+ name: Name,
+ argument_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let mut res = Self::new(span, name, None, argument_casing, env_casing);
+ res.push_attrs(attrs);
+ res.push_doc_comment(attrs, "about");
+
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "`parse` attribute is only allowed on fields"
+ );
+ }
+ match &*res.kind {
+ Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"),
+ Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"),
+ Kind::Arg(_) => res,
+ Kind::FromGlobal(_) => abort!(res.kind.span(), "from_global is only allowed on fields"),
+ Kind::Flatten => abort!(res.kind.span(), "flatten is only allowed on fields"),
+ Kind::ExternalSubcommand => abort!(
+ res.kind.span(),
+ "external_subcommand is only allowed on fields"
+ ),
+ }
+ }
+
+ pub fn from_variant(
+ variant: &Variant,
+ struct_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let name = variant.ident.clone();
+ let mut res = Self::new(
+ variant.span(),
+ Name::Derived(name),
+ None,
+ struct_casing,
+ env_casing,
+ );
+ res.push_attrs(&variant.attrs);
+ res.push_doc_comment(&variant.attrs, "about");
+
+ match &*res.kind {
+ Kind::Flatten => {
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "parse attribute is not allowed for flattened entry"
+ );
+ }
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods are not allowed for flattened entry"
+ );
+ }
+
+ // ignore doc comments
+ res.doc_comment = vec![];
+ }
+
+ Kind::ExternalSubcommand => (),
+
+ Kind::Subcommand(_) => {
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "parse attribute is not allowed for subcommand"
+ );
+ }
+
+ use syn::Fields::*;
+ use syn::FieldsUnnamed;
+ let field_ty = match variant.fields {
+ Named(_) => {
+ abort!(variant.span(), "structs are not allowed for subcommand");
+ }
+ Unit => abort!(variant.span(), "unit-type is not allowed for subcommand"),
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ &unnamed[0].ty
+ }
+ Unnamed(..) => {
+ abort!(
+ variant,
+ "non single-typed tuple is not allowed for subcommand"
+ )
+ }
+ };
+ let ty = Ty::from_syn_ty(field_ty);
+ match *ty {
+ Ty::OptionOption => {
+ abort!(
+ field_ty,
+ "Option<Option<T>> type is not allowed for subcommand"
+ );
+ }
+ Ty::OptionVec => {
+ abort!(
+ field_ty,
+ "Option<Vec<T>> type is not allowed for subcommand"
+ );
+ }
+ _ => (),
+ }
+
+ res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span());
+ }
+ Kind::Skip(_) => (),
+ Kind::FromGlobal(_) => {
+ abort!(res.kind.span(), "from_global is not supported on variants");
+ }
+ Kind::Arg(_) => (),
+ }
+
+ res
+ }
+
+ pub fn from_arg_enum_variant(
+ variant: &Variant,
+ argument_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let mut res = Self::new(
+ variant.span(),
+ Name::Derived(variant.ident.clone()),
+ None,
+ argument_casing,
+ env_casing,
+ );
+ res.push_attrs(&variant.attrs);
+ res.push_doc_comment(&variant.attrs, "help");
+
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "`parse` attribute is only allowed on fields"
+ );
+ }
+ match &*res.kind {
+ Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"),
+ Kind::Skip(_) => res,
+ Kind::Arg(_) => res,
+ Kind::FromGlobal(_) => abort!(res.kind.span(), "from_global is only allowed on fields"),
+ Kind::Flatten => abort!(res.kind.span(), "flatten is only allowed on fields"),
+ Kind::ExternalSubcommand => abort!(
+ res.kind.span(),
+ "external_subcommand is only allowed on fields"
+ ),
+ }
+ }
+
+ pub fn from_field(
+ field: &Field,
+ struct_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let name = field.ident.clone().unwrap();
+ let mut res = Self::new(
+ field.span(),
+ Name::Derived(name),
+ Some(field.ty.clone()),
+ struct_casing,
+ env_casing,
+ );
+ res.push_attrs(&field.attrs);
+ res.push_doc_comment(&field.attrs, "help");
+
+ match &*res.kind {
+ Kind::Flatten => {
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "parse attribute is not allowed for flattened entry"
+ );
+ }
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods are not allowed for flattened entry"
+ );
+ }
+
+ // ignore doc comments
+ res.doc_comment = vec![];
+ }
+
+ Kind::ExternalSubcommand => {
+ abort! { res.kind.span(),
+ "`external_subcommand` can be used only on enum variants"
+ }
+ }
+
+ Kind::Subcommand(_) => {
+ if res.has_custom_parser {
+ abort!(
+ res.parser.span(),
+ "parse attribute is not allowed for subcommand"
+ );
+ }
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods in attributes are not allowed for subcommand"
+ );
+ }
+
+ let ty = Ty::from_syn_ty(&field.ty);
+ match *ty {
+ Ty::OptionOption => {
+ abort!(
+ field.ty,
+ "Option<Option<T>> type is not allowed for subcommand"
+ );
+ }
+ Ty::OptionVec => {
+ abort!(
+ field.ty,
+ "Option<Vec<T>> type is not allowed for subcommand"
+ );
+ }
+ _ => (),
+ }
+
+ res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span());
+ }
+ Kind::Skip(_) => {
+ if res.has_explicit_methods() {
+ abort!(
+ res.kind.span(),
+ "methods are not allowed for skipped fields"
+ );
+ }
+ }
+ Kind::FromGlobal(orig_ty) => {
+ let ty = Ty::from_syn_ty(&field.ty);
+ res.kind = Sp::new(Kind::FromGlobal(ty), orig_ty.span());
+ }
+ Kind::Arg(orig_ty) => {
+ let mut ty = Ty::from_syn_ty(&field.ty);
+ if res.has_custom_parser {
+ match *ty {
+ Ty::Option | Ty::Vec | Ty::OptionVec => (),
+ _ => ty = Sp::new(Ty::Other, ty.span()),
+ }
+ }
+
+ match *ty {
+ Ty::Bool => {
+ if res.is_positional() && !res.has_custom_parser {
+ abort!(field.ty,
+ "`bool` cannot be used as positional parameter with default parser";
+ help = "if you want to create a flag add `long` or `short`";
+ help = "If you really want a boolean parameter \
+ add an explicit parser, for example `parse(try_from_str)`";
+ note = "see also https://github.com/clap-rs/clap/blob/master/examples/derive_ref/custom-bool.md";
+ )
+ }
+ if res.is_enum {
+ abort!(field.ty, "`arg_enum` is meaningless for bool")
+ }
+ if let Some(m) = res.find_default_method() {
+ abort!(m.name, "default_value is meaningless for bool")
+ }
+ if let Some(m) = res.find_method("required") {
+ abort!(m.name, "required is meaningless for bool")
+ }
+ }
+ Ty::Option => {
+ if let Some(m) = res.find_default_method() {
+ abort!(m.name, "default_value is meaningless for Option")
+ }
+ }
+ Ty::OptionOption => {
+ if res.is_positional() {
+ abort!(
+ field.ty,
+ "Option<Option<T>> type is meaningless for positional argument"
+ )
+ }
+ }
+ Ty::OptionVec => {
+ if res.is_positional() {
+ abort!(
+ field.ty,
+ "Option<Vec<T>> type is meaningless for positional argument"
+ )
+ }
+ }
+
+ _ => (),
+ }
+ res.kind = Sp::new(Kind::Arg(ty), orig_ty.span());
+ }
+ }
+
+ res
+ }
+
+ fn new(
+ default_span: Span,
+ name: Name,
+ ty: Option<Type>,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ Self {
+ name,
+ ty,
+ casing,
+ env_casing,
+ doc_comment: vec![],
+ methods: vec![],
+ parser: Parser::default_spanned(default_span),
+ verbatim_doc_comment: None,
+ next_display_order: None,
+ next_help_heading: None,
+ help_heading: None,
+ is_enum: false,
+ has_custom_parser: false,
+ kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span),
+ }
+ }
+
+ fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
+ if name == "name" {
+ self.name = Name::Assigned(quote!(#arg));
+ } else {
+ self.methods.push(Method::new(name, quote!(#arg)));
+ }
+ }
+
+ fn push_attrs(&mut self, attrs: &[Attribute]) {
+ use ClapAttr::*;
+
+ let parsed = parse_clap_attributes(attrs);
+ for attr in &parsed {
+ let attr = attr.clone();
+ match attr {
+ Short(ident) => {
+ self.push_method(ident, self.name.clone().translate_char(*self.casing));
+ }
+
+ Long(ident) => {
+ self.push_method(ident, self.name.clone().translate(*self.casing));
+ }
+
+ Env(ident) => {
+ self.push_method(ident, self.name.clone().translate(*self.env_casing));
+ }
+
+ ArgEnum(_) => self.is_enum = true,
+
+ FromGlobal(ident) => {
+ let ty = Sp::call_site(Ty::Other);
+ let kind = Sp::new(Kind::FromGlobal(ty), ident.span());
+ self.set_kind(kind);
+ }
+
+ Subcommand(ident) => {
+ let ty = Sp::call_site(Ty::Other);
+ let kind = Sp::new(Kind::Subcommand(ty), ident.span());
+ self.set_kind(kind);
+ }
+
+ ExternalSubcommand(ident) => {
+ let kind = Sp::new(Kind::ExternalSubcommand, ident.span());
+ self.set_kind(kind);
+ }
+
+ Flatten(ident) => {
+ let kind = Sp::new(Kind::Flatten, ident.span());
+ self.set_kind(kind);
+ }
+
+ Skip(ident, expr) => {
+ let kind = Sp::new(Kind::Skip(expr), ident.span());
+ self.set_kind(kind);
+ }
+
+ VerbatimDocComment(ident) => self.verbatim_doc_comment = Some(ident),
+
+ DefaultValueT(ident, expr) => {
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ ident,
+ "#[clap(default_value_t)] (without an argument) can be used \
+ only on field level";
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+
+ let val = if let Some(expr) = expr {
+ quote!(#expr)
+ } else {
+ quote!(<#ty as ::std::default::Default>::default())
+ };
+
+ let val = if parsed.iter().any(|a| matches!(a, ArgEnum(_))) {
+ quote_spanned!(ident.span()=> {
+ {
+ let val: #ty = #val;
+ clap::ArgEnum::to_possible_value(&val).unwrap().get_name()
+ }
+ })
+ } else {
+ quote_spanned!(ident.span()=> {
+ clap::lazy_static::lazy_static! {
+ static ref DEFAULT_VALUE: &'static str = {
+ let val: #ty = #val;
+ let s = ::std::string::ToString::to_string(&val);
+ ::std::boxed::Box::leak(s.into_boxed_str())
+ };
+ }
+ *DEFAULT_VALUE
+ })
+ };
+
+ let raw_ident = Ident::new("default_value", ident.span());
+ self.methods.push(Method::new(raw_ident, val));
+ }
+
+ DefaultValueOsT(ident, expr) => {
+ let ty = if let Some(ty) = self.ty.as_ref() {
+ ty
+ } else {
+ abort!(
+ ident,
+ "#[clap(default_value_os_t)] (without an argument) can be used \
+ only on field level";
+
+ note = "see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
+ };
+
+ let val = if let Some(expr) = expr {
+ quote!(#expr)
+ } else {
+ quote!(<#ty as ::std::default::Default>::default())
+ };
+
+ let val = if parsed.iter().any(|a| matches!(a, ArgEnum(_))) {
+ quote_spanned!(ident.span()=> {
+ {
+ let val: #ty = #val;
+ clap::ArgEnum::to_possible_value(&val).unwrap().get_name()
+ }
+ })
+ } else {
+ quote_spanned!(ident.span()=> {
+ clap::lazy_static::lazy_static! {
+ static ref DEFAULT_VALUE: &'static ::std::ffi::OsStr = {
+ let val: #ty = #val;
+ let s: ::std::ffi::OsString = val.into();
+ ::std::boxed::Box::leak(s.into_boxed_os_str())
+ };
+ }
+ *DEFAULT_VALUE
+ })
+ };
+
+ let raw_ident = Ident::new("default_value_os", ident.span());
+ self.methods.push(Method::new(raw_ident, val));
+ }
+
+ NextDisplayOrder(ident, expr) => {
+ self.next_display_order = Some(Method::new(ident, quote!(#expr)));
+ }
+
+ HelpHeading(ident, expr) => {
+ self.help_heading = Some(Method::new(ident, quote!(#expr)));
+ }
+ NextHelpHeading(ident, expr) => {
+ self.next_help_heading = Some(Method::new(ident, quote!(#expr)));
+ }
+
+ About(ident) => {
+ if let Some(method) = Method::from_env(ident, "CARGO_PKG_DESCRIPTION") {
+ self.methods.push(method);
+ }
+ }
+
+ Author(ident) => {
+ if let Some(method) = Method::from_env(ident, "CARGO_PKG_AUTHORS") {
+ self.methods.push(method);
+ }
+ }
+
+ Version(ident) => {
+ if let Some(method) = Method::from_env(ident, "CARGO_PKG_VERSION") {
+ self.methods.push(method);
+ }
+ }
+
+ NameLitStr(name, lit) => {
+ self.push_method(name, lit);
+ }
+
+ NameExpr(name, expr) => {
+ self.push_method(name, expr);
+ }
+
+ MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)),
+
+ RenameAll(_, casing_lit) => {
+ self.casing = CasingStyle::from_lit(casing_lit);
+ }
+
+ RenameAllEnv(_, casing_lit) => {
+ self.env_casing = CasingStyle::from_lit(casing_lit);
+ }
+
+ Parse(ident, spec) => {
+ self.has_custom_parser = true;
+ self.parser = Parser::from_spec(ident, spec);
+ }
+ }
+ }
+ }
+
+ fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) {
+ use syn::Lit::*;
+ use syn::Meta::*;
+
+ let comment_parts: Vec<_> = attrs
+ .iter()
+ .filter(|attr| attr.path.is_ident("doc"))
+ .filter_map(|attr| {
+ if let Ok(NameValue(MetaNameValue { lit: Str(s), .. })) = attr.parse_meta() {
+ Some(s.value())
+ } else {
+ // non #[doc = "..."] attributes are not our concern
+ // we leave them for rustc to handle
+ None
+ }
+ })
+ .collect();
+
+ self.doc_comment =
+ process_doc_comment(comment_parts, name, self.verbatim_doc_comment.is_none());
+ }
+
+ fn set_kind(&mut self, kind: Sp<Kind>) {
+ if let Kind::Arg(_) = *self.kind {
+ self.kind = kind;
+ } else {
+ abort!(
+ kind.span(),
+ "`subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together"
+ );
+ }
+ }
+
+ pub fn find_method(&self, name: &str) -> Option<&Method> {
+ self.methods.iter().find(|m| m.name == name)
+ }
+
+ 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();
+ let help_heading = self.help_heading.as_ref().into_iter();
+ quote!(
+ #(#next_display_order)*
+ #(#next_help_heading)*
+ #(#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, supports_long_help: bool) -> proc_macro2::TokenStream {
+ let methods = &self.methods;
+ let help_heading = self.help_heading.as_ref().into_iter();
+ match supports_long_help {
+ true => {
+ let doc_comment = &self.doc_comment;
+ quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* )
+ }
+ false => {
+ let doc_comment = self
+ .doc_comment
+ .iter()
+ .filter(|mth| mth.name != "long_help");
+ quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* )
+ }
+ }
+ }
+
+ 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();
+ let help_heading = self.help_heading.as_ref().into_iter();
+ quote!( #(#next_help_heading)* #(#help_heading)* )
+ }
+
+ #[cfg(feature = "unstable-v4")]
+ pub fn id(&self) -> TokenStream {
+ self.name.clone().raw()
+ }
+
+ #[cfg(not(feature = "unstable-v4"))]
+ pub fn id(&self) -> TokenStream {
+ self.cased_name()
+ }
+
+ 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 parser(&self) -> &Sp<Parser> {
+ &self.parser
+ }
+
+ pub fn kind(&self) -> Sp<Kind> {
+ self.kind.clone()
+ }
+
+ pub fn is_enum(&self) -> bool {
+ self.is_enum
+ }
+
+ pub fn ignore_case(&self) -> TokenStream {
+ let method = self.find_method("ignore_case");
+
+ if let Some(method) = method {
+ method.args.clone()
+ } else {
+ quote! { false }
+ }
+ }
+
+ pub fn casing(&self) -> Sp<CasingStyle> {
+ self.casing.clone()
+ }
+
+ pub fn env_casing(&self) -> Sp<CasingStyle> {
+ self.env_casing.clone()
+ }
+
+ pub fn is_positional(&self) -> bool {
+ self.methods
+ .iter()
+ .all(|m| m.name != "long" && m.name != "short")
+ }
+
+ pub fn has_explicit_methods(&self) -> bool {
+ self.methods
+ .iter()
+ .any(|m| m.name != "help" && m.name != "long_help")
+ }
+}
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Clone)]
+pub enum Kind {
+ Arg(Sp<Ty>),
+ FromGlobal(Sp<Ty>),
+ Subcommand(Sp<Ty>),
+ Flatten,
+ Skip(Option<Expr>),
+ ExternalSubcommand,
+}
+
+#[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) -> Option<Self> {
+ let mut lit = match env::var(env_var) {
+ Ok(val) => {
+ if val.is_empty() {
+ return None;
+ }
+ LitStr::new(&val, ident.span())
+ }
+ Err(_) => {
+ abort!(ident,
+ "cannot derive `{}` from Cargo.toml", ident;
+ note = "`{}` environment variable is not set", env_var;
+ help = "use `{} = \"...\"` to set {} manually", ident, ident;
+ );
+ }
+ };
+
+ if ident == "author" {
+ let edited = process_author_str(&lit.value());
+ lit = LitStr::new(&edited, lit.span());
+ }
+
+ Some(Method::new(ident, quote!(#lit)))
+ }
+}
+
+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);
+ }
+}
+
+/// 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
+}
+
+#[derive(Clone)]
+pub struct Parser {
+ pub kind: Sp<ParserKind>,
+ pub func: TokenStream,
+}
+
+impl Parser {
+ fn default_spanned(span: Span) -> Sp<Self> {
+ let kind = Sp::new(ParserKind::TryFromStr, span);
+ let func = quote_spanned!(span=> ::std::str::FromStr::from_str);
+ Sp::new(Parser { kind, func }, span)
+ }
+
+ fn from_spec(parse_ident: Ident, spec: ParserSpec) -> Sp<Self> {
+ use self::ParserKind::*;
+
+ let kind = match &*spec.kind.to_string() {
+ "from_str" => FromStr,
+ "try_from_str" => TryFromStr,
+ "from_os_str" => FromOsStr,
+ "try_from_os_str" => TryFromOsStr,
+ "from_occurrences" => FromOccurrences,
+ "from_flag" => FromFlag,
+ s => abort!(spec.kind.span(), "unsupported parser `{}`", s),
+ };
+
+ let func = match spec.parse_func {
+ None => match kind {
+ FromStr | FromOsStr => {
+ quote_spanned!(spec.kind.span()=> ::std::convert::From::from)
+ }
+ TryFromStr => quote_spanned!(spec.kind.span()=> ::std::str::FromStr::from_str),
+ TryFromOsStr => abort!(
+ spec.kind.span(),
+ "you must set parser for `try_from_os_str` explicitly"
+ ),
+ FromOccurrences => quote_spanned!(spec.kind.span()=> { |v| v as _ }),
+ FromFlag => quote_spanned!(spec.kind.span()=> ::std::convert::From::from),
+ },
+
+ Some(func) => match func {
+ Expr::Path(_) => quote!(#func),
+ _ => abort!(func, "`parse` argument must be a function path"),
+ },
+ };
+
+ let kind = Sp::new(kind, spec.kind.span());
+ let parser = Parser { kind, func };
+ Sp::new(parser, parse_ident.span())
+ }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum ParserKind {
+ FromStr,
+ TryFromStr,
+ FromOsStr,
+ TryFromOsStr,
+ FromOccurrences,
+ FromFlag,
+}
+
+/// Defines the casing for the attributes long representation.
+#[derive(Copy, Clone, Debug, PartialEq)]
+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) -> Sp<Self> {
+ use self::CasingStyle::*;
+
+ let normalized = name.value().to_upper_camel_case().to_lowercase();
+ let cs = |kind| Sp::new(kind, name.span());
+
+ 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),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum Name {
+ Derived(Ident),
+ Assigned(TokenStream),
+}
+
+impl Name {
+ #[cfg(feature = "unstable-v4")]
+ 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/derives/arg_enum.rs b/third_party/rust/clap_derive/src/derives/arg_enum.rs
new file mode 100644
index 0000000000..77067e9029
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/arg_enum.rs
@@ -0,0 +1,123 @@
+// 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 crate::{
+ attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
+ dummies,
+ utils::Sp,
+};
+
+use proc_macro2::{Span, TokenStream};
+use proc_macro_error::{abort, abort_call_site};
+use quote::quote;
+use syn::{
+ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataEnum, DeriveInput,
+ Fields, Ident, Variant,
+};
+
+pub fn derive_arg_enum(input: &DeriveInput) -> TokenStream {
+ let ident = &input.ident;
+
+ dummies::arg_enum(ident);
+
+ match input.data {
+ Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
+ _ => abort_call_site!("`#[derive(ArgEnum)]` only supports enums"),
+ }
+}
+
+pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Derived(name.clone()),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+
+ let lits = lits(&e.variants, &attrs);
+ let value_variants = gen_value_variants(&lits);
+ let to_possible_value = gen_to_possible_value(&lits);
+
+ quote! {
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl clap::ArgEnum for #name {
+ #value_variants
+ #to_possible_value
+ }
+ }
+}
+
+fn lits(
+ variants: &Punctuated<Variant, Comma>,
+ parent_attribute: &Attrs,
+) -> Vec<(TokenStream, Ident)> {
+ variants
+ .iter()
+ .filter_map(|variant| {
+ let attrs = Attrs::from_arg_enum_variant(
+ variant,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ if let Kind::Skip(_) = &*attrs.kind() {
+ None
+ } else {
+ if !matches!(variant.fields, Fields::Unit) {
+ abort!(variant.span(), "`#[derive(ArgEnum)]` only supports non-unit variants, unless they are skipped");
+ }
+ let fields = attrs.field_methods(false);
+ let name = attrs.cased_name();
+ Some((
+ quote! {
+ clap::PossibleValue::new(#name)
+ #fields
+ },
+ variant.ident.clone(),
+ ))
+ }
+ })
+ .collect::<Vec<_>>()
+}
+
+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(lits: &[(TokenStream, Ident)]) -> TokenStream {
+ let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();
+
+ quote! {
+ fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
+ match self {
+ #(Self::#variant => Some(#lit),)*
+ _ => None
+ }
+ }
+ }
+}
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..e1486ea2bd
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/args.rs
@@ -0,0 +1,651 @@
+// 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 crate::{
+ attrs::{Attrs, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING},
+ dummies,
+ utils::{inner_type, sub_type, Sp, Ty},
+};
+
+use proc_macro2::{Ident, Span, TokenStream};
+use proc_macro_error::{abort, abort_call_site};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DataStruct,
+ DeriveInput, Field, Fields, Generics, Type,
+};
+
+pub fn derive_args(input: &DeriveInput) -> TokenStream {
+ let ident = &input.ident;
+
+ dummies::args(ident);
+
+ match input.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref fields),
+ ..
+ }) => gen_for_struct(ident, &input.generics, &fields.named, &input.attrs),
+ Data::Struct(DataStruct {
+ fields: Fields::Unit,
+ ..
+ }) => gen_for_struct(
+ ident,
+ &input.generics,
+ &Punctuated::<Field, Comma>::new(),
+ &input.attrs,
+ ),
+ _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
+ }
+}
+
+pub fn gen_for_struct(
+ struct_name: &Ident,
+ generics: &Generics,
+ fields: &Punctuated<Field, Comma>,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let from_arg_matches = gen_from_arg_matches_for_struct(struct_name, generics, fields, attrs);
+
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Derived(struct_name.clone()),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+ let app_var = Ident::new("__clap_app", Span::call_site());
+ let augmentation = gen_augment(fields, &app_var, &attrs, false);
+ let augmentation_update = gen_augment(fields, &app_var, &attrs, true);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ #from_arg_matches
+
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl #impl_generics clap::Args for #struct_name #ty_generics #where_clause {
+ fn augment_args<'b>(#app_var: clap::Command<'b>) -> clap::Command<'b> {
+ #augmentation
+ }
+ fn augment_args_for_update<'b>(#app_var: clap::Command<'b>) -> clap::Command<'b> {
+ #augmentation_update
+ }
+ }
+ }
+}
+
+pub fn gen_from_arg_matches_for_struct(
+ struct_name: &Ident,
+ generics: &Generics,
+ fields: &Punctuated<Field, Comma>,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Derived(struct_name.clone()),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+
+ let constructor = gen_constructor(fields, &attrs);
+ let updater = gen_updater(fields, &attrs, true);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl #impl_generics clap::FromArgMatches for #struct_name #ty_generics #where_clause {
+ fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ let v = #struct_name #constructor;
+ ::std::result::Result::Ok(v)
+ }
+
+ fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+ #updater
+ ::std::result::Result::Ok(())
+ }
+ }
+ }
+}
+
+/// Generate a block of code to add arguments/subcommands corresponding to
+/// the `fields` to an cmd.
+pub fn gen_augment(
+ fields: &Punctuated<Field, Comma>,
+ app_var: &Ident,
+ parent_attribute: &Attrs,
+ override_required: bool,
+) -> TokenStream {
+ let mut subcmds = fields.iter().filter_map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let kind = attrs.kind();
+ if let Kind::Subcommand(ty) = &*kind {
+ let subcmd_type = match (**ty, sub_type(&field.ty)) {
+ (Ty::Option, Some(sub_type)) => sub_type,
+ _ => &field.ty,
+ };
+ let required = if **ty == Ty::Option {
+ quote!()
+ } else {
+ quote_spanned! { kind.span()=>
+ #[allow(deprecated)]
+ let #app_var = #app_var.setting(
+ clap::AppSettings::SubcommandRequiredElseHelp
+ );
+ }
+ };
+
+ let span = field.span();
+ let ts = if override_required {
+ quote! {
+ let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands_for_update( #app_var );
+ }
+ } else{
+ quote! {
+ let #app_var = <#subcmd_type as clap::Subcommand>::augment_subcommands( #app_var );
+ #required
+ }
+ };
+ Some((span, ts))
+ } else {
+ None
+ }
+ });
+ let subcmd = subcmds.next().map(|(_, ts)| ts);
+ if let Some((span, _)) = subcmds.next() {
+ abort!(
+ span,
+ "multiple subcommand sets are not allowed, that's the second"
+ );
+ }
+
+ let args = fields.iter().filter_map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let kind = attrs.kind();
+ match &*kind {
+ Kind::Subcommand(_)
+ | Kind::Skip(_)
+ | Kind::FromGlobal(_)
+ | Kind::ExternalSubcommand => None,
+ Kind::Flatten => {
+ let ty = &field.ty;
+ let old_heading_var = format_ident!("__clap_old_heading");
+ let next_help_heading = attrs.next_help_heading();
+ let next_display_order = attrs.next_display_order();
+ if override_required {
+ Some(quote_spanned! { kind.span()=>
+ let #old_heading_var = #app_var.get_next_help_heading();
+ let #app_var = #app_var #next_help_heading #next_display_order;
+ let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var);
+ let #app_var = #app_var.next_help_heading(#old_heading_var);
+ })
+ } else {
+ Some(quote_spanned! { kind.span()=>
+ let #old_heading_var = #app_var.get_next_help_heading();
+ let #app_var = #app_var #next_help_heading #next_display_order;
+ let #app_var = <#ty as clap::Args>::augment_args(#app_var);
+ let #app_var = #app_var.next_help_heading(#old_heading_var);
+ })
+ }
+ }
+ Kind::Arg(ty) => {
+ let convert_type = inner_type(**ty, &field.ty);
+
+ let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
+ let flag = *attrs.parser().kind == ParserKind::FromFlag;
+
+ let parser = attrs.parser();
+ let func = &parser.func;
+
+ let validator = match *parser.kind {
+ _ if attrs.is_enum() => quote!(),
+ ParserKind::TryFromStr => quote_spanned! { func.span()=>
+ .validator(|s| {
+ #func(s)
+ .map(|_: #convert_type| ())
+ })
+ },
+ ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
+ .validator_os(|s| #func(s).map(|_: #convert_type| ()))
+ },
+ ParserKind::FromStr
+ | ParserKind::FromOsStr
+ | ParserKind::FromFlag
+ | ParserKind::FromOccurrences => quote!(),
+ };
+ let allow_invalid_utf8 = match *parser.kind {
+ _ if attrs.is_enum() => quote!(),
+ ParserKind::FromOsStr | ParserKind::TryFromOsStr => {
+ quote_spanned! { func.span()=>
+ .allow_invalid_utf8(true)
+ }
+ }
+ ParserKind::FromStr
+ | ParserKind::TryFromStr
+ | ParserKind::FromFlag
+ | ParserKind::FromOccurrences => quote!(),
+ };
+
+ let value_name = attrs.value_name();
+ let possible_values = if attrs.is_enum() {
+ gen_arg_enum_possible_values(convert_type)
+ } else {
+ quote!()
+ };
+
+ let modifier = match **ty {
+ Ty::Bool => quote!(),
+
+ Ty::Option => {
+ quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .value_name(#value_name)
+ #possible_values
+ #validator
+ #allow_invalid_utf8
+ }
+ }
+
+ Ty::OptionOption => quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .value_name(#value_name)
+ .min_values(0)
+ .max_values(1)
+ .multiple_values(false)
+ #possible_values
+ #validator
+ #allow_invalid_utf8
+ },
+
+ Ty::OptionVec => quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .value_name(#value_name)
+ .multiple_occurrences(true)
+ #possible_values
+ #validator
+ #allow_invalid_utf8
+ },
+
+ Ty::Vec => {
+ quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .value_name(#value_name)
+ .multiple_occurrences(true)
+ #possible_values
+ #validator
+ #allow_invalid_utf8
+ }
+ }
+
+ Ty::Other if occurrences => quote_spanned! { ty.span()=>
+ .multiple_occurrences(true)
+ },
+
+ Ty::Other if flag => quote_spanned! { ty.span()=>
+ .takes_value(false)
+ },
+
+ Ty::Other => {
+ let required = attrs.find_default_method().is_none() && !override_required;
+ quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .value_name(#value_name)
+ .required(#required)
+ #possible_values
+ #validator
+ #allow_invalid_utf8
+ }
+ }
+ };
+
+ let id = attrs.id();
+ let methods = attrs.field_methods(true);
+
+ Some(quote_spanned! { field.span()=>
+ let #app_var = #app_var.arg(
+ clap::Arg::new(#id)
+ #modifier
+ #methods
+ );
+ })
+ }
+ }
+ });
+
+ let initial_app_methods = parent_attribute.initial_top_level_methods();
+ let final_app_methods = parent_attribute.final_top_level_methods();
+ quote! {{
+ let #app_var = #app_var #initial_app_methods;
+ #( #args )*
+ #subcmd
+ #app_var #final_app_methods
+ }}
+}
+
+fn gen_arg_enum_possible_values(ty: &Type) -> TokenStream {
+ quote_spanned! { ty.span()=>
+ .possible_values(<#ty as clap::ArgEnum>::value_variants().iter().filter_map(clap::ArgEnum::to_possible_value))
+ }
+}
+
+pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
+ let fields = fields.iter().map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let field_name = field.ident.as_ref().unwrap();
+ let kind = attrs.kind();
+ let arg_matches = format_ident!("__clap_arg_matches");
+ match &*kind {
+ Kind::ExternalSubcommand => {
+ abort! { kind.span(),
+ "`external_subcommand` can be used only on enum variants"
+ }
+ }
+ 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(#arg_matches)?)
+ } else {
+ None
+ }
+ }
+ }
+ },
+ _ => {
+ quote_spanned! { kind.span()=>
+ #field_name: {
+ <#subcmd_type as clap::FromArgMatches>::from_arg_matches(#arg_matches)?
+ }
+ }
+ },
+ }
+ }
+
+ Kind::Flatten => quote_spanned! { kind.span()=>
+ #field_name: clap::FromArgMatches::from_arg_matches(#arg_matches)?
+ },
+
+ 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(&attrs, ty, field_name, field, None)
+ }
+ }
+ });
+
+ quote! {{
+ #( #fields ),*
+ }}
+}
+
+pub fn gen_updater(
+ fields: &Punctuated<Field, Comma>,
+ parent_attribute: &Attrs,
+ use_self: bool,
+) -> TokenStream {
+ let fields = fields.iter().map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let field_name = field.ident.as_ref().unwrap();
+ let kind = attrs.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");
+
+ match &*kind {
+ Kind::ExternalSubcommand => {
+ abort! { kind.span(),
+ "`external_subcommand` can be used only on enum variants"
+ }
+ }
+ 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(#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(
+ #arg_matches
+ )?);
+ }
+ },
+ _ => quote_spanned! { kind.span()=>
+ #updater
+ },
+ };
+
+ quote_spanned! { kind.span()=>
+ {
+ #access
+ #updater
+ }
+ }
+ }
+
+ Kind::Flatten => quote_spanned! { kind.span()=> {
+ #access
+ clap::FromArgMatches::update_from_arg_matches(#field_name, #arg_matches)?;
+ }
+ },
+
+ Kind::Skip(_) => quote!(),
+
+ Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(&attrs, ty, field_name, field, Some(&access)),
+ }
+ });
+
+ quote! {
+ #( #fields )*
+ }
+}
+
+fn gen_parsers(
+ attrs: &Attrs,
+ ty: &Sp<Ty>,
+ field_name: &Ident,
+ field: &Field,
+ update: Option<&TokenStream>,
+) -> TokenStream {
+ use self::ParserKind::*;
+
+ let parser = attrs.parser();
+ let func = &parser.func;
+ let span = parser.kind.span();
+ let convert_type = inner_type(**ty, &field.ty);
+ let id = attrs.id();
+ let (value_of, values_of, mut parse) = match *parser.kind {
+ FromStr => (
+ quote_spanned!(span=> value_of),
+ quote_spanned!(span=> values_of),
+ quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
+ ),
+ TryFromStr => (
+ quote_spanned!(span=> value_of),
+ quote_spanned!(span=> values_of),
+ quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
+ ),
+ FromOsStr => (
+ quote_spanned!(span=> value_of_os),
+ quote_spanned!(span=> values_of_os),
+ quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
+ ),
+ TryFromOsStr => (
+ quote_spanned!(span=> value_of_os),
+ quote_spanned!(span=> values_of_os),
+ quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
+ ),
+ FromOccurrences => (
+ quote_spanned!(span=> occurrences_of),
+ quote!(),
+ func.clone(),
+ ),
+ FromFlag => (quote!(), quote!(), func.clone()),
+ };
+ if attrs.is_enum() {
+ let ci = attrs.ignore_case();
+
+ parse = quote_spanned! { convert_type.span()=>
+ |s| <#convert_type as clap::ArgEnum>::from_str(s, #ci).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))
+ }
+ }
+
+ let flag = *attrs.parser().kind == ParserKind::FromFlag;
+ let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
+ // 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::Bool => {
+ if update.is_some() {
+ quote_spanned! { ty.span()=>
+ *#field_name || #arg_matches.is_present(#id)
+ }
+ } else {
+ quote_spanned! { ty.span()=>
+ #arg_matches.is_present(#id)
+ }
+ }
+ }
+
+ Ty::Option => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#value_of(#id)
+ .map(#parse)
+ .transpose()?
+ }
+ }
+
+ Ty::OptionOption => quote_spanned! { ty.span()=>
+ if #arg_matches.is_present(#id) {
+ Some(#arg_matches.#value_of(#id).map(#parse).transpose()?)
+ } else {
+ None
+ }
+ },
+
+ Ty::OptionVec => quote_spanned! { ty.span()=>
+ if #arg_matches.is_present(#id) {
+ Some(#arg_matches.#values_of(#id)
+ .map(|v| v.map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
+ .transpose()?
+ .unwrap_or_else(Vec::new))
+ } else {
+ None
+ }
+ },
+
+ Ty::Vec => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#values_of(#id)
+ .map(|v| v.map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
+ .transpose()?
+ .unwrap_or_else(Vec::new)
+ }
+ }
+
+ Ty::Other if occurrences => quote_spanned! { ty.span()=>
+ #parse(#arg_matches.#value_of(#id))
+ },
+
+ Ty::Other if flag => quote_spanned! { ty.span()=>
+ #parse(#arg_matches.is_present(#id))
+ },
+
+ Ty::Other => {
+ quote_spanned! { ty.span()=>
+ #arg_matches.#value_of(#id)
+ .ok_or_else(|| clap::Error::raw(clap::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))
+ .and_then(#parse)?
+ }
+ }
+ };
+
+ if let Some(access) = update {
+ quote_spanned! { field.span()=>
+ if #arg_matches.is_present(#id) {
+ #access
+ *#field_name = #field_value
+ }
+ }
+ } else {
+ quote_spanned!(field.span()=> #field_name: #field_value )
+ }
+}
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..3197357534
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/into_app.rs
@@ -0,0 +1,119 @@
+// 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 proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{Attribute, Generics, Ident};
+
+use crate::{
+ attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
+ utils::Sp,
+};
+
+pub fn gen_for_struct(
+ struct_name: &Ident,
+ generics: &Generics,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
+
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Assigned(quote!(#app_name)),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+ let name = attrs.cased_name();
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let tokens = quote! {
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ #[allow(deprecated)]
+ impl #impl_generics clap::CommandFactory for #struct_name #ty_generics #where_clause {
+ fn into_app<'b>() -> clap::Command<'b> {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Args>::augment_args(#app_var)
+ }
+
+ fn into_app_for_update<'b>() -> clap::Command<'b> {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Args>::augment_args_for_update(#app_var)
+ }
+ }
+ };
+
+ tokens
+}
+
+pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute]) -> TokenStream {
+ let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
+
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Assigned(quote!(#app_name)),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+ let name = attrs.cased_name();
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl #impl_generics clap::CommandFactory for #enum_name #ty_generics #where_clause {
+ fn into_app<'b>() -> clap::Command<'b> {
+ #[allow(deprecated)]
+ let #app_var = clap::Command::new(#name)
+ .setting(clap::AppSettings::SubcommandRequiredElseHelp);
+ <Self as clap::Subcommand>::augment_subcommands(#app_var)
+ }
+
+ fn into_app_for_update<'b>() -> clap::Command<'b> {
+ let #app_var = clap::Command::new(#name);
+ <Self as clap::Subcommand>::augment_subcommands_for_update(#app_var)
+ }
+ }
+ }
+}
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..ee0cce56e7
--- /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 arg_enum;
+mod args;
+mod into_app;
+mod parser;
+mod subcommand;
+
+pub use self::parser::derive_parser;
+pub use arg_enum::derive_arg_enum;
+pub use args::derive_args;
+pub use subcommand::derive_subcommand;
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..621430f310
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/parser.rs
@@ -0,0 +1,95 @@
+// 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 crate::{
+ derives::{args, into_app, subcommand},
+ dummies,
+};
+
+use proc_macro2::TokenStream;
+use proc_macro_error::abort_call_site;
+use quote::quote;
+use syn::{
+ self, punctuated::Punctuated, token::Comma, Attribute, Data, DataEnum, DataStruct, DeriveInput,
+ Field, Fields, Generics, Ident,
+};
+
+pub fn derive_parser(input: &DeriveInput) -> TokenStream {
+ let ident = &input.ident;
+
+ match input.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(ref fields),
+ ..
+ }) => {
+ dummies::parser_struct(ident);
+ gen_for_struct(ident, &input.generics, &fields.named, &input.attrs)
+ }
+ Data::Struct(DataStruct {
+ fields: Fields::Unit,
+ ..
+ }) => {
+ dummies::parser_struct(ident);
+ gen_for_struct(
+ ident,
+ &input.generics,
+ &Punctuated::<Field, Comma>::new(),
+ &input.attrs,
+ )
+ }
+ Data::Enum(ref e) => {
+ dummies::parser_enum(ident);
+ gen_for_enum(ident, &input.generics, &input.attrs, e)
+ }
+ _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
+ }
+}
+
+fn gen_for_struct(
+ name: &Ident,
+ generics: &Generics,
+ fields: &Punctuated<Field, Comma>,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let into_app = into_app::gen_for_struct(name, generics, attrs);
+ let args = args::gen_for_struct(name, generics, fields, attrs);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ impl #impl_generics clap::Parser for #name #ty_generics #where_clause {}
+
+ #into_app
+ #args
+ }
+}
+
+fn gen_for_enum(
+ name: &Ident,
+ generics: &Generics,
+ attrs: &[Attribute],
+ e: &DataEnum,
+) -> TokenStream {
+ let into_app = into_app::gen_for_enum(name, generics, attrs);
+ let subcommand = subcommand::gen_for_enum(name, generics, attrs, e);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ impl #impl_generics clap::Parser for #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..7d12347985
--- /dev/null
+++ b/third_party/rust/clap_derive/src/derives/subcommand.rs
@@ -0,0 +1,666 @@
+// 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 crate::{
+ attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
+ derives::args,
+ dummies,
+ utils::{is_simple_ty, subty_if_name, Sp},
+};
+
+use proc_macro2::{Ident, Span, TokenStream};
+use proc_macro_error::{abort, abort_call_site};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+ punctuated::Punctuated, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput,
+ FieldsUnnamed, Generics, Token, Variant,
+};
+
+pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
+ let ident = &input.ident;
+
+ dummies::subcommand(ident);
+
+ match input.data {
+ Data::Enum(ref e) => gen_for_enum(ident, &input.generics, &input.attrs, e),
+ _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
+ }
+}
+
+pub fn gen_for_enum(
+ enum_name: &Ident,
+ generics: &Generics,
+ attrs: &[Attribute],
+ e: &DataEnum,
+) -> TokenStream {
+ let from_arg_matches = gen_from_arg_matches_for_enum(enum_name, generics, attrs, e);
+
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Derived(enum_name.clone()),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+ let augmentation = gen_augment(&e.variants, &attrs, false);
+ let augmentation_update = gen_augment(&e.variants, &attrs, true);
+ let has_subcommand = gen_has_subcommand(&e.variants, &attrs);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ #from_arg_matches
+
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl #impl_generics clap::Subcommand for #enum_name #ty_generics #where_clause {
+ fn augment_subcommands <'b>(__clap_app: clap::Command<'b>) -> clap::Command<'b> {
+ #augmentation
+ }
+ fn augment_subcommands_for_update <'b>(__clap_app: clap::Command<'b>) -> clap::Command<'b> {
+ #augmentation_update
+ }
+ fn has_subcommand(__clap_name: &str) -> bool {
+ #has_subcommand
+ }
+ }
+ }
+}
+
+fn gen_from_arg_matches_for_enum(
+ name: &Ident,
+ generics: &Generics,
+ attrs: &[Attribute],
+ e: &DataEnum,
+) -> TokenStream {
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Derived(name.clone()),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+
+ let from_arg_matches = gen_from_arg_matches(name, &e.variants, &attrs);
+ let update_from_arg_matches = gen_update_from_arg_matches(name, &e.variants, &attrs);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ quote! {
+ #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
+ #[allow(
+ clippy::style,
+ clippy::complexity,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::perf,
+ clippy::deprecated,
+ clippy::nursery,
+ clippy::cargo,
+ clippy::suspicious_else_formatting,
+ )]
+ #[deny(clippy::correctness)]
+ impl #impl_generics clap::FromArgMatches for #name #ty_generics #where_clause {
+ #from_arg_matches
+ #update_from_arg_matches
+ }
+ }
+}
+
+fn gen_augment(
+ variants: &Punctuated<Variant, Token![,]>,
+ parent_attribute: &Attrs,
+ override_required: bool,
+) -> TokenStream {
+ use syn::Fields::*;
+
+ let app_var = Ident::new("__clap_app", Span::call_site());
+
+ let subcommands: Vec<_> = variants
+ .iter()
+ .filter_map(|variant| {
+ let attrs = Attrs::from_variant(
+ variant,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let kind = attrs.kind();
+
+ match &*kind {
+ Kind::Skip(_) => 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 subcommand = match subty_if_name(ty, "Vec") {
+ Some(subty) => {
+ if is_simple_ty(subty, "OsString") {
+ quote_spanned! { kind.span()=>
+ let #app_var = #app_var.allow_external_subcommands(true).allow_invalid_utf8_for_external_subcommands(true);
+ }
+ } else {
+ quote_spanned! { kind.span()=>
+ let #app_var = #app_var.allow_external_subcommands(true);
+ }
+ }
+ }
+
+ None => abort!(
+ ty.span(),
+ "The type must be `Vec<_>` \
+ to be used with `external_subcommand`."
+ ),
+ };
+ Some(subcommand)
+ }
+
+ Kind::Flatten => match variant.fields {
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ let ty = &unnamed[0];
+ let old_heading_var = format_ident!("__clap_old_heading");
+ let next_help_heading = attrs.next_help_heading();
+ let next_display_order = attrs.next_display_order();
+ let subcommand = if override_required {
+ quote! {
+ let #old_heading_var = #app_var.get_next_help_heading();
+ let #app_var = #app_var #next_help_heading #next_display_order;
+ let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
+ let #app_var = #app_var.next_help_heading(#old_heading_var);
+ }
+ } else {
+ quote! {
+ let #old_heading_var = #app_var.get_next_help_heading();
+ let #app_var = #app_var #next_help_heading #next_display_order;
+ let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
+ let #app_var = #app_var.next_help_heading(#old_heading_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];
+ 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 = attrs.cased_name();
+ let initial_app_methods = attrs.initial_top_level_methods();
+ let final_from_attrs = attrs.final_top_level_methods();
+ let subcommand = quote! {
+ let #app_var = #app_var.subcommand({
+ let #subcommand_var = clap::Command::new(#name);
+ let #subcommand_var = #subcommand_var #initial_app_methods;
+ let #subcommand_var = #arg_block;
+ #[allow(deprecated)]
+ let #subcommand_var = #subcommand_var.setting(clap::AppSettings::SubcommandRequiredElseHelp);
+ #subcommand_var #final_from_attrs
+ });
+ };
+ Some(subcommand)
+ }
+
+ _ => {
+ 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
+ args::gen_augment(&fields.named, &subcommand_var, &attrs, override_required)
+ }
+ Unit => {
+ let arg_block = quote!( #subcommand_var );
+ let initial_app_methods = attrs.initial_top_level_methods();
+ let final_from_attrs = attrs.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];
+ 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 = attrs.initial_top_level_methods();
+ let final_from_attrs = attrs.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 name = attrs.cased_name();
+ let subcommand = quote! {
+ let #app_var = #app_var.subcommand({
+ let #subcommand_var = clap::Command::new(#name);
+ #sub_augment
+ });
+ };
+ Some(subcommand)
+ }
+ }
+ })
+ .collect();
+
+ let initial_app_methods = parent_attribute.initial_top_level_methods();
+ let final_app_methods = parent_attribute.final_top_level_methods();
+ quote! {
+ let #app_var = #app_var #initial_app_methods;
+ #( #subcommands )*;
+ #app_var #final_app_methods
+ }
+}
+
+fn gen_has_subcommand(
+ variants: &Punctuated<Variant, Token![,]>,
+ parent_attribute: &Attrs,
+) -> TokenStream {
+ use syn::Fields::*;
+
+ let mut ext_subcmd = false;
+
+ let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
+ .iter()
+ .filter_map(|variant| {
+ let attrs = Attrs::from_variant(
+ variant,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+
+ if let Kind::ExternalSubcommand = &*attrs.kind() {
+ ext_subcmd = true;
+ None
+ } else {
+ Some((variant, attrs))
+ }
+ })
+ .partition(|(_, attrs)| {
+ let kind = attrs.kind();
+ matches!(&*kind, Kind::Flatten)
+ });
+
+ let subcommands = variants.iter().map(|(_variant, attrs)| {
+ let sub_name = attrs.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];
+ quote! {
+ if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
+ return true;
+ }
+ }
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ });
+
+ if ext_subcmd {
+ quote! { true }
+ } else {
+ quote! {
+ #( #subcommands )*
+
+ #( #child_subcommands )else*
+
+ false
+ }
+ }
+}
+
+fn gen_from_arg_matches(
+ name: &Ident,
+ variants: &Punctuated<Variant, Token![,]>,
+ parent_attribute: &Attrs,
+) -> TokenStream {
+ use syn::Fields::*;
+
+ let mut ext_subcmd = None;
+
+ let subcommand_name_var = format_ident!("__clap_name");
+ let sub_arg_matches_var = format_ident!("__clap_sub_arg_matches");
+ let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
+ .iter()
+ .filter_map(|variant| {
+ let attrs = Attrs::from_variant(
+ variant,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+
+ if let Kind::ExternalSubcommand = &*attrs.kind() {
+ if ext_subcmd.is_some() {
+ abort!(
+ attrs.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, values_of) = match subty_if_name(ty, "Vec") {
+ Some(subty) => {
+ if is_simple_ty(subty, "String") {
+ (
+ subty.span(),
+ quote!(::std::string::String),
+ quote!(values_of),
+ )
+ } else if is_simple_ty(subty, "OsString") {
+ (
+ subty.span(),
+ quote!(::std::ffi::OsString),
+ quote!(values_of_os),
+ )
+ } 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, values_of));
+ None
+ } else {
+ Some((variant, attrs))
+ }
+ })
+ .partition(|(_, attrs)| {
+ let kind = attrs.kind();
+ matches!(&*kind, Kind::Flatten)
+ });
+
+ let subcommands = variants.iter().map(|(variant, attrs)| {
+ let sub_name = attrs.cased_name();
+ let variant_name = &variant.ident;
+ let constructor_block = match variant.fields {
+ Named(ref fields) => args::gen_constructor(&fields.named, attrs),
+ Unit => quote!(),
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0];
+ quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)? ) )
+ }
+ Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
+ };
+
+ if cfg!(feature = "unstable-v4") {
+ quote! {
+ if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.is_present("") {
+ return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
+ }
+ }
+ } else {
+ quote! {
+ if #sub_name == #subcommand_name_var {
+ return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
+ }
+ }
+ }
+ });
+ 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];
+ quote! {
+ if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
+ let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)?;
+ return ::std::result::Result::Ok(#name :: #variant_name (__clap_res));
+ }
+ }
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ }
+ });
+
+ let wildcard = match ext_subcmd {
+ Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
+ ::std::result::Result::Ok(#name::#var_name(
+ ::std::iter::once(#str_ty::from(#subcommand_name_var))
+ .chain(
+ #sub_arg_matches_var.#values_of("").into_iter().flatten().map(#str_ty::from)
+ )
+ .collect::<::std::vec::Vec<_>>()
+ ))
+ },
+
+ None => quote! {
+ ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::UnrecognizedSubcommand, format!("The subcommand '{}' wasn't recognized", #subcommand_name_var)))
+ },
+ };
+
+ quote! {
+ fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+ if let Some((#subcommand_name_var, #sub_arg_matches_var)) = __clap_arg_matches.subcommand() {
+ {
+ let __clap_arg_matches = #sub_arg_matches_var;
+ #( #subcommands )*
+ }
+
+ #( #child_subcommands )else*
+
+ #wildcard
+ } else {
+ ::std::result::Result::Err(clap::Error::raw(clap::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
+ }
+ }
+ }
+}
+
+fn gen_update_from_arg_matches(
+ name: &Ident,
+ variants: &Punctuated<Variant, Token![,]>,
+ parent_attribute: &Attrs,
+) -> TokenStream {
+ use syn::Fields::*;
+
+ let (flatten, variants): (Vec<_>, Vec<_>) = variants
+ .iter()
+ .filter_map(|variant| {
+ let attrs = Attrs::from_variant(
+ variant,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+
+ match &*attrs.kind() {
+ // Fallback to `from_arg_matches`
+ Kind::ExternalSubcommand => None,
+ _ => Some((variant, attrs)),
+ }
+ })
+ .partition(|(_, attrs)| {
+ let kind = attrs.kind();
+ matches!(&*kind, Kind::Flatten)
+ });
+
+ let subcommands = variants.iter().map(|(variant, attrs)| {
+ let sub_name = attrs.cased_name();
+ let variant_name = &variant.ident;
+ let (pattern, updater) = match variant.fields {
+ Named(ref fields) => {
+ let (fields, update): (Vec<_>, Vec<_>) = fields
+ .named
+ .iter()
+ .map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let field_name = field.ident.as_ref().unwrap();
+ (
+ quote!( ref mut #field_name ),
+ args::gen_updater(&fields.named, &attrs, false),
+ )
+ })
+ .unzip();
+ (quote!( { #( #fields, )* }), 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(
+ __clap_arg,
+ __clap_sub_arg_matches
+ )?),
+ )
+ } else {
+ abort_call_site!("{}: tuple enums are not supported", variant.ident)
+ }
+ }
+ };
+
+ quote! {
+ #name :: #variant_name #pattern if #sub_name == __clap_name => {
+ let __clap_arg_matches = __clap_sub_arg_matches;
+ #updater
+ }
+ }
+ });
+
+ 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];
+ quote! {
+ if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
+ if let #name :: #variant_name (child) = s {
+ <#ty as clap::FromArgMatches>::update_from_arg_matches(child, __clap_arg_matches)?;
+ return ::std::result::Result::Ok(());
+ }
+ }
+ }
+ }
+ _ => abort!(
+ variant,
+ "`flatten` is usable only with single-typed tuple variants"
+ ),
+ }
+ });
+
+ quote! {
+ fn update_from_arg_matches<'b>(
+ &mut self,
+ __clap_arg_matches: &clap::ArgMatches,
+ ) -> ::std::result::Result<(), clap::Error> {
+ if let Some((__clap_name, __clap_sub_arg_matches)) = __clap_arg_matches.subcommand() {
+ match self {
+ #( #subcommands ),*
+ s => {
+ #( #child_subcommands )*
+ *s = <Self as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)?;
+ }
+ }
+ }
+ ::std::result::Result::Ok(())
+ }
+ }
+}
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..be1583062b
--- /dev/null
+++ b/third_party/rust/clap_derive/src/dummies.rs
@@ -0,0 +1,91 @@
+//! Dummy implementations that we emit along with an error.
+
+use proc_macro2::Ident;
+use proc_macro_error::append_dummy;
+use quote::quote;
+
+pub fn parser_struct(name: &Ident) {
+ into_app(name);
+ args(name);
+ append_dummy(quote!( impl clap::Parser for #name {} ));
+}
+
+pub fn parser_enum(name: &Ident) {
+ into_app(name);
+ subcommand(name);
+ append_dummy(quote!( impl clap::Parser for #name {} ));
+}
+
+pub fn into_app(name: &Ident) {
+ append_dummy(quote! {
+ #[allow(deprecated)]
+ impl clap::CommandFactory for #name {
+ fn into_app<'b>() -> clap::Command<'b> {
+ unimplemented!()
+ }
+ fn into_app_for_update<'b>() -> clap::Command<'b> {
+ unimplemented!()
+ }
+ }
+ });
+}
+
+pub fn from_arg_matches(name: &Ident) {
+ append_dummy(quote! {
+ 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!()
+ }
+ }
+ });
+}
+
+pub fn subcommand(name: &Ident) {
+ from_arg_matches(name);
+ append_dummy(quote! {
+ 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!()
+ }
+ }
+ });
+}
+
+pub fn args(name: &Ident) {
+ from_arg_matches(name);
+ append_dummy(quote! {
+ 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!()
+ }
+ }
+ });
+}
+
+pub fn arg_enum(name: &Ident) {
+ append_dummy(quote! {
+ impl clap::ArgEnum 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::PossibleValue<'a>>{
+ unimplemented!()
+ }
+ }
+ })
+}
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..973b61e177
--- /dev/null
+++ b/third_party/rust/clap_derive/src/lib.rs
@@ -0,0 +1,66 @@
+// 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 proc_macro_error::proc_macro_error;
+use syn::{parse_macro_input, DeriveInput};
+
+mod attrs;
+mod derives;
+mod dummies;
+mod parse;
+mod utils;
+
+/// Generates the `ArgEnum` impl.
+#[proc_macro_derive(ArgEnum, attributes(clap))]
+#[proc_macro_error]
+pub fn arg_enum(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_arg_enum(&input).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))]
+#[proc_macro_error]
+pub fn parser(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_parser(&input).into()
+}
+
+/// Generates the `Subcommand` impl.
+#[proc_macro_derive(Subcommand, attributes(clap))]
+#[proc_macro_error]
+pub fn subcommand(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_subcommand(&input).into()
+}
+
+/// Generates the `Args` impl.
+#[proc_macro_derive(Args, attributes(clap))]
+#[proc_macro_error]
+pub fn args(input: TokenStream) -> TokenStream {
+ let input: DeriveInput = parse_macro_input!(input);
+ derives::derive_args(&input).into()
+}
diff --git a/third_party/rust/clap_derive/src/parse.rs b/third_party/rust/clap_derive/src/parse.rs
new file mode 100644
index 0000000000..2973cf670f
--- /dev/null
+++ b/third_party/rust/clap_derive/src/parse.rs
@@ -0,0 +1,279 @@
+use std::iter::FromIterator;
+
+use proc_macro_error::{abort, ResultExt};
+use quote::ToTokens;
+use syn::{
+ self, parenthesized,
+ parse::{Parse, ParseBuffer, ParseStream},
+ punctuated::Punctuated,
+ Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token,
+};
+
+pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> {
+ all_attrs
+ .iter()
+ .filter(|attr| attr.path.is_ident("clap") || attr.path.is_ident("structopt"))
+ .flat_map(|attr| {
+ attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)
+ .unwrap_or_abort()
+ })
+ .collect()
+}
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Clone)]
+pub enum ClapAttr {
+ // single-identifier attributes
+ Short(Ident),
+ Long(Ident),
+ Env(Ident),
+ Flatten(Ident),
+ ArgEnum(Ident),
+ FromGlobal(Ident),
+ Subcommand(Ident),
+ VerbatimDocComment(Ident),
+ ExternalSubcommand(Ident),
+ About(Ident),
+ Author(Ident),
+ Version(Ident),
+
+ // ident = "string literal"
+ RenameAllEnv(Ident, LitStr),
+ RenameAll(Ident, LitStr),
+ NameLitStr(Ident, LitStr),
+
+ // parse(parser_kind [= parser_func])
+ Parse(Ident, ParserSpec),
+
+ // ident [= arbitrary_expr]
+ Skip(Ident, Option<Expr>),
+
+ // ident = arbitrary_expr
+ NameExpr(Ident, Expr),
+ DefaultValueT(Ident, Option<Expr>),
+ DefaultValueOsT(Ident, Option<Expr>),
+ NextDisplayOrder(Ident, Expr),
+ NextHelpHeading(Ident, Expr),
+ HelpHeading(Ident, Expr),
+
+ // ident(arbitrary_expr,*)
+ MethodCall(Ident, Vec<Expr>),
+}
+
+impl Parse for ClapAttr {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ use self::ClapAttr::*;
+
+ let name: Ident = input.parse()?;
+ let name_str = name.to_string();
+
+ if input.peek(Token![=]) {
+ // `name = value` attributes.
+ let assign_token = input.parse::<Token![=]>()?; // skip '='
+
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+
+ match &*name_str {
+ "rename_all" => Ok(RenameAll(name, lit)),
+ "rename_all_env" => Ok(RenameAllEnv(name, lit)),
+
+ "skip" => {
+ let expr = ExprLit {
+ attrs: vec![],
+ lit: Lit::Str(lit),
+ };
+ let expr = Expr::Lit(expr);
+ Ok(Skip(name, Some(expr)))
+ }
+
+ "next_display_order" => {
+ let expr = ExprLit {
+ attrs: vec![],
+ lit: Lit::Str(lit),
+ };
+ let expr = Expr::Lit(expr);
+ Ok(NextDisplayOrder(name, expr))
+ }
+
+ "next_help_heading" => {
+ let expr = ExprLit {
+ attrs: vec![],
+ lit: Lit::Str(lit),
+ };
+ let expr = Expr::Lit(expr);
+ Ok(NextHelpHeading(name, expr))
+ }
+ "help_heading" => {
+ let expr = ExprLit {
+ attrs: vec![],
+ lit: Lit::Str(lit),
+ };
+ let expr = Expr::Lit(expr);
+ Ok(HelpHeading(name, expr))
+ }
+
+ _ => Ok(NameLitStr(name, lit)),
+ }
+ } else {
+ match input.parse::<Expr>() {
+ Ok(expr) => match &*name_str {
+ "skip" => Ok(Skip(name, Some(expr))),
+ "default_value_t" => Ok(DefaultValueT(name, Some(expr))),
+ "default_value_os_t" => Ok(DefaultValueOsT(name, Some(expr))),
+ "next_display_order" => Ok(NextDisplayOrder(name, expr)),
+ "next_help_heading" => Ok(NextHelpHeading(name, expr)),
+ "help_heading" => Ok(HelpHeading(name, expr)),
+ _ => Ok(NameExpr(name, 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);
+
+ match name_str.as_ref() {
+ "parse" => {
+ let parser_specs: Punctuated<ParserSpec, Token![,]> =
+ nested.parse_terminated(ParserSpec::parse)?;
+
+ if parser_specs.len() == 1 {
+ Ok(Parse(name, parser_specs[0].clone()))
+ } else {
+ abort!(name, "parse must have exactly one argument")
+ }
+ }
+
+ "raw" => match nested.parse::<LitBool>() {
+ Ok(bool_token) => {
+ let expr = ExprLit {
+ attrs: vec![],
+ lit: Lit::Bool(bool_token),
+ };
+ let expr = Expr::Lit(expr);
+ Ok(MethodCall(name, vec![expr]))
+ }
+
+ Err(_) => {
+ abort!(name,
+ "`#[clap(raw(...))` attributes are removed, \
+ they are replaced with raw methods";
+ help = "if you meant to call `clap::Arg::raw()` method \
+ you should use bool literal, like `raw(true)` or `raw(false)`";
+ note = raw_method_suggestion(nested);
+ );
+ }
+ },
+
+ _ => {
+ let method_args: Punctuated<_, Token![,]> =
+ nested.parse_terminated(Expr::parse)?;
+ Ok(MethodCall(name, Vec::from_iter(method_args)))
+ }
+ }
+ } else {
+ // Attributes represented with a sole identifier.
+ match name_str.as_ref() {
+ "long" => Ok(Long(name)),
+ "short" => Ok(Short(name)),
+ "env" => Ok(Env(name)),
+ "flatten" => Ok(Flatten(name)),
+ "arg_enum" => Ok(ArgEnum(name)),
+ "from_global" => Ok(FromGlobal(name)),
+ "subcommand" => Ok(Subcommand(name)),
+ "external_subcommand" => Ok(ExternalSubcommand(name)),
+ "verbatim_doc_comment" => Ok(VerbatimDocComment(name)),
+
+ "default_value" => {
+ abort!(name,
+ "`#[clap(default_value)` attribute (without a value) has been replaced by `#[clap(default_value_t)]`.";
+ help = "Change the attribute to `#[clap(default_value_t)]`";
+ )
+ }
+ "default_value_t" => Ok(DefaultValueT(name, None)),
+ "default_value_os_t" => Ok(DefaultValueOsT(name, None)),
+ "about" => (Ok(About(name))),
+ "author" => (Ok(Author(name))),
+ "version" => Ok(Version(name)),
+
+ "skip" => Ok(Skip(name, None)),
+
+ _ => abort!(name, "unexpected attribute: {}", name_str),
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct ParserSpec {
+ pub kind: Ident,
+ pub eq_token: Option<Token![=]>,
+ pub parse_func: Option<Expr>,
+}
+
+impl Parse for ParserSpec {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let kind = input
+ .parse()
+ .map_err(|_| input.error("parser specification must start with identifier"))?;
+ let eq_token = input.parse()?;
+ let parse_func = match eq_token {
+ None => None,
+ Some(_) => Some(input.parse()?),
+ };
+ Ok(ParserSpec {
+ kind,
+ eq_token,
+ parse_func,
+ })
+ }
+}
+
+fn raw_method_suggestion(ts: ParseBuffer) -> String {
+ let do_parse = move || -> Result<(Ident, Punctuated<Expr, Token![,]>), syn::Error> {
+ let name = ts.parse()?;
+ let _eq: Token![=] = ts.parse()?;
+ let val: LitStr = ts.parse()?;
+ let exprs = val.parse_with(Punctuated::<Expr, Token![,]>::parse_terminated)?;
+ Ok((name, exprs))
+ };
+
+ fn to_string<T: ToTokens>(val: &T) -> String {
+ val.to_token_stream()
+ .to_string()
+ .replace(' ', "")
+ .replace(',', ", ")
+ }
+
+ if let Ok((name, exprs)) = do_parse() {
+ let suggestion = if exprs.len() == 1 {
+ let val = to_string(&exprs[0]);
+ format!(" = {}", val)
+ } else {
+ let val = exprs
+ .into_iter()
+ .map(|expr| to_string(&expr))
+ .collect::<Vec<_>>()
+ .join(", ");
+
+ format!("({})", val)
+ };
+
+ format!(
+ "if you need to call `clap::Arg/Command::{}` method you \
+ can do it like this: #[clap({}{})]",
+ name, name, suggestion
+ )
+ } else {
+ "if you need to call some method from `clap::Arg/Command` \
+ you should use raw method, see \
+ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#raw-attributes"
+ .into()
+ }
+}
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..f0a5034d70
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/doc_comments.rs
@@ -0,0 +1,107 @@
+//! 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 crate::attrs::Method;
+
+use quote::{format_ident, quote};
+use std::iter;
+
+pub fn process_doc_comment(lines: Vec<String>, name: &str, preprocess: bool) -> Vec<Method> {
+ // 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<&str> = lines
+ .iter()
+ .skip_while(|s| is_blank(s))
+ .flat_map(|s| s.split('\n'))
+ .collect();
+
+ while let Some(true) = lines.last().map(|s| is_blank(s)) {
+ lines.pop();
+ }
+
+ // remove one leading space no matter what
+ for line in lines.iter_mut() {
+ if line.starts_with(' ') {
+ *line = &line[1..];
+ }
+ }
+
+ if lines.is_empty() {
+ return vec![];
+ }
+
+ let short_name = format_ident!("{}", name);
+ let long_name = format_ident!("long_{}", name);
+
+ 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)
+ };
+
+ vec![
+ Method::new(short_name, quote!(#short)),
+ Method::new(long_name, quote!(#long)),
+ ]
+ } else {
+ let short = if preprocess {
+ let s = merge_lines(&lines);
+ remove_period(s)
+ } else {
+ lines.join("\n")
+ };
+
+ vec![
+ Method::new(short_name, quote!(#short)),
+ Method::new(long_name, quote!(None)),
+ ]
+ }
+}
+
+fn split_paragraphs(lines: &[&str]) -> 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: &[&str]) -> String {
+ lines.iter().map(|s| s.trim()).collect::<Vec<_>>().join(" ")
+}
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..77a467c754
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/mod.rs
@@ -0,0 +1,9 @@
+mod doc_comments;
+mod spanned;
+mod ty;
+
+pub use self::{
+ doc_comments::process_doc_comment,
+ 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..11415f6f0e
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/spanned.rs
@@ -0,0 +1,92 @@
+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, 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 call_site(val: T) -> Self {
+ Sp {
+ val,
+ span: Span::call_site(),
+ }
+ }
+
+ 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..891aa37d68
--- /dev/null
+++ b/third_party/rust/clap_derive/src/utils/ty.rs
@@ -0,0 +1,121 @@
+//! Special types handling
+
+use super::spanned::Sp;
+
+use syn::{
+ spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
+ PathSegment, Type, TypePath,
+};
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Ty {
+ Bool,
+ Vec,
+ Option,
+ OptionOption,
+ OptionVec,
+ 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_simple_ty(ty, "bool") {
+ t(Bool)
+ } else if is_generic_ty(ty, "Vec") {
+ t(Vec)
+ } else if let Some(subty) = subty_if_name(ty, "Option") {
+ if is_generic_ty(subty, "Option") {
+ t(OptionOption)
+ } else if is_generic_ty(subty, "Vec") {
+ t(OptionVec)
+ } else {
+ t(Option)
+ }
+ } else {
+ t(Other)
+ }
+ }
+}
+
+pub fn inner_type(ty: Ty, field_ty: &syn::Type) -> &syn::Type {
+ match ty {
+ Ty::Vec | Ty::Option => sub_type(field_ty).unwrap_or(field_ty),
+ Ty::OptionOption | Ty::OptionVec => {
+ sub_type(field_ty).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 only_one<I, T>(mut iter: I) -> Option<T>
+where
+ I: Iterator<Item = T>,
+{
+ iter.next().filter(|_| iter.next().is_none())
+}