aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2023-02-01 15:16:32 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-02-01 15:16:32 +0000
commitbc2a581ba65c679078c97b6f7ba93c90099384de (patch)
tree97df10f43d1dddccfaff512372427b7fa2a41bd2
parent775d7dae3d84ead3a1f9a3fd9c250bc0d343088c (diff)
parent0b51dba309b5f9130df8cb98b5e70b067cffb14a (diff)
downloadargh_derive-bc2a581ba65c679078c97b6f7ba93c90099384de.tar.gz
Upgrade argh_derive to 0.1.10 am: 5428a147ee am: 0b51dba309
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/argh_derive/+/2411608 Change-Id: Iee9939fecc51601232e914cb77d2a9157b51d13a Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp3
-rw-r--r--Cargo.toml7
-rw-r--r--Cargo.toml.orig5
-rw-r--r--METADATA14
-rw-r--r--src/help.rs17
-rw-r--r--src/lib.rs71
-rw-r--r--src/parse_attrs.rs21
8 files changed, 94 insertions, 46 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 1b6d0b1..9595563 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "f1f85d2d89cbe09314dc1b59e581b8a43531cf3e"
+ "sha1": "3f3c29726a21c4b541bb2b9aa2c592461897ded0"
},
"path_in_vcs": "argh_derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 9648d44..5607288 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,12 +22,11 @@ rust_proc_macro {
name: "libargh_derive",
crate_name: "argh_derive",
cargo_env_compat: true,
- cargo_pkg_version: "0.1.9",
+ cargo_pkg_version: "0.1.10",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
"libargh_shared",
- "libheck",
"libproc_macro2",
"libquote",
"libsyn",
diff --git a/Cargo.toml b/Cargo.toml
index bf0ee93..b7ddfba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "argh_derive"
-version = "0.1.9"
+version = "0.1.10"
authors = [
"Taylor Cramer <cramertj@google.com>",
"Benjamin Brittain <bwb@google.com>",
@@ -27,10 +27,7 @@ repository = "https://github.com/google/argh"
proc-macro = true
[dependencies.argh_shared]
-version = "0.1.9"
-
-[dependencies.heck]
-version = "0.4"
+version = "0.1.10"
[dependencies.proc-macro2]
version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f6614e9..750b494 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "argh_derive"
-version = "0.1.9"
+version = "0.1.10"
authors = ["Taylor Cramer <cramertj@google.com>", "Benjamin Brittain <bwb@google.com>", "Erick Tryzelaar <etryzelaar@google.com>"]
edition = "2018"
license = "BSD-3-Clause"
@@ -12,8 +12,7 @@ readme = "README.md"
proc-macro = true
[dependencies]
-heck = "0.4"
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0"
-argh_shared = { version = "0.1.9", path = "../argh_shared" }
+argh_shared = { version = "0.1.10", path = "../argh_shared" }
diff --git a/METADATA b/METADATA
index c0071c8..c7f6717 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/argh_derive
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "argh_derive"
description: "Derive-based argument parsing optimized for code size"
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/argh_derive/argh_derive-0.1.9.crate"
+ value: "https://static.crates.io/crates/argh_derive/argh_derive-0.1.10.crate"
}
- version: "0.1.9"
+ version: "0.1.10"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 12
- day: 5
+ year: 2023
+ month: 2
+ day: 1
}
}
diff --git a/src/help.rs b/src/help.rs
index c9daad5..bd20622 100644
--- a/src/help.rs
+++ b/src/help.rs
@@ -27,11 +27,11 @@ pub(crate) fn help(
fields: &[StructField<'_>],
subcommand: Option<&StructField<'_>>,
) -> TokenStream {
- #![allow(clippy::format_push_string)]
let mut format_lit = "Usage: {command_name}".to_string();
- let positional =
- fields.iter().filter(|f| f.kind == FieldKind::Positional && f.attrs.greedy.is_none());
+ let positional = fields.iter().filter(|f| {
+ f.kind == FieldKind::Positional && f.attrs.greedy.is_none() && !f.attrs.hidden_help
+ });
let mut has_positional = false;
for arg in positional.clone() {
has_positional = true;
@@ -39,14 +39,15 @@ pub(crate) fn help(
positional_usage(&mut format_lit, arg);
}
- let options = fields.iter().filter(|f| f.long_name.is_some());
+ let options = fields.iter().filter(|f| f.long_name.is_some() && !f.attrs.hidden_help);
for option in options.clone() {
format_lit.push(' ');
option_usage(&mut format_lit, option);
}
- let remain =
- fields.iter().filter(|f| f.kind == FieldKind::Positional && f.attrs.greedy.is_some());
+ let remain = fields.iter().filter(|f| {
+ f.kind == FieldKind::Positional && f.attrs.greedy.is_some() && !f.attrs.hidden_help
+ });
for arg in remain {
format_lit.push(' ');
positional_usage(&mut format_lit, arg);
@@ -154,7 +155,7 @@ fn positional_usage(out: &mut String, field: &StructField<'_>) {
if field.attrs.greedy.is_none() {
out.push('<');
}
- let name = field.arg_name();
+ let name = field.positional_arg_name();
out.push_str(&name);
if field.optionality == Optionality::Repeating {
out.push_str("...");
@@ -229,7 +230,7 @@ Add a doc comment or an `#[argh(description = \"...\")]` attribute.",
/// Describes a positional argument like this:
/// hello positional argument description
fn positional_description(out: &mut String, field: &StructField<'_>) {
- let field_name = field.arg_name();
+ let field_name = field.positional_arg_name();
let mut description = String::from("");
if let Some(desc) = &field.attrs.description {
diff --git a/src/lib.rs b/src/lib.rs
index 49a6c17..94cc437 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,13 +11,12 @@ extern crate proc_macro;
use {
crate::{
errors::Errors,
- parse_attrs::{FieldAttrs, FieldKind, TypeAttrs},
+ parse_attrs::{check_long_name, FieldAttrs, FieldKind, TypeAttrs},
},
- heck::ToKebabCase,
proc_macro2::{Span, TokenStream},
quote::{quote, quote_spanned, ToTokens},
std::{collections::HashMap, str::FromStr},
- syn::{spanned::Spanned, LitStr},
+ syn::{spanned::Spanned, GenericArgument, LitStr, PathArguments, Type},
};
mod errors;
@@ -179,11 +178,11 @@ impl<'a> StructField<'a> {
// Defaults to the kebab-case'd field name if `#[argh(long = "...")]` is omitted.
let long_name = match kind {
FieldKind::Switch | FieldKind::Option => {
- let long_name = attrs
- .long
- .as_ref()
- .map(syn::LitStr::value)
- .unwrap_or_else(|| name.to_string().to_kebab_case());
+ let long_name = attrs.long.as_ref().map(syn::LitStr::value).unwrap_or_else(|| {
+ let kebab_name = to_kebab_case(&name.to_string());
+ check_long_name(errors, name, &kebab_name);
+ kebab_name
+ });
if long_name == "help" {
errors.err(field, "Custom `--help` flags are not supported.");
}
@@ -196,11 +195,43 @@ impl<'a> StructField<'a> {
Some(StructField { field, attrs, kind, optionality, ty_without_wrapper, name, long_name })
}
- pub(crate) fn arg_name(&self) -> String {
- self.attrs.arg_name.as_ref().map(LitStr::value).unwrap_or_else(|| self.name.to_string())
+ pub(crate) fn positional_arg_name(&self) -> String {
+ self.attrs
+ .arg_name
+ .as_ref()
+ .map(LitStr::value)
+ .unwrap_or_else(|| self.name.to_string().trim_matches('_').to_owned())
}
}
+fn to_kebab_case(s: &str) -> String {
+ let words = s.split('_').filter(|word| !word.is_empty());
+ let mut res = String::with_capacity(s.len());
+ for word in words {
+ if !res.is_empty() {
+ res.push('-')
+ }
+ res.push_str(word)
+ }
+ res
+}
+
+#[test]
+fn test_kebabs() {
+ #[track_caller]
+ fn check(s: &str, want: &str) {
+ let got = to_kebab_case(s);
+ assert_eq!(got.as_str(), want)
+ }
+ check("", "");
+ check("_", "");
+ check("foo", "foo");
+ check("__foo_", "foo");
+ check("foo_bar", "foo-bar");
+ check("foo__Bar", "foo-Bar");
+ check("foo_bar__baz_", "foo-bar-baz");
+}
+
/// Implements `FromArgs` and `TopLevelCommand` or `SubCommand` for a `#[derive(FromArgs)]` struct.
fn impl_from_args_struct(
errors: &Errors,
@@ -530,7 +561,7 @@ fn ensure_unique_names(errors: &Errors, fields: &[StructField<'_>]) {
first_use_field,
&format!("The short name of \"-{}\" was already used here.", short_name),
);
- errors.err_span_tokens(&field.field, "Later usage here.");
+ errors.err_span_tokens(field.field, "Later usage here.");
}
seen_short_names.insert(short_name, &field.field);
@@ -542,7 +573,7 @@ fn ensure_unique_names(errors: &Errors, fields: &[StructField<'_>]) {
*first_use_field,
&format!("The long name of \"{}\" was already used here.", long_name),
);
- errors.err_span_tokens(&field.field, "Later usage here.");
+ errors.err_span_tokens(field.field, "Later usage here.");
}
seen_long_names.insert(long_name, field.field);
@@ -710,7 +741,7 @@ fn declare_local_storage_for_redacted_fields<'a>(
}
};
- let arg_name = field.arg_name();
+ let arg_name = field.positional_arg_name();
quote! {
let mut #field_name: argh::ParseValueSlotTy::<#field_slot_type, String> =
argh::ParseValueSlotTy {
@@ -802,7 +833,7 @@ fn append_missing_requirements<'a>(
match field.kind {
FieldKind::Switch => unreachable!("switches are always optional"),
FieldKind::Positional => {
- let name = field.arg_name();
+ let name = field.positional_arg_name();
quote! {
if #field_name.slot.is_none() {
#mri.missing_positional_arg(#name)
@@ -850,6 +881,16 @@ fn ty_expect_switch(errors: &Errors, ty: &syn::Type) -> bool {
return false;
}
let ident = &path.path.segments[0].ident;
+ // `Option<bool>` can be used as a `switch`.
+ if ident == "Option" {
+ if let PathArguments::AngleBracketed(args) = &path.path.segments[0].arguments {
+ if let GenericArgument::Type(Type::Path(p)) = &args.args[0] {
+ if p.path.segments[0].ident == "bool" {
+ return true;
+ }
+ }
+ }
+ }
["bool", "u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128"]
.iter()
.any(|path| ident == path)
@@ -860,7 +901,7 @@ fn ty_expect_switch(errors: &Errors, ty: &syn::Type) -> bool {
let res = ty_can_be_switch(ty);
if !res {
- errors.err(ty, "switches must be of type `bool` or integer type");
+ errors.err(ty, "switches must be of type `bool`, `Option<bool>`, or integer type");
}
res
}
diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs
index f5fa841..04dcbdd 100644
--- a/src/parse_attrs.rs
+++ b/src/parse_attrs.rs
@@ -19,6 +19,7 @@ pub struct FieldAttrs {
pub short: Option<syn::LitChar>,
pub arg_name: Option<syn::LitStr>,
pub greedy: Option<syn::Path>,
+ pub hidden_help: bool,
}
/// The purpose of a particular field on a `#![derive(FromArgs)]` struct.
@@ -123,13 +124,15 @@ impl FieldAttrs {
);
} else if name.is_ident("greedy") {
this.greedy = Some(name.clone());
+ } else if name.is_ident("hidden_help") {
+ this.hidden_help = true;
} else {
errors.err(
&meta,
concat!(
"Invalid field-level `argh` attribute\n",
"Expected one of: `arg_name`, `default`, `description`, `from_str_fn`, `greedy`, ",
- "`long`, `option`, `short`, `subcommand`, `switch`",
+ "`long`, `option`, `short`, `subcommand`, `switch`, `hidden_help`",
),
);
}
@@ -180,12 +183,7 @@ impl FieldAttrs {
parse_attr_single_string(errors, m, "long", &mut self.long);
let long = self.long.as_ref().unwrap();
let value = long.value();
- if !value.is_ascii() {
- errors.err(long, "Long names must be ASCII");
- }
- if !value.chars().all(|c| c.is_lowercase() || c == '-' || c.is_ascii_digit()) {
- errors.err(long, "Long names must be lowercase");
- }
+ check_long_name(errors, long, &value);
}
fn parse_attr_short(&mut self, errors: &Errors, m: &syn::MetaNameValue) {
@@ -200,6 +198,15 @@ impl FieldAttrs {
}
}
+pub(crate) fn check_long_name(errors: &Errors, spanned: &impl syn::spanned::Spanned, value: &str) {
+ if !value.is_ascii() {
+ errors.err(spanned, "Long names must be ASCII");
+ }
+ if !value.chars().all(|c| c.is_lowercase() || c == '-' || c.is_ascii_digit()) {
+ errors.err(spanned, "Long names must be lowercase");
+ }
+}
+
fn parse_attr_fn_name(
errors: &Errors,
m: &syn::MetaList,