aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2023-10-18 19:16:57 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-10-18 19:16:57 +0000
commit927c23903aba335fcc613af9448dde1e1d4c1d7f (patch)
treeb19ddf8460efebc25c13a06a118420c6667dd5ad
parent12a1fe723bc6ade529a682a65e8ee9069f7e3ef2 (diff)
parentc045fa9ccc932ca15f18eb51a1fa6b3dba4a00bf (diff)
downloadargh_derive-927c23903aba335fcc613af9448dde1e1d4c1d7f.tar.gz
Upgrade argh_derive to 0.1.12 am: ab2e2d02ca am: c045fa9ccc
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/argh_derive/+/2796277 Change-Id: I01b5cba4a4ac67446344c2c41db2b21bc0e31eb2 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.bp2
-rw-r--r--Cargo.toml6
-rw-r--r--Cargo.toml.orig6
-rw-r--r--METADATA10
-rw-r--r--patches/syn-2.patch383
-rw-r--r--src/args_info.rs356
-rw-r--r--src/lib.rs9
-rw-r--r--src/parse_attrs.rs4
9 files changed, 380 insertions, 398 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 9595563..e06a0db 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "3f3c29726a21c4b541bb2b9aa2c592461897ded0"
+ "sha1": "e8efc8285f632a4ebedfe4377e0f1d78276f8e19"
},
"path_in_vcs": "argh_derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index a31ff09..bad6d6f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,7 +22,7 @@ rust_proc_macro {
name: "libargh_derive",
crate_name: "argh_derive",
cargo_env_compat: true,
- cargo_pkg_version: "0.1.10",
+ cargo_pkg_version: "0.1.12",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
diff --git a/Cargo.toml b/Cargo.toml
index fcd6837..0d7d67a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "argh_derive"
-version = "0.1.10"
+version = "0.1.12"
authors = [
"Taylor Cramer <cramertj@google.com>",
"Benjamin Brittain <bwb@google.com>",
@@ -27,7 +27,7 @@ repository = "https://github.com/google/argh"
proc-macro = true
[dependencies.argh_shared]
-version = "0.1.10"
+version = "0.1.12"
[dependencies.proc-macro2]
version = "1.0"
@@ -36,4 +36,4 @@ version = "1.0"
version = "1.0"
[dependencies.syn]
-version = "2.0.1"
+version = "2.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 750b494..dcc965f 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "argh_derive"
-version = "0.1.10"
+version = "0.1.12"
authors = ["Taylor Cramer <cramertj@google.com>", "Benjamin Brittain <bwb@google.com>", "Erick Tryzelaar <etryzelaar@google.com>"]
edition = "2018"
license = "BSD-3-Clause"
@@ -14,5 +14,5 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
-syn = "1.0"
-argh_shared = { version = "0.1.10", path = "../argh_shared" }
+syn = "2.0"
+argh_shared = { version = "0.1.12", path = "../argh_shared" }
diff --git a/METADATA b/METADATA
index c7f6717..7031327 100644
--- a/METADATA
+++ b/METADATA
@@ -1,6 +1,6 @@
# 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
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "argh_derive"
description: "Derive-based argument parsing optimized for code size"
@@ -11,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/argh_derive/argh_derive-0.1.10.crate"
+ value: "https://static.crates.io/crates/argh_derive/argh_derive-0.1.12.crate"
}
- version: "0.1.10"
+ version: "0.1.12"
license_type: NOTICE
last_upgrade_date {
year: 2023
- month: 2
- day: 1
+ month: 10
+ day: 11
}
}
diff --git a/patches/syn-2.patch b/patches/syn-2.patch
deleted file mode 100644
index ffe0488..0000000
--- a/patches/syn-2.patch
+++ /dev/null
@@ -1,383 +0,0 @@
-diff --git a/src/errors.rs b/src/errors.rs
-index cfad383..fc5b584 100644
---- a/src/errors.rs
-+++ b/src/errors.rs
-@@ -14,15 +14,15 @@ pub struct Errors {
- errors: RefCell<Vec<syn::Error>>,
- }
-
--/// Produce functions to expect particular variants of `syn::Lit`
-+/// Produce functions to expect particular literals in `syn::Expr`
- macro_rules! expect_lit_fn {
- ($(($fn_name:ident, $syn_type:ident, $variant:ident, $lit_name:literal),)*) => {
- $(
-- pub fn $fn_name<'a>(&self, lit: &'a syn::Lit) -> Option<&'a syn::$syn_type> {
-- if let syn::Lit::$variant(inner) = lit {
-+ pub fn $fn_name<'a>(&self, e: &'a syn::Expr) -> Option<&'a syn::$syn_type> {
-+ if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::$variant(inner), .. }) = e {
- Some(inner)
- } else {
-- self.unexpected_lit($lit_name, lit);
-+ self.unexpected_lit($lit_name, e);
- None
- }
- }
-@@ -65,28 +65,6 @@ impl Errors {
- self.err_span(first, &["First ", attr_kind, " attribute here"].concat());
- }
-
-- /// Error on literals, expecting attribute syntax.
-- pub fn expect_nested_meta<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Meta> {
-- match nm {
-- syn::NestedMeta::Lit(l) => {
-- self.err(l, "Unexpected literal");
-- None
-- }
-- syn::NestedMeta::Meta(m) => Some(m),
-- }
-- }
--
-- /// Error on attribute syntax, expecting literals
-- pub fn expect_nested_lit<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Lit> {
-- match nm {
-- syn::NestedMeta::Meta(m) => {
-- self.err(m, "Expected literal");
-- None
-- }
-- syn::NestedMeta::Lit(l) => Some(l),
-- }
-- }
--
- expect_lit_fn![
- (expect_lit_str, LitStr, Str, "string"),
- (expect_lit_char, LitChar, Char, "character"),
-@@ -99,7 +77,7 @@ impl Errors {
- (expect_meta_name_value, MetaNameValue, NameValue, "name-value pair"),
- ];
-
-- fn unexpected_lit(&self, expected: &str, found: &syn::Lit) {
-+ fn unexpected_lit(&self, expected: &str, found: &syn::Expr) {
- fn lit_kind(lit: &syn::Lit) -> &'static str {
- use syn::Lit::{Bool, Byte, ByteStr, Char, Float, Int, Str, Verbatim};
- match lit {
-@@ -111,13 +89,21 @@ impl Errors {
- Float(_) => "float",
- Bool(_) => "boolean",
- Verbatim(_) => "unknown (possibly extra-large integer)",
-+ _ => "unknown literal kind",
- }
- }
-
-- self.err(
-- found,
-- &["Expected ", expected, " literal, found ", lit_kind(found), " literal"].concat(),
-- )
-+ if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = found {
-+ self.err(
-+ found,
-+ &["Expected ", expected, " literal, found ", lit_kind(lit), " literal"].concat(),
-+ )
-+ } else {
-+ self.err(
-+ found,
-+ &["Expected ", expected, " literal, found non-literal expression."].concat(),
-+ )
-+ }
- }
-
- fn unexpected_meta(&self, expected: &str, found: &syn::Meta) {
-@@ -155,6 +141,17 @@ impl Errors {
- pub fn push(&self, err: syn::Error) {
- self.errors.borrow_mut().push(err);
- }
-+
-+ /// Convert a `syn::Result` to an `Option`, logging the error if present.
-+ pub fn ok<T>(&self, r: syn::Result<T>) -> Option<T> {
-+ match r {
-+ Ok(v) => Some(v),
-+ Err(e) => {
-+ self.push(e);
-+ None
-+ }
-+ }
-+ }
- }
-
- impl ToTokens for Errors {
-diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs
-index 04dcbdd..10ebcb5 100644
---- a/src/parse_attrs.rs
-+++ b/src/parse_attrs.rs
-@@ -76,49 +76,47 @@ impl FieldAttrs {
- continue;
- };
-
-- for meta in &ml.nested {
-- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue };
--
-+ for meta in ml {
- let name = meta.path();
- if name.is_ident("arg_name") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_arg_name(errors, m);
- }
- } else if name.is_ident("default") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_default(errors, m);
- }
- } else if name.is_ident("description") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- parse_attr_description(errors, m, &mut this.description);
- }
- } else if name.is_ident("from_str_fn") {
-- if let Some(m) = errors.expect_meta_list(meta) {
-+ if let Some(m) = errors.expect_meta_list(&meta) {
- this.parse_attr_from_str_fn(errors, m);
- }
- } else if name.is_ident("long") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_long(errors, m);
- }
- } else if name.is_ident("option") {
-- parse_attr_field_type(errors, meta, FieldKind::Option, &mut this.field_type);
-+ parse_attr_field_type(errors, &meta, FieldKind::Option, &mut this.field_type);
- } else if name.is_ident("short") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_short(errors, m);
- }
- } else if name.is_ident("subcommand") {
- parse_attr_field_type(
- errors,
-- meta,
-+ &meta,
- FieldKind::SubCommand,
- &mut this.field_type,
- );
- } else if name.is_ident("switch") {
-- parse_attr_field_type(errors, meta, FieldKind::Switch, &mut this.field_type);
-+ parse_attr_field_type(errors, &meta, FieldKind::Switch, &mut this.field_type);
- } else if name.is_ident("positional") {
- parse_attr_field_type(
- errors,
-- meta,
-+ &meta,
- FieldKind::Positional,
- &mut this.field_type,
- );
-@@ -189,7 +187,7 @@ impl FieldAttrs {
- fn parse_attr_short(&mut self, errors: &Errors, m: &syn::MetaNameValue) {
- if let Some(first) = &self.short {
- errors.duplicate_attrs("short", first, m);
-- } else if let Some(lit_char) = errors.expect_lit_char(&m.lit) {
-+ } else if let Some(lit_char) = errors.expect_lit_char(&m.value) {
- self.short = Some(lit_char.clone());
- if !lit_char.value().is_ascii() {
- errors.err(lit_char, "Short names must be ASCII");
-@@ -217,16 +215,7 @@ fn parse_attr_fn_name(
- errors.duplicate_attrs(attr_name, first, m);
- }
-
-- if m.nested.len() != 1 {
-- errors.err(&m.nested, "Expected a single argument containing the function name");
-- return;
-- }
--
-- // `unwrap` will not fail because of the call above
-- let nested = m.nested.first().unwrap();
-- if let Some(path) = errors.expect_nested_meta(nested).and_then(|m| errors.expect_meta_word(m)) {
-- *slot = path.get_ident().cloned();
-- }
-+ *slot = errors.ok(m.parse_args());
- }
-
- fn parse_attr_field_type(
-@@ -246,7 +235,7 @@ fn parse_attr_field_type(
-
- // Whether the attribute is one like `#[<name> ...]`
- fn is_matching_attr(name: &str, attr: &syn::Attribute) -> bool {
-- attr.path.segments.len() == 1 && attr.path.segments[0].ident == name
-+ attr.path().segments.len() == 1 && attr.path().segments[0].ident == name
- }
-
- /// Checks for `#[doc ...]`, which is generated by doc comments.
-@@ -259,34 +248,18 @@ fn is_argh_attr(attr: &syn::Attribute) -> bool {
- is_matching_attr("argh", attr)
- }
-
--fn attr_to_meta_subtype<R: Clone>(
-+/// Filters out non-`#[argh(...)]` attributes and converts to a sequence of `syn::Meta`.
-+fn argh_attr_to_meta_list(
- errors: &Errors,
- attr: &syn::Attribute,
-- f: impl FnOnce(&syn::Meta) -> Option<&R>,
--) -> Option<R> {
-- match attr.parse_meta() {
-- Ok(meta) => f(&meta).cloned(),
-- Err(e) => {
-- errors.push(e);
-- None
-- }
-- }
--}
--
--fn attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> {
-- attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_list(m))
--}
--
--fn attr_to_meta_name_value(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaNameValue> {
-- attr_to_meta_subtype(errors, attr, |m| errors.expect_meta_name_value(m))
--}
--
--/// Filters out non-`#[argh(...)]` attributes and converts to `syn::MetaList`.
--fn argh_attr_to_meta_list(errors: &Errors, attr: &syn::Attribute) -> Option<syn::MetaList> {
-+) -> Option<impl IntoIterator<Item = syn::Meta>> {
- if !is_argh_attr(attr) {
- return None;
- }
-- attr_to_meta_list(errors, attr)
-+ let ml = errors.expect_meta_list(&attr.meta)?;
-+ errors.ok(ml.parse_args_with(
-+ syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated,
-+ ))
- }
-
- /// Represents a `#[derive(FromArgs)]` type's top-level attributes.
-@@ -317,32 +290,31 @@ impl TypeAttrs {
- continue;
- };
-
-- for meta in &ml.nested {
-- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue };
--
-+ for meta in ml {
- let name = meta.path();
- if name.is_ident("description") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- parse_attr_description(errors, m, &mut this.description);
- }
- } else if name.is_ident("error_code") {
-- if let Some(m) = errors.expect_meta_list(meta) {
-+ if let Some(m) = errors.expect_meta_list(&meta) {
- this.parse_attr_error_code(errors, m);
- }
- } else if name.is_ident("example") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_example(errors, m);
- }
- } else if name.is_ident("name") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_name(errors, m);
- }
- } else if name.is_ident("note") {
-- if let Some(m) = errors.expect_meta_name_value(meta) {
-+ if let Some(m) = errors.expect_meta_name_value(&meta) {
- this.parse_attr_note(errors, m);
- }
- } else if name.is_ident("subcommand") {
-- if let Some(ident) = errors.expect_meta_word(meta).and_then(|p| p.get_ident()) {
-+ if let Some(ident) = errors.expect_meta_word(&meta).and_then(|p| p.get_ident())
-+ {
- this.parse_attr_subcommand(errors, ident);
- }
- } else {
-@@ -396,20 +368,17 @@ impl TypeAttrs {
- }
-
- fn parse_attr_error_code(&mut self, errors: &Errors, ml: &syn::MetaList) {
-- if ml.nested.len() != 2 {
-- errors.err(&ml, "Expected two arguments, an error number and a string description");
-- return;
-- }
--
-- let err_code = &ml.nested[0];
-- let err_msg = &ml.nested[1];
--
-- let err_code = errors.expect_nested_lit(err_code).and_then(|l| errors.expect_lit_int(l));
-- let err_msg = errors.expect_nested_lit(err_msg).and_then(|l| errors.expect_lit_str(l));
--
-- if let (Some(err_code), Some(err_msg)) = (err_code, err_msg) {
-- self.error_codes.push((err_code.clone(), err_msg.clone()));
-- }
-+ errors.ok(ml.parse_args_with(|input: syn::parse::ParseStream| {
-+ let err_code = input.parse()?;
-+ input.parse::<syn::Token![,]>()?;
-+ let err_msg = input.parse()?;
-+ if let (Some(err_code), Some(err_msg)) =
-+ (errors.expect_lit_int(&err_code), errors.expect_lit_str(&err_msg))
-+ {
-+ self.error_codes.push((err_code.clone(), err_msg.clone()));
-+ }
-+ Ok(())
-+ }));
- }
-
- fn parse_attr_example(&mut self, errors: &Errors, m: &syn::MetaNameValue) {
-@@ -470,15 +439,13 @@ impl VariantAttrs {
- continue;
- };
-
-- for meta in &ml.nested {
-- let meta = if let Some(m) = errors.expect_nested_meta(meta) { m } else { continue };
--
-+ for meta in ml {
- let name = meta.path();
- if name.is_ident("dynamic") {
- if let Some(prev) = this.is_dynamic.as_ref() {
-- errors.duplicate_attrs("dynamic", prev, meta);
-+ errors.duplicate_attrs("dynamic", prev, &meta);
- } else {
-- this.is_dynamic = errors.expect_meta_word(meta).cloned();
-+ this.is_dynamic = errors.expect_meta_word(&meta).cloned();
- }
- } else {
- errors.err(
-@@ -515,19 +482,19 @@ fn parse_attr_single_string(
- ) {
- if let Some(first) = slot {
- errors.duplicate_attrs(name, first, m);
-- } else if let Some(lit_str) = errors.expect_lit_str(&m.lit) {
-+ } else if let Some(lit_str) = errors.expect_lit_str(&m.value) {
- *slot = Some(lit_str.clone());
- }
- }
-
- fn parse_attr_multi_string(errors: &Errors, m: &syn::MetaNameValue, list: &mut Vec<syn::LitStr>) {
-- if let Some(lit_str) = errors.expect_lit_str(&m.lit) {
-+ if let Some(lit_str) = errors.expect_lit_str(&m.value) {
- list.push(lit_str.clone());
- }
- }
-
- fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Description>) {
-- let nv = if let Some(nv) = attr_to_meta_name_value(errors, attr) {
-+ let nv = if let Some(nv) = errors.expect_meta_name_value(&attr.meta) {
- nv
- } else {
- return;
-@@ -538,7 +505,7 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc
- return;
- }
-
-- if let Some(lit_str) = errors.expect_lit_str(&nv.lit) {
-+ if let Some(lit_str) = errors.expect_lit_str(&nv.value) {
- let lit_str = if let Some(previous) = slot {
- let previous = &previous.content;
- let previous_span = previous.span();
-@@ -551,7 +518,8 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option<Desc
- }
-
- fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Option<Description>) {
-- let lit_str = if let Some(lit_str) = errors.expect_lit_str(&m.lit) { lit_str } else { return };
-+ let lit_str =
-+ if let Some(lit_str) = errors.expect_lit_str(&m.value) { lit_str } else { return };
-
- // Don't allow multiple explicit (non doc-comment) descriptions
- if let Some(description) = slot {
diff --git a/src/args_info.rs b/src/args_info.rs
new file mode 100644
index 0000000..ae640a7
--- /dev/null
+++ b/src/args_info.rs
@@ -0,0 +1,356 @@
+// Copyright (c) 2023 Google LLC All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+use crate::{
+ enum_only_single_field_unnamed_variants,
+ errors::Errors,
+ help::require_description,
+ parse_attrs::{check_enum_type_attrs, FieldAttrs, FieldKind, TypeAttrs, VariantAttrs},
+ Optionality, StructField,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens};
+use syn::LitStr;
+
+/// Implement the derive macro for ArgsInfo.
+pub(crate) fn impl_args_info(input: &syn::DeriveInput) -> TokenStream {
+ let errors = &Errors::default();
+
+ // parse the types
+ let type_attrs = &TypeAttrs::parse(errors, input);
+
+ // Based on the type generate the appropriate code.
+ let mut output_tokens = match &input.data {
+ syn::Data::Struct(ds) => {
+ impl_arg_info_struct(errors, &input.ident, type_attrs, &input.generics, ds)
+ }
+ syn::Data::Enum(de) => {
+ impl_arg_info_enum(errors, &input.ident, type_attrs, &input.generics, de)
+ }
+ syn::Data::Union(_) => {
+ errors.err(input, "`#[derive(ArgsInfo)]` cannot be applied to unions");
+ TokenStream::new()
+ }
+ };
+ errors.to_tokens(&mut output_tokens);
+ output_tokens
+}
+
+/// Implement the ArgsInfo trait for a struct annotated with argh attributes.
+fn impl_arg_info_struct(
+ errors: &Errors,
+ name: &syn::Ident,
+ type_attrs: &TypeAttrs,
+ generic_args: &syn::Generics,
+ ds: &syn::DataStruct,
+) -> TokenStream {
+ // Collect the fields, skipping fields that are not supported.
+ let fields = match &ds.fields {
+ syn::Fields::Named(fields) => fields,
+ syn::Fields::Unnamed(_) => {
+ errors.err(
+ &ds.struct_token,
+ "`#![derive(ArgsInfo)]` is not currently supported on tuple structs",
+ );
+ return TokenStream::new();
+ }
+ syn::Fields::Unit => {
+ errors.err(&ds.struct_token, "#![derive(ArgsInfo)]` cannot be applied to unit structs");
+ return TokenStream::new();
+ }
+ };
+
+ // Map the fields into StructField objects.
+ let fields: Vec<_> = fields
+ .named
+ .iter()
+ .filter_map(|field| {
+ let attrs = FieldAttrs::parse(errors, field);
+ StructField::new(errors, field, attrs)
+ })
+ .collect();
+
+ let impl_span = Span::call_site();
+
+ // Generate the implementation of `get_args_info()` for this struct.
+ let args_info = impl_args_info_data(name, errors, type_attrs, &fields);
+
+ // Split out the generics info for the impl declaration.
+ let (impl_generics, ty_generics, where_clause) = generic_args.split_for_impl();
+
+ quote_spanned! { impl_span =>
+ #[automatically_derived]
+ impl #impl_generics argh::ArgsInfo for #name #ty_generics #where_clause {
+ fn get_args_info() -> argh::CommandInfoWithArgs {
+ #args_info
+ }
+ }
+ }
+}
+
+/// Implement ArgsInfo for an enum. The enum is a collection of subcommands.
+fn impl_arg_info_enum(
+ errors: &Errors,
+ name: &syn::Ident,
+ type_attrs: &TypeAttrs,
+ generic_args: &syn::Generics,
+ de: &syn::DataEnum,
+) -> TokenStream {
+ // Validate the enum is OK for argh.
+ check_enum_type_attrs(errors, type_attrs, &de.enum_token.span);
+
+ // Ensure that `#[argh(subcommand)]` is present.
+ if type_attrs.is_subcommand.is_none() {
+ errors.err_span(
+ de.enum_token.span,
+ concat!(
+ "`#![derive(ArgsInfo)]` on `enum`s can only be used to enumerate subcommands.\n",
+ "Consider adding `#[argh(subcommand)]` to the `enum` declaration.",
+ ),
+ );
+ }
+
+ // One of the variants can be annotated as providing dynamic subcommands.
+ // We treat this differently since we need to call a function at runtime
+ // to determine the subcommands provided.
+ let mut dynamic_type_and_variant = None;
+
+ // An enum variant like `<name>(<ty>)`. This is used to collect
+ // the type of the variant for each subcommand.
+ struct ArgInfoVariant<'a> {
+ ty: &'a syn::Type,
+ }
+
+ let variants: Vec<ArgInfoVariant<'_>> = de
+ .variants
+ .iter()
+ .filter_map(|variant| {
+ let name = &variant.ident;
+ let ty = enum_only_single_field_unnamed_variants(errors, &variant.fields)?;
+ if VariantAttrs::parse(errors, variant).is_dynamic.is_some() {
+ if dynamic_type_and_variant.is_some() {
+ errors.err(variant, "Only one variant can have the `dynamic` attribute");
+ }
+ dynamic_type_and_variant = Some((ty, name));
+ None
+ } else {
+ Some(ArgInfoVariant { ty })
+ }
+ })
+ .collect();
+
+ let dynamic_subcommands = if let Some((dynamic_type, _)) = dynamic_type_and_variant {
+ quote! {
+ <#dynamic_type as argh::DynamicSubCommand>::commands().iter()
+ .map(|s|
+ SubCommandInfo {
+ name: s.name,
+ command: CommandInfoWithArgs {
+ name: s.name,
+ description: s.description,
+ ..Default::default()
+ }
+ }).collect()
+ }
+ } else {
+ quote! { vec![]}
+ };
+
+ let variant_ty_info = variants.iter().map(|t| {
+ let ty = t.ty;
+ quote!(
+ argh::SubCommandInfo {
+ name: #ty::get_args_info().name,
+ command: #ty::get_args_info()
+ }
+ )
+ });
+
+ let cmd_name = if let Some(id) = &type_attrs.name {
+ id.clone()
+ } else {
+ LitStr::new("", Span::call_site())
+ };
+
+ let (impl_generics, ty_generics, where_clause) = generic_args.split_for_impl();
+
+ quote! {
+ #[automatically_derived]
+ impl #impl_generics argh::ArgsInfo for #name #ty_generics #where_clause {
+ fn get_args_info() -> argh::CommandInfoWithArgs {
+
+ let mut the_subcommands = vec![#(#variant_ty_info),*];
+ let mut dynamic_commands = #dynamic_subcommands;
+
+ the_subcommands.append(&mut dynamic_commands);
+
+
+ argh::CommandInfoWithArgs {
+ name: #cmd_name,
+ /// A short description of the command's functionality.
+ description: " enum of subcommands",
+ commands: the_subcommands,
+ ..Default::default()
+ }
+ } // end of get_args_ifo
+ } // end of impl ArgsInfo
+ }
+}
+
+fn impl_args_info_data<'a>(
+ name: &proc_macro2::Ident,
+ errors: &Errors,
+ type_attrs: &TypeAttrs,
+ fields: &'a [StructField<'a>],
+) -> TokenStream {
+ let mut subcommands_iter =
+ fields.iter().filter(|field| field.kind == FieldKind::SubCommand).fuse();
+
+ let subcommand: Option<&StructField<'_>> = subcommands_iter.next();
+ for dup_subcommand in subcommands_iter {
+ errors.duplicate_attrs("subcommand", subcommand.unwrap().field, dup_subcommand.field);
+ }
+
+ let impl_span = Span::call_site();
+
+ let mut positionals = vec![];
+ let mut flags = vec![];
+
+ // Add the implicit --help flag
+ flags.push(quote! {
+ argh::FlagInfo {
+ short: None,
+ long: "--help",
+ description: "display usage information",
+ optionality: argh::Optionality::Optional,
+ kind: argh::FlagInfoKind::Switch,
+ hidden: false
+ }
+ });
+
+ for field in fields {
+ let optionality = match field.optionality {
+ Optionality::None => quote! { argh::Optionality::Required },
+ Optionality::Defaulted(_) => quote! { argh::Optionality::Optional },
+ Optionality::Optional => quote! { argh::Optionality::Optional },
+ Optionality::Repeating if field.attrs.greedy.is_some() => {
+ quote! { argh::Optionality::Greedy }
+ }
+ Optionality::Repeating => quote! { argh::Optionality::Repeating },
+ };
+
+ match field.kind {
+ FieldKind::Positional => {
+ let name = field.positional_arg_name();
+
+ let description = if let Some(desc) = &field.attrs.description {
+ desc.content.value().trim().to_owned()
+ } else {
+ String::new()
+ };
+ let hidden = field.attrs.hidden_help;
+
+ positionals.push(quote! {
+ argh::PositionalInfo {
+ name: #name,
+ description: #description,
+ optionality: #optionality,
+ hidden: #hidden,
+ }
+ });
+ }
+ FieldKind::Switch | FieldKind::Option => {
+ let short = if let Some(short) = &field.attrs.short {
+ quote! { Some(#short) }
+ } else {
+ quote! { None }
+ };
+
+ let long = field.long_name.as_ref().expect("missing long name for option");
+
+ let description = require_description(
+ errors,
+ field.name.span(),
+ &field.attrs.description,
+ "field",
+ );
+
+ let kind = if field.kind == FieldKind::Switch {
+ quote! {
+ argh::FlagInfoKind::Switch
+ }
+ } else {
+ let arg_name = if let Some(arg_name) = &field.attrs.arg_name {
+ quote! { #arg_name }
+ } else {
+ let arg_name = long.trim_start_matches("--");
+ quote! { #arg_name }
+ };
+
+ quote! {
+ argh::FlagInfoKind::Option {
+ arg_name: #arg_name,
+ }
+ }
+ };
+
+ let hidden = field.attrs.hidden_help;
+
+ flags.push(quote! {
+ argh::FlagInfo {
+ short: #short,
+ long: #long,
+ description: #description,
+ optionality: #optionality,
+ kind: #kind,
+ hidden: #hidden,
+ }
+ });
+ }
+ FieldKind::SubCommand => {}
+ }
+ }
+
+ let empty_str = syn::LitStr::new("", Span::call_site());
+ let type_name = LitStr::new(&name.to_string(), Span::call_site());
+ let subcommand_name = if type_attrs.is_subcommand.is_some() {
+ type_attrs.name.as_ref().unwrap_or_else(|| {
+ errors.err(name, "`#[argh(name = \"...\")]` attribute is required for subcommands");
+ &empty_str
+ })
+ } else {
+ &type_name
+ };
+
+ let subcommand = if let Some(subcommand) = subcommand {
+ let subcommand_ty = subcommand.ty_without_wrapper;
+ quote! {
+ #subcommand_ty::get_subcommands()
+ }
+ } else {
+ quote! {vec![]}
+ };
+
+ let description =
+ require_description(errors, Span::call_site(), &type_attrs.description, "type");
+ let examples = type_attrs.examples.iter().map(|e| quote! { #e });
+ let notes = type_attrs.notes.iter().map(|e| quote! { #e });
+
+ let error_codes = type_attrs.error_codes.iter().map(|(code, text)| {
+ quote! { argh::ErrorCodeInfo{code:#code, description: #text} }
+ });
+
+ quote_spanned! { impl_span =>
+ argh::CommandInfoWithArgs {
+ name: #subcommand_name,
+ description: #description,
+ examples: &[#( #examples, )*],
+ notes: &[#( #notes, )*],
+ positionals: &[#( #positionals, )*],
+ flags: &[#( #flags, )*],
+ commands: #subcommand,
+ error_codes: &[#( #error_codes, )*],
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 94cc437..8b3b7ee 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ use {
syn::{spanned::Spanned, GenericArgument, LitStr, PathArguments, Type},
};
+mod args_info;
mod errors;
mod help;
mod parse_attrs;
@@ -31,6 +32,14 @@ pub fn argh_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
gen.into()
}
+/// Entrypoint for `#[derive(ArgsInfo)]`.
+#[proc_macro_derive(ArgsInfo, attributes(argh))]
+pub fn args_info_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let ast = syn::parse_macro_input!(input as syn::DeriveInput);
+ let gen = args_info::impl_args_info(&ast);
+ gen.into()
+}
+
/// Transform the input into a token stream containing any generated implementations,
/// as well as all errors that occurred.
fn impl_from_args(input: &syn::DeriveInput) -> TokenStream {
diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs
index 10ebcb5..f968ca7 100644
--- a/src/parse_attrs.rs
+++ b/src/parse_attrs.rs
@@ -13,7 +13,7 @@ use {
pub struct FieldAttrs {
pub default: Option<syn::LitStr>,
pub description: Option<Description>,
- pub from_str_fn: Option<syn::Ident>,
+ pub from_str_fn: Option<syn::ExprPath>,
pub field_type: Option<FieldType>,
pub long: Option<syn::LitStr>,
pub short: Option<syn::LitChar>,
@@ -209,7 +209,7 @@ fn parse_attr_fn_name(
errors: &Errors,
m: &syn::MetaList,
attr_name: &str,
- slot: &mut Option<syn::Ident>,
+ slot: &mut Option<syn::ExprPath>,
) {
if let Some(first) = slot {
errors.duplicate_attrs(attr_name, first, m);