Snap for 10985023 from 24a1e567d57b886f024eeda9a020a11cce143c2f to 24Q1-release

Change-Id: I4b4607c1acee4bb7cf7225389ad640c95aabeb26
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index eed94c9..d4d81dd 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "3f3c29726a21c4b541bb2b9aa2c592461897ded0"
+    "sha1": "e8efc8285f632a4ebedfe4377e0f1d78276f8e19"
   },
   "path_in_vcs": "argh"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 21682b8..8149825 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,7 +23,7 @@
     host_supported: true,
     crate_name: "argh",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.10",
+    cargo_pkg_version: "0.1.12",
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
@@ -39,11 +39,32 @@
 }
 
 rust_test {
+    name: "argh_test_tests_args_info_tests",
+    host_supported: true,
+    crate_name: "args_info_tests",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.1.12",
+    srcs: ["tests/args_info_tests.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    test_options: {
+        unit_test: true,
+    },
+    edition: "2018",
+    rustlibs: [
+        "libargh",
+        "libargh_shared",
+        "libonce_cell",
+    ],
+    proc_macros: ["libargh_derive"],
+}
+
+rust_test {
     name: "argh_test_tests_lib",
     host_supported: true,
     crate_name: "lib",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.10",
+    cargo_pkg_version: "0.1.12",
     srcs: ["tests/lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
@@ -64,7 +85,7 @@
     host_supported: true,
     crate_name: "argh",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.10",
+    cargo_pkg_version: "0.1.12",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: ["libargh_shared"],
diff --git a/Cargo.lock b/Cargo.lock
index 59f8c1a..d08061e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,7 +4,7 @@
 
 [[package]]
 name = "argh"
-version = "0.1.10"
+version = "0.1.12"
 dependencies = [
  "argh_derive",
  "argh_shared",
@@ -14,9 +14,9 @@
 
 [[package]]
 name = "argh_derive"
-version = "0.1.10"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
+checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a"
 dependencies = [
  "argh_shared",
  "proc-macro2",
@@ -26,9 +26,21 @@
 
 [[package]]
 name = "argh_shared"
-version = "0.1.10"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
+checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "basic-toml"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bfc506e7a2370ec239e1d072507b2a80c833083699d3c6fa176fbb4de8448c6"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "glob"
@@ -38,51 +50,54 @@
 
 [[package]]
 name = "itoa"
-version = "1.0.5"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "once_cell"
-version = "1.17.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.49"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.23"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.12"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "serde"
-version = "1.0.152"
+version = "1.0.183"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
+dependencies = [
+ "serde_derive",
+]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.152"
+version = "1.0.183"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -91,9 +106,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.91"
+version = "1.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
+checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
 dependencies = [
  "itoa",
  "ryu",
@@ -102,9 +117,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.107"
+version = "2.0.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -113,42 +128,33 @@
 
 [[package]]
 name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
-name = "toml"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
-dependencies = [
- "serde",
-]
-
-[[package]]
 name = "trybuild"
-version = "1.0.75"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1212c215a87a183687a7cc7065901b1a98da6b37277d51a1b5faedbb4efd4f3"
+checksum = "a84e0202ea606ba5ebee8507ab2bfbe89b98551ed9b8f0be198109275cff284b"
 dependencies = [
+ "basic-toml",
  "glob",
  "once_cell",
  "serde",
  "serde_derive",
  "serde_json",
  "termcolor",
- "toml",
 ]
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
 
 [[package]]
 name = "winapi"
diff --git a/Cargo.toml b/Cargo.toml
index 2867953..16a3e18 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2018"
 name = "argh"
-version = "0.1.10"
+version = "0.1.12"
 authors = [
     "Taylor Cramer <cramertj@google.com>",
     "Benjamin Brittain <bwb@google.com>",
@@ -30,10 +30,10 @@
 repository = "https://github.com/google/argh"
 
 [dependencies.argh_derive]
-version = "0.1.10"
+version = "0.1.12"
 
 [dependencies.argh_shared]
-version = "0.1.10"
+version = "0.1.12"
 
 [dev-dependencies.once_cell]
 version = "1.10.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 2f1b7f0..fc6daba 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "argh"
-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"
 keywords = ["args", "arguments", "derive", "cli"]
@@ -10,8 +10,8 @@
 readme = "README.md"
 
 [dependencies]
-argh_shared = { version = "0.1.10", path = "../argh_shared" }
-argh_derive = { version = "0.1.10", path = "../argh_derive" }
+argh_shared = { version = "0.1.12", path = "../argh_shared" }
+argh_derive = { version = "0.1.12", path = "../argh_derive" }
 
 [dev-dependencies]
 once_cell = "1.10.0"
diff --git a/METADATA b/METADATA
index 6a1602e..b5ae03f 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
-# 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"
 description: "Derive-based argument parser optimized for code size"
@@ -11,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/argh/argh-0.1.10.crate"
+    value: "https://static.crates.io/crates/argh/argh-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: 19
   }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 4fb6ff5..c419e6a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -318,11 +318,30 @@
 
 use std::str::FromStr;
 
-pub use argh_derive::FromArgs;
+pub use argh_derive::{ArgsInfo, FromArgs};
 
 /// Information about a particular command used for output.
 pub type CommandInfo = argh_shared::CommandInfo<'static>;
 
+/// Information about the command including the options and arguments.
+pub type CommandInfoWithArgs = argh_shared::CommandInfoWithArgs<'static>;
+
+/// Information about a subcommand.
+pub type SubCommandInfo = argh_shared::SubCommandInfo<'static>;
+
+pub use argh_shared::{ErrorCodeInfo, FlagInfo, FlagInfoKind, Optionality, PositionalInfo};
+
+/// Structured information about the command line arguments.
+pub trait ArgsInfo {
+    /// Returns the argument info.
+    fn get_args_info() -> CommandInfoWithArgs;
+
+    /// Returns the list of subcommands
+    fn get_subcommands() -> Vec<SubCommandInfo> {
+        Self::get_args_info().commands
+    }
+}
+
 /// Types which can be constructed from a set of commandline arguments.
 pub trait FromArgs: Sized {
     /// Construct the type from an input set of arguments.
diff --git a/tests/args_info_tests.rs b/tests/args_info_tests.rs
new file mode 100644
index 0000000..1fc0730
--- /dev/null
+++ b/tests/args_info_tests.rs
@@ -0,0 +1,1029 @@
+#![cfg(test)]
+// 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 argh::{
+    ArgsInfo, CommandInfoWithArgs, ErrorCodeInfo, FlagInfo, FlagInfoKind, FromArgs, Optionality,
+    PositionalInfo, SubCommandInfo,
+};
+
+fn assert_args_info<T: ArgsInfo>(expected: &CommandInfoWithArgs) {
+    let actual_value = T::get_args_info();
+    assert_eq!(expected, &actual_value)
+}
+
+const HELP_FLAG: FlagInfo<'_> = FlagInfo {
+    kind: FlagInfoKind::Switch,
+    optionality: Optionality::Optional,
+    long: "--help",
+    short: None,
+    description: "display usage information",
+    hidden: false,
+};
+
+/// Tests that exercise the JSON output for help text.
+#[test]
+fn args_info_test_subcommand() {
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// Top-level command.
+    struct TopLevel {
+        #[argh(subcommand)]
+        nested: MySubCommandEnum,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(subcommand)]
+    enum MySubCommandEnum {
+        One(SubCommandOne),
+        Two(SubCommandTwo),
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// First subcommand.
+    #[argh(subcommand, name = "one")]
+    struct SubCommandOne {
+        #[argh(option)]
+        /// how many x
+        x: usize,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// Second subcommand.
+    #[argh(subcommand, name = "two")]
+    struct SubCommandTwo {
+        #[argh(switch)]
+        /// whether to fooey
+        fooey: bool,
+    }
+
+    let command_one = CommandInfoWithArgs {
+        name: "one",
+        description: "First subcommand.",
+        flags: &[
+            HELP_FLAG,
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "x" },
+                optionality: Optionality::Required,
+                long: "--x",
+                short: None,
+                description: "how many x",
+                hidden: false,
+            },
+        ],
+        ..Default::default()
+    };
+
+    assert_args_info::<TopLevel>(&CommandInfoWithArgs {
+        name: "TopLevel",
+        description: "Top-level command.",
+        examples: &[],
+        flags: &[HELP_FLAG],
+        notes: &[],
+        positionals: &[],
+        error_codes: &[],
+        commands: vec![
+            SubCommandInfo { name: "one", command: command_one.clone() },
+            SubCommandInfo {
+                name: "two",
+                command: CommandInfoWithArgs {
+                    name: "two",
+                    description: "Second subcommand.",
+                    flags: &[
+                        HELP_FLAG,
+                        FlagInfo {
+                            kind: FlagInfoKind::Switch,
+                            optionality: Optionality::Optional,
+                            long: "--fooey",
+                            short: None,
+                            description: "whether to fooey",
+                            hidden: false,
+                        },
+                    ],
+                    ..Default::default()
+                },
+            },
+        ],
+    });
+
+    assert_args_info::<SubCommandOne>(&command_one);
+}
+
+#[test]
+fn args_info_test_multiline_doc_comment() {
+    #[derive(FromArgs, ArgsInfo)]
+    /// Short description
+    struct Cmd {
+        #[argh(switch)]
+        /// a switch with a description
+        /// that is spread across
+        /// a number of
+        /// lines of comments.
+        _s: bool,
+    }
+    assert_args_info::<Cmd>(
+            &CommandInfoWithArgs {
+                name: "Cmd",
+                description: "Short description",
+                flags: &[HELP_FLAG,
+                FlagInfo {
+                    kind: FlagInfoKind::Switch,
+                    optionality: Optionality::Optional,
+                    long: "--s",
+                    short: None,
+                    description: "a switch with a description that is spread across a number of lines of comments.",
+                    hidden:false
+                }
+                ],
+           ..Default::default()
+            });
+}
+
+#[test]
+fn args_info_test_basic_args() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Basic command args demonstrating multiple types and cardinality. "With quotes"
+    struct Basic {
+        /// should the power be on. "Quoted value" should work too.
+        #[argh(switch)]
+        power: bool,
+
+        /// option that is required because of no default and not Option<>.
+        #[argh(option, long = "required")]
+        required_flag: String,
+
+        /// optional speed if not specified it is None.
+        #[argh(option, short = 's')]
+        speed: Option<u8>,
+
+        /// repeatable option.
+        #[argh(option, arg_name = "url")]
+        link: Vec<String>,
+    }
+    assert_args_info::<Basic>(&CommandInfoWithArgs {
+        name: "Basic",
+        description:
+            "Basic command args demonstrating multiple types and cardinality. \"With quotes\"",
+        flags: &[
+            FlagInfo {
+                kind: FlagInfoKind::Switch,
+                optionality: Optionality::Optional,
+                long: "--help",
+                short: None,
+                description: "display usage information",
+                hidden: false,
+            },
+            FlagInfo {
+                kind: FlagInfoKind::Switch,
+                optionality: Optionality::Optional,
+                long: "--power",
+                short: None,
+                description: "should the power be on. \"Quoted value\" should work too.",
+                hidden: false,
+            },
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "required" },
+                optionality: Optionality::Required,
+                long: "--required",
+                short: None,
+                description: "option that is required because of no default and not Option<>.",
+                hidden: false,
+            },
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "speed" },
+                optionality: Optionality::Optional,
+                long: "--speed",
+                short: Some('s'),
+                description: "optional speed if not specified it is None.",
+                hidden: false,
+            },
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "url" },
+                optionality: Optionality::Repeating,
+                long: "--link",
+                short: None,
+                description: "repeatable option.",
+                hidden: false,
+            },
+        ],
+        ..Default::default()
+    });
+}
+
+#[test]
+fn args_info_test_positional_args() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Command with positional args demonstrating. "With quotes"
+    struct Positional {
+        /// the "root" position.
+        #[argh(positional, arg_name = "root")]
+        root_value: String,
+
+        /// trunk value
+        #[argh(positional)]
+        trunk: String,
+
+        /// leaves. There can be many leaves.
+        #[argh(positional)]
+        leaves: Vec<String>,
+    }
+    assert_args_info::<Positional>(&CommandInfoWithArgs {
+        name: "Positional",
+        description: "Command with positional args demonstrating. \"With quotes\"",
+        flags: &[HELP_FLAG],
+        positionals: &[
+            PositionalInfo {
+                name: "root",
+                description: "the \"root\" position.",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "trunk",
+                description: "trunk value",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "leaves",
+                description: "leaves. There can be many leaves.",
+                optionality: Optionality::Repeating,
+                hidden: false,
+            },
+        ],
+
+        ..Default::default()
+    });
+}
+
+#[test]
+fn args_info_test_optional_positional_args() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Command with positional args demonstrating last value is optional
+    struct Positional {
+        /// the "root" position.
+        #[argh(positional, arg_name = "root")]
+        root_value: String,
+
+        /// trunk value
+        #[argh(positional)]
+        trunk: String,
+
+        /// leaves. There can be an optional leaves.
+        #[argh(positional)]
+        leaves: Option<String>,
+    }
+    assert_args_info::<Positional>(&CommandInfoWithArgs {
+        name: "Positional",
+        description: "Command with positional args demonstrating last value is optional",
+        flags: &[FlagInfo {
+            kind: FlagInfoKind::Switch,
+            optionality: Optionality::Optional,
+            long: "--help",
+            short: None,
+            description: "display usage information",
+            hidden: false,
+        }],
+        positionals: &[
+            PositionalInfo {
+                name: "root",
+                description: "the \"root\" position.",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "trunk",
+                description: "trunk value",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "leaves",
+                description: "leaves. There can be an optional leaves.",
+                optionality: Optionality::Optional,
+                hidden: false,
+            },
+        ],
+
+        ..Default::default()
+    });
+}
+
+#[test]
+fn args_info_test_default_positional_args() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Command with positional args demonstrating last value is defaulted.
+    struct Positional {
+        /// the "root" position.
+        #[argh(positional, arg_name = "root")]
+        root_value: String,
+
+        /// trunk value
+        #[argh(positional)]
+        trunk: String,
+
+        /// leaves. There can be one leaf, defaults to hello.
+        #[argh(positional, default = "String::from(\"hello\")")]
+        leaves: String,
+    }
+    assert_args_info::<Positional>(&CommandInfoWithArgs {
+        name: "Positional",
+        description: "Command with positional args demonstrating last value is defaulted.",
+        flags: &[HELP_FLAG],
+        positionals: &[
+            PositionalInfo {
+                name: "root",
+                description: "the \"root\" position.",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "trunk",
+                description: "trunk value",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "leaves",
+                description: "leaves. There can be one leaf, defaults to hello.",
+                optionality: Optionality::Optional,
+                hidden: false,
+            },
+        ],
+
+        ..Default::default()
+    });
+}
+
+#[test]
+fn args_info_test_notes_examples_errors() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Command with Examples and usage Notes, including error codes.
+    #[argh(
+        note = r##"
+    These usage notes appear for {command_name} and how to best use it.
+    The formatting should be preserved.
+    one
+    two
+    three then a blank
+    
+    and one last line with "quoted text"."##,
+        example = r##"
+    Use the command with 1 file:
+    `{command_name} /path/to/file`
+    Use it with a "wildcard":
+    `{command_name} /path/to/*`
+     a blank line
+    
+    and one last line with "quoted text"."##,
+        error_code(0, "Success"),
+        error_code(1, "General Error"),
+        error_code(2, "Some error with \"quotes\"")
+    )]
+    struct NotesExamplesErrors {
+        /// the "root" position.
+        #[argh(positional, arg_name = "files")]
+        fields: Vec<std::path::PathBuf>,
+    }
+    assert_args_info::<NotesExamplesErrors>(
+            &CommandInfoWithArgs {
+                name: "NotesExamplesErrors",
+                description: "Command with Examples and usage Notes, including error codes.",
+                examples: &["\n    Use the command with 1 file:\n    `{command_name} /path/to/file`\n    Use it with a \"wildcard\":\n    `{command_name} /path/to/*`\n     a blank line\n    \n    and one last line with \"quoted text\"."],
+                flags: &[HELP_FLAG
+                ],
+                positionals: &[
+                    PositionalInfo{
+                        name: "files",
+                        description: "the \"root\" position.",
+                        optionality: Optionality::Repeating,
+                        hidden:false
+                    }
+                ],
+                notes: &["\n    These usage notes appear for {command_name} and how to best use it.\n    The formatting should be preserved.\n    one\n    two\n    three then a blank\n    \n    and one last line with \"quoted text\"."],
+                error_codes: & [ErrorCodeInfo { code: 0, description: "Success" }, ErrorCodeInfo { code: 1, description: "General Error" }, ErrorCodeInfo { code: 2, description: "Some error with \"quotes\"" }],
+                ..Default::default()
+            });
+}
+
+#[test]
+fn args_info_test_subcommands() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    ///Top level command with "subcommands".
+    struct TopLevel {
+        /// show verbose output
+        #[argh(switch)]
+        verbose: bool,
+
+        /// this doc comment does not appear anywhere.
+        #[argh(subcommand)]
+        cmd: SubcommandEnum,
+    }
+
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(subcommand)]
+    /// Doc comments for subcommand enums does not appear in the help text.
+    enum SubcommandEnum {
+        Command1(Command1Args),
+        Command2(Command2Args),
+        Command3(Command3Args),
+    }
+
+    /// Command1 args are used for Command1.
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(subcommand, name = "one")]
+    struct Command1Args {
+        /// the "root" position.
+        #[argh(positional, arg_name = "root")]
+        root_value: String,
+
+        /// trunk value
+        #[argh(positional)]
+        trunk: String,
+
+        /// leaves. There can be zero leaves, defaults to hello.
+        #[argh(positional, default = "String::from(\"hello\")")]
+        leaves: String,
+    }
+    /// Command2 args are used for Command2.
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(subcommand, name = "two")]
+    struct Command2Args {
+        /// should the power be on. "Quoted value" should work too.
+        #[argh(switch)]
+        power: bool,
+
+        /// option that is required because of no default and not Option<>.
+        #[argh(option, long = "required")]
+        required_flag: String,
+
+        /// optional speed if not specified it is None.
+        #[argh(option, short = 's')]
+        speed: Option<u8>,
+
+        /// repeatable option.
+        #[argh(option, arg_name = "url")]
+        link: Vec<String>,
+    }
+    /// Command3 args are used for Command3 which has no options or arguments.
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(subcommand, name = "three")]
+    struct Command3Args {}
+
+    assert_args_info::<TopLevel>(&CommandInfoWithArgs {
+        name: "TopLevel",
+        description: "Top level command with \"subcommands\".",
+        flags: &[
+            HELP_FLAG,
+            FlagInfo {
+                kind: FlagInfoKind::Switch,
+                optionality: Optionality::Optional,
+                long: "--verbose",
+                short: None,
+                description: "show verbose output",
+                hidden: false,
+            },
+        ],
+        positionals: &[],
+        commands: vec![
+            SubCommandInfo {
+                name: "one",
+                command: CommandInfoWithArgs {
+                    name: "one",
+                    description: "Command1 args are used for Command1.",
+                    flags: &[HELP_FLAG],
+                    positionals: &[
+                        PositionalInfo {
+                            name: "root",
+                            description: "the \"root\" position.",
+                            optionality: Optionality::Required,
+                            hidden: false,
+                        },
+                        PositionalInfo {
+                            name: "trunk",
+                            description: "trunk value",
+                            optionality: Optionality::Required,
+                            hidden: false,
+                        },
+                        PositionalInfo {
+                            name: "leaves",
+                            description: "leaves. There can be zero leaves, defaults to hello.",
+                            optionality: Optionality::Optional,
+                            hidden: false,
+                        },
+                    ],
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "two",
+                command: CommandInfoWithArgs {
+                    name: "two",
+                    description: "Command2 args are used for Command2.",
+                    flags: &[
+                        HELP_FLAG,
+                        FlagInfo {
+                            kind: FlagInfoKind::Switch,
+                            optionality: Optionality::Optional,
+                            long: "--power",
+                            short: None,
+                            description:
+                                "should the power be on. \"Quoted value\" should work too.",
+                            hidden: false,
+                        },
+                        FlagInfo {
+                            kind: FlagInfoKind::Option { arg_name: "required" },
+                            optionality: Optionality::Required,
+                            long: "--required",
+                            short: None,
+                            description:
+                                "option that is required because of no default and not Option<>.",
+                            hidden: false,
+                        },
+                        FlagInfo {
+                            kind: FlagInfoKind::Option { arg_name: "speed" },
+                            optionality: Optionality::Optional,
+                            long: "--speed",
+                            short: Some('s'),
+                            description: "optional speed if not specified it is None.",
+                            hidden: false,
+                        },
+                        FlagInfo {
+                            kind: FlagInfoKind::Option { arg_name: "url" },
+                            optionality: Optionality::Repeating,
+                            long: "--link",
+                            short: None,
+                            description: "repeatable option.",
+                            hidden: false,
+                        },
+                    ],
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "three",
+                command: CommandInfoWithArgs {
+                    name: "three",
+                    description:
+                        "Command3 args are used for Command3 which has no options or arguments.",
+                    flags: &[HELP_FLAG],
+                    positionals: &[],
+                    ..Default::default()
+                },
+            },
+        ],
+        ..Default::default()
+    });
+}
+
+#[test]
+fn args_info_test_subcommand_notes_examples() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    ///Top level command with "subcommands".
+    #[argh(
+        note = "Top level note",
+        example = "Top level example",
+        error_code(0, "Top level success")
+    )]
+    struct TopLevel {
+        /// this doc comment does not appear anywhere.
+        #[argh(subcommand)]
+        cmd: SubcommandEnum,
+    }
+
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(subcommand)]
+    /// Doc comments for subcommand enums does not appear in the help text.
+    enum SubcommandEnum {
+        Command1(Command1Args),
+    }
+
+    /// Command1 args are used for subcommand one.
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    #[argh(
+        subcommand,
+        name = "one",
+        note = "{command_name} is used as a subcommand of \"Top level\"",
+        example = "\"Typical\" usage is `{command_name}`.",
+        error_code(0, "one level success")
+    )]
+    struct Command1Args {
+        /// the "root" position.
+        #[argh(positional, arg_name = "root")]
+        root_value: String,
+
+        /// trunk value
+        #[argh(positional)]
+        trunk: String,
+
+        /// leaves. There can be many leaves.
+        #[argh(positional)]
+        leaves: Vec<String>,
+    }
+
+    let command_one = CommandInfoWithArgs {
+        name: "one",
+        description: "Command1 args are used for subcommand one.",
+        error_codes: &[ErrorCodeInfo { code: 0, description: "one level success" }],
+        examples: &["\"Typical\" usage is `{command_name}`."],
+        flags: &[HELP_FLAG],
+        notes: &["{command_name} is used as a subcommand of \"Top level\""],
+        positionals: &[
+            PositionalInfo {
+                name: "root",
+                description: "the \"root\" position.",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "trunk",
+                description: "trunk value",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "leaves",
+                description: "leaves. There can be many leaves.",
+                optionality: Optionality::Repeating,
+                hidden: false,
+            },
+        ],
+        ..Default::default()
+    };
+
+    assert_args_info::<TopLevel>(&CommandInfoWithArgs {
+        name: "TopLevel",
+        description: "Top level command with \"subcommands\".",
+        error_codes: &[ErrorCodeInfo { code: 0, description: "Top level success" }],
+        examples: &["Top level example"],
+        flags: &[HELP_FLAG],
+        notes: &["Top level note"],
+        commands: vec![SubCommandInfo { name: "one", command: command_one.clone() }],
+        ..Default::default()
+    });
+
+    assert_args_info::<Command1Args>(&command_one);
+}
+
+#[test]
+fn args_info_test_example() {
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(
+        description = "Destroy the contents of <file> with a specific \"method of destruction\".",
+        example = "Scribble 'abc' and then run |grind|.\n$ {command_name} -s 'abc' grind old.txt taxes.cp",
+        note = "Use `{command_name} help <command>` for details on [<args>] for a subcommand.",
+        error_code(2, "The blade is too dull."),
+        error_code(3, "Out of fuel.")
+    )]
+    struct HelpExample {
+        /// force, ignore minor errors. This description is so long that it wraps to the next line.
+        #[argh(switch, short = 'f')]
+        force: bool,
+
+        /// documentation
+        #[argh(switch)]
+        really_really_really_long_name_for_pat: bool,
+
+        /// write <scribble> repeatedly
+        #[argh(option, short = 's')]
+        scribble: String,
+
+        /// say more. Defaults to $BLAST_VERBOSE.
+        #[argh(switch, short = 'v')]
+        verbose: bool,
+
+        #[argh(subcommand)]
+        command: HelpExampleSubCommands,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(subcommand)]
+    enum HelpExampleSubCommands {
+        BlowUp(BlowUp),
+        Grind(GrindCommand),
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(subcommand, name = "blow-up")]
+    /// explosively separate
+    struct BlowUp {
+        /// blow up bombs safely
+        #[argh(switch)]
+        safely: bool,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(subcommand, name = "grind", description = "make smaller by many small cuts")]
+    struct GrindCommand {
+        /// wear a visor while grinding
+        #[argh(switch)]
+        safely: bool,
+    }
+
+    assert_args_info::<HelpExample>(
+            &CommandInfoWithArgs {
+                name: "HelpExample",
+                description: "Destroy the contents of <file> with a specific \"method of destruction\".",
+                examples: &["Scribble 'abc' and then run |grind|.\n$ {command_name} -s 'abc' grind old.txt taxes.cp"],
+                flags: &[HELP_FLAG,
+                FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--force", short: Some('f'), description: "force, ignore minor errors. This description is so long that it wraps to the next line.",
+                hidden:false },
+                FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--really-really-really-long-name-for-pat", short: None, description: "documentation",
+                hidden:false },
+                FlagInfo { kind: FlagInfoKind::Option { arg_name: "scribble"},
+                 optionality: Optionality::Required, long: "--scribble", short: Some('s'), description: "write <scribble> repeatedly",
+                 hidden:false },
+                  FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--verbose", short: Some('v'), description: "say more. Defaults to $BLAST_VERBOSE.",
+                  hidden:false }
+                ],
+                notes: &["Use `{command_name} help <command>` for details on [<args>] for a subcommand."],
+                commands: vec![
+                    SubCommandInfo { name: "blow-up",
+                 command: CommandInfoWithArgs { name: "blow-up",
+                  description: "explosively separate", 
+                  flags:& [HELP_FLAG,
+                   FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--safely", short: None, description: "blow up bombs safely",
+                   hidden:false }
+                   ],
+                ..Default::default()
+             } },
+              SubCommandInfo {
+                 name: "grind",
+                 command: CommandInfoWithArgs {
+                     name: "grind",
+                     description: "make smaller by many small cuts",
+                     flags: &[HELP_FLAG,
+                      FlagInfo { kind: FlagInfoKind::Switch, optionality: Optionality::Optional, long: "--safely", short: None, description: "wear a visor while grinding" ,hidden:false}],
+                      ..Default::default()
+                     }
+                }],
+                error_codes: &[ErrorCodeInfo { code: 2, description: "The blade is too dull." }, ErrorCodeInfo { code: 3, description: "Out of fuel." }],
+                ..Default::default()
+            }
+            );
+}
+
+#[test]
+fn positional_greedy() {
+    #[allow(dead_code)]
+    #[derive(FromArgs, ArgsInfo)]
+    /// Woot
+    struct LastRepeatingGreedy {
+        #[argh(positional)]
+        /// fooey
+        pub a: u32,
+        #[argh(switch)]
+        /// woo
+        pub b: bool,
+        #[argh(option)]
+        /// stuff
+        pub c: Option<String>,
+        #[argh(positional, greedy)]
+        /// fooey
+        pub d: Vec<String>,
+    }
+    assert_args_info::<LastRepeatingGreedy>(&CommandInfoWithArgs {
+        name: "LastRepeatingGreedy",
+        description: "Woot",
+        flags: &[
+            HELP_FLAG,
+            FlagInfo {
+                kind: FlagInfoKind::Switch,
+                optionality: Optionality::Optional,
+                long: "--b",
+                short: None,
+                description: "woo",
+                hidden: false,
+            },
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "c" },
+                optionality: Optionality::Optional,
+                long: "--c",
+                short: None,
+                description: "stuff",
+                hidden: false,
+            },
+        ],
+        positionals: &[
+            PositionalInfo {
+                name: "a",
+                description: "fooey",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+            PositionalInfo {
+                name: "d",
+                description: "fooey",
+                optionality: Optionality::Greedy,
+                hidden: false,
+            },
+        ],
+        ..Default::default()
+    });
+}
+
+#[test]
+fn hidden_help_attribute() {
+    #[derive(FromArgs, ArgsInfo)]
+    /// Short description
+    struct Cmd {
+        /// this one should be hidden
+        #[argh(positional, hidden_help)]
+        _one: String,
+        #[argh(positional)]
+        /// this one is real
+        _two: String,
+        /// this one should be hidden
+        #[argh(option, hidden_help)]
+        _three: String,
+    }
+
+    assert_args_info::<Cmd>(&CommandInfoWithArgs {
+        name: "Cmd",
+        description: "Short description",
+        flags: &[
+            HELP_FLAG,
+            FlagInfo {
+                kind: FlagInfoKind::Option { arg_name: "three" },
+                optionality: Optionality::Required,
+                long: "--three",
+                short: None,
+                description: "this one should be hidden",
+                hidden: true,
+            },
+        ],
+        positionals: &[
+            PositionalInfo {
+                name: "one",
+                description: "this one should be hidden",
+                optionality: Optionality::Required,
+                hidden: true,
+            },
+            PositionalInfo {
+                name: "two",
+                description: "this one is real",
+                optionality: Optionality::Required,
+                hidden: false,
+            },
+        ],
+        ..Default::default()
+    });
+}
+
+#[test]
+fn test_dynamic_subcommand() {
+    #[derive(PartialEq, Debug)]
+    struct DynamicSubCommandImpl {
+        got: String,
+    }
+
+    impl argh::DynamicSubCommand for DynamicSubCommandImpl {
+        fn commands() -> &'static [&'static argh::CommandInfo] {
+            &[
+                &argh::CommandInfo { name: "three", description: "Third command" },
+                &argh::CommandInfo { name: "four", description: "Fourth command" },
+                &argh::CommandInfo { name: "five", description: "Fifth command" },
+            ]
+        }
+
+        fn try_redact_arg_values(
+            _command_name: &[&str],
+            _args: &[&str],
+        ) -> Option<Result<Vec<String>, argh::EarlyExit>> {
+            Some(Err(argh::EarlyExit::from("Test should not redact".to_owned())))
+        }
+
+        fn try_from_args(
+            command_name: &[&str],
+            args: &[&str],
+        ) -> Option<Result<DynamicSubCommandImpl, argh::EarlyExit>> {
+            let command_name = match command_name.last() {
+                Some(x) => *x,
+                None => return Some(Err(argh::EarlyExit::from("No command".to_owned()))),
+            };
+            let description = Self::commands().iter().find(|x| x.name == command_name)?.description;
+            if args.len() > 1 {
+                Some(Err(argh::EarlyExit::from("Too many arguments".to_owned())))
+            } else if let Some(arg) = args.first() {
+                Some(Ok(DynamicSubCommandImpl { got: format!("{} got {:?}", description, arg) }))
+            } else {
+                Some(Err(argh::EarlyExit::from("Not enough arguments".to_owned())))
+            }
+        }
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// Top-level command.
+    struct TopLevel {
+        #[argh(subcommand)]
+        nested: MySubCommandEnum,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    #[argh(subcommand)]
+    enum MySubCommandEnum {
+        One(SubCommandOne),
+        Two(SubCommandTwo),
+        #[argh(dynamic)]
+        ThreeFourFive(DynamicSubCommandImpl),
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// First subcommand.
+    #[argh(subcommand, name = "one")]
+    struct SubCommandOne {
+        #[argh(option)]
+        /// how many x
+        x: usize,
+    }
+
+    #[derive(FromArgs, ArgsInfo, PartialEq, Debug)]
+    /// Second subcommand.
+    #[argh(subcommand, name = "two")]
+    struct SubCommandTwo {
+        #[argh(switch)]
+        /// whether to fooey
+        fooey: bool,
+    }
+
+    assert_args_info::<TopLevel>(&CommandInfoWithArgs {
+        name: "TopLevel",
+        description: "Top-level command.",
+        flags: &[HELP_FLAG],
+        commands: vec![
+            SubCommandInfo {
+                name: "one",
+                command: CommandInfoWithArgs {
+                    name: "one",
+                    description: "First subcommand.",
+                    flags: &[
+                        HELP_FLAG,
+                        FlagInfo {
+                            kind: FlagInfoKind::Option { arg_name: "x" },
+                            optionality: Optionality::Required,
+                            long: "--x",
+                            short: None,
+                            description: "how many x",
+                            hidden: false,
+                        },
+                    ],
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "two",
+                command: CommandInfoWithArgs {
+                    name: "two",
+                    description: "Second subcommand.",
+                    flags: &[
+                        HELP_FLAG,
+                        FlagInfo {
+                            kind: FlagInfoKind::Switch,
+                            optionality: Optionality::Optional,
+                            long: "--fooey",
+                            short: None,
+                            description: "whether to fooey",
+                            hidden: false,
+                        },
+                    ],
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "three",
+                command: CommandInfoWithArgs {
+                    name: "three",
+                    description: "Third command",
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "four",
+                command: CommandInfoWithArgs {
+                    name: "four",
+                    description: "Fourth command",
+                    ..Default::default()
+                },
+            },
+            SubCommandInfo {
+                name: "five",
+                command: CommandInfoWithArgs {
+                    name: "five",
+                    description: "Fifth command",
+                    ..Default::default()
+                },
+            },
+        ],
+        ..Default::default()
+    })
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index 713c73d..dbd3b48 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -82,6 +82,48 @@
 }
 
 #[test]
+fn nested_from_str_example() {
+    #[derive(FromArgs)]
+    /// Goofy thing.
+    struct FiveStruct {
+        /// always five
+        #[argh(option, from_str_fn(nested::always_five))]
+        five: usize,
+    }
+
+    pub mod nested {
+        pub fn always_five(_value: &str) -> Result<usize, String> {
+            Ok(5)
+        }
+    }
+
+    let f = FiveStruct::from_args(&["cmdname"], &["--five", "woot"]).expect("failed to five");
+    assert_eq!(f.five, 5);
+}
+
+#[test]
+fn method_from_str_example() {
+    #[derive(FromArgs)]
+    /// Goofy thing.
+    struct FiveStruct {
+        /// always five
+        #[argh(option, from_str_fn(AlwaysFive::<usize>::always_five))]
+        five: usize,
+    }
+
+    struct AlwaysFive<T>(T);
+
+    impl AlwaysFive<usize> {
+        fn always_five(_value: &str) -> Result<usize, String> {
+            Ok(5)
+        }
+    }
+
+    let f = FiveStruct::from_args(&["cmdname"], &["--five", "woot"]).expect("failed to five");
+    assert_eq!(f.five, 5);
+}
+
+#[test]
 fn subcommand_example() {
     #[derive(FromArgs, PartialEq, Debug)]
     /// Top-level command.