summaryrefslogtreecommitdiffstats
path: root/vendor/clap_derive/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/clap_derive/src/parse.rs')
-rw-r--r--vendor/clap_derive/src/parse.rs284
1 files changed, 284 insertions, 0 deletions
diff --git a/vendor/clap_derive/src/parse.rs b/vendor/clap_derive/src/parse.rs
new file mode 100644
index 000000000..253736cfd
--- /dev/null
+++ b/vendor/clap_derive/src/parse.rs
@@ -0,0 +1,284 @@
+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),
+ ValueParser(Ident),
+ Action(Ident),
+ Env(Ident),
+ Flatten(Ident),
+ ValueEnum(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)),
+ "value_parser" => Ok(ValueParser(name)),
+ "action" => Ok(Action(name)),
+ "env" => Ok(Env(name)),
+ "flatten" => Ok(Flatten(name)),
+ "arg_enum" => Ok(ValueEnum(name)),
+ "value_enum" => Ok(ValueEnum(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()
+ }
+}