diff options
| -rw-r--r-- | nitrocli/src/arg_util.rs | 58 | ||||
| -rw-r--r-- | nitrocli/src/args.rs | 65 | 
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, | 
