summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2019-01-14 16:34:27 -0800
committerDaniel Mueller <deso@posteo.net>2019-01-14 16:34:27 -0800
commit74b9b3845846e0fbc2efd37d80b5527be1bee564 (patch)
tree5dbc875a78aa7434025c5cbda259c25a99b12704
parent8e0badc9988775b1b0340db5b2cb975c3ecf407d (diff)
downloadnitrocli-74b9b3845846e0fbc2efd37d80b5527be1bee564.tar.gz
nitrocli-74b9b3845846e0fbc2efd37d80b5527be1bee564.tar.bz2
Auto-populate help text content
With the ability to fully generate the command enums we use for working with the argparse crate, we can now take things one step further and populate the contents of the help string we print for the user that lists the available commands. Doing so we also fix a bug where we forgot to mention the "storage status" command in the help text.
-rw-r--r--nitrocli/src/arg_util.rs58
-rw-r--r--nitrocli/src/args.rs65
2 files changed, 93 insertions, 30 deletions
diff --git a/nitrocli/src/arg_util.rs b/nitrocli/src/arg_util.rs
index b188085..b1ced25 100644
--- a/nitrocli/src/arg_util.rs
+++ b/nitrocli/src/arg_util.rs
@@ -17,6 +17,13 @@
// * along with this program. If not, see <http://www.gnu.org/licenses/>. *
// *************************************************************************
+macro_rules! count {
+ ($head:ident) => { 1 };
+ ($head:ident, $($tail:ident),*) => {
+ 1 + count!($($tail),*)
+ }
+}
+
/// A macro for generating an enum with a set of simple (i.e., no
/// parameters) variants and their textual representations.
// TODO: Right now we hard code the derives we create. We may want to
@@ -50,6 +57,21 @@ macro_rules! Enum {
)*
}
+ impl $name {
+ #[allow(unused)]
+ pub fn all(&self) -> [$name; count!($($var),*) ] {
+ $name::all_variants()
+ }
+
+ pub fn all_variants() -> [$name; count!($($var),*) ] {
+ [
+ $(
+ $name::$var,
+ )*
+ ]
+ }
+ }
+
impl AsRef<str> for $name {
fn as_ref(&self) -> &'static str {
match *self {
@@ -81,6 +103,34 @@ macro_rules! Enum {
};
}
+/// A macro for formatting the variants of an enum (as created by the
+/// Enum!{} macro) ready to be used in a help text. The supplied `fmt`
+/// needs to contain the named parameter `{variants}`, which will be
+/// replaced with a generated version of the enum's variants.
+macro_rules! fmt_enum {
+ ( $enm:ident ) => {{
+ $enm
+ .all()
+ .iter()
+ .map(::std::convert::AsRef::as_ref)
+ .collect::<::std::vec::Vec<_>>()
+ .join("|")
+ }};
+}
+
+/// A macro for generating the help text for a command/subcommand. The
+/// argument is the variable representing the command (which in turn is
+/// an enum).
+/// Note that the name of this variable is embedded into the help text!
+macro_rules! cmd_help {
+ ( $cmd:ident ) => {
+ format!(
+ concat!("The ", stringify!($cmd), " to execute ({})"),
+ fmt_enum!($cmd)
+ )
+ };
+}
+
#[cfg(test)]
mod tests {
Enum! {Command, [
@@ -90,6 +140,14 @@ mod tests {
]}
#[test]
+ fn all_variants() {
+ assert_eq!(
+ Command::all_variants(),
+ [Command::Var1, Command::Var2, Command::Var3]
+ )
+ }
+
+ #[test]
fn text_representations() {
assert_eq!(Command::Var1.as_ref(), "var1");
assert_eq!(Command::Var2.as_ref(), "2");
diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs
index 915186d..83a0999 100644
--- a/nitrocli/src/args.rs
+++ b/nitrocli/src/args.rs
@@ -200,14 +200,15 @@ Enum! {StorageCommand, [
/// Execute a storage subcommand.
fn storage(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
let mut subcommand = StorageCommand::Open;
+ let help = cmd_help!(subcommand);
let mut subargs = vec![];
let mut parser = argparse::ArgumentParser::new();
parser.set_description("Interacts with the device's storage");
- let _ = parser.refer(&mut subcommand).required().add_argument(
- "subcommand",
- argparse::Store,
- "The subcommand to execute (open|close)",
- );
+ let _ =
+ parser
+ .refer(&mut subcommand)
+ .required()
+ .add_argument("subcommand", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,
@@ -251,14 +252,15 @@ fn storage_status(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
/// Execute a config subcommand.
fn config(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
let mut subcommand = ConfigCommand::Get;
+ let help = cmd_help!(subcommand);
let mut subargs = vec![];
let mut parser = argparse::ArgumentParser::new();
parser.set_description("Reads or writes the device configuration");
- let _ = parser.refer(&mut subcommand).required().add_argument(
- "subcommand",
- argparse::Store,
- "The subcommand to execute (get|set)",
- );
+ let _ =
+ parser
+ .refer(&mut subcommand)
+ .required()
+ .add_argument("subcommand", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,
@@ -361,14 +363,15 @@ fn lock(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
/// Execute an OTP subcommand.
fn otp(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
let mut subcommand = OtpCommand::Get;
+ let help = cmd_help!(subcommand);
let mut subargs = vec![];
let mut parser = argparse::ArgumentParser::new();
parser.set_description("Accesses one-time passwords");
- let _ = parser.refer(&mut subcommand).required().add_argument(
- "subcommand",
- argparse::Store,
- "The subcommand to execute (clear|get|set|status)",
- );
+ let _ =
+ parser
+ .refer(&mut subcommand)
+ .required()
+ .add_argument("subcommand", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,
@@ -538,14 +541,15 @@ fn otp_status(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
/// Execute a PIN subcommand.
fn pin(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
let mut subcommand = PinCommand::Clear;
+ let help = cmd_help!(subcommand);
let mut subargs = vec![];
let mut parser = argparse::ArgumentParser::new();
parser.set_description("Manages the Nitrokey PINs");
- let _ = parser.refer(&mut subcommand).required().add_argument(
- "subcommand",
- argparse::Store,
- "The subcommand to execute (clear|set|unblock)",
- );
+ let _ =
+ parser
+ .refer(&mut subcommand)
+ .required()
+ .add_argument("subcommand", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,
@@ -597,13 +601,14 @@ fn pin_unblock(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
fn pws(ctx: &mut ExecCtx<'_>, args: Vec<String>) -> Result<()> {
let mut subcommand = PwsCommand::Get;
let mut subargs = vec![];
+ let help = cmd_help!(subcommand);
let mut parser = argparse::ArgumentParser::new();
parser.set_description("Accesses the password safe");
- let _ = parser.refer(&mut subcommand).required().add_argument(
- "subcommand",
- argparse::Store,
- "The subcommand to execute (clear|get|set|status)",
- );
+ let _ =
+ parser
+ .refer(&mut subcommand)
+ .required()
+ .add_argument("subcommand", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,
@@ -732,6 +737,7 @@ fn parse_arguments<'io, 'ctx: 'io>(
let mut model: Option<DeviceModel> = None;
let mut verbosity = 0;
let mut command = Command::Status;
+ let help = cmd_help!(command);
let mut subargs = vec![];
let mut parser = argparse::ArgumentParser::new();
let _ = parser.refer(&mut verbosity).add_option(
@@ -745,11 +751,10 @@ fn parse_arguments<'io, 'ctx: 'io>(
"Select the device model to connect to (pro|storage)",
);
parser.set_description("Provides access to a Nitrokey device");
- let _ = parser.refer(&mut command).required().add_argument(
- "command",
- argparse::Store,
- "The command to execute (config|lock|otp|pin|pws|status|storage)",
- );
+ let _ = parser
+ .refer(&mut command)
+ .required()
+ .add_argument("command", argparse::Store, &help);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
argparse::List,