From 048c97adcedab552e8c5b33567a06de4cb5c0f81 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 18 Dec 2018 00:39:24 +0100 Subject: Port argument handling to argparse This patch replaces the macro for argument parsing with `argparse::ArgumentParser` from the argparse crate. It moves the application logic to the `commands` module and the argument parsing to the `options` module. An enum is used to represent the available commands. The code is based on the `subcommands.rs` example shipped with argparse. --- nitrocli/src/args.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++ nitrocli/src/error.rs | 2 + nitrocli/src/main.rs | 47 ++++++---------- 3 files changed, 171 insertions(+), 31 deletions(-) create mode 100644 nitrocli/src/args.rs (limited to 'nitrocli/src') diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs new file mode 100644 index 0000000..07a3e6a --- /dev/null +++ b/nitrocli/src/args.rs @@ -0,0 +1,153 @@ +// args.rs + +// ************************************************************************* +// * Copyright (C) 2018 Daniel Mueller (deso@posteo.net) * +// * * +// * This program is free software: you can redistribute it and/or modify * +// * it under the terms of the GNU General Public License as published by * +// * the Free Software Foundation, either version 3 of the License, or * +// * (at your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, * +// * but WITHOUT ANY WARRANTY; without even the implied warranty of * +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +// * GNU General Public License for more details. * +// * * +// * You should have received a copy of the GNU General Public License * +// * along with this program. If not, see . * +// ************************************************************************* + +use std::fmt; +use std::io; +use std::result; +use std::str; + +use crate::commands; +use crate::error::Error; + +type Result = result::Result; + +/// A top-level command for nitrocli. +#[derive(Debug)] +pub enum Command { + Clear, + Close, + Open, + Status, +} + +impl Command { + /// Execute this command with the given arguments. + pub fn execute(&self, args: Vec) -> Result<()> { + match *self { + Command::Clear => clear(args), + Command::Close => close(args), + Command::Open => open(args), + Command::Status => status(args), + } + } +} + +impl fmt::Display for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match *self { + Command::Clear => "clear", + Command::Close => "close", + Command::Open => "open", + Command::Status => "status", + } + ) + } +} + +impl str::FromStr for Command { + type Err = (); + + fn from_str(s: &str) -> result::Result { + match s { + "clear" => Ok(Command::Clear), + "close" => Ok(Command::Close), + "open" => Ok(Command::Open), + "status" => Ok(Command::Status), + _ => Err(()), + } + } +} + +fn parse(parser: &argparse::ArgumentParser<'_>, args: Vec) -> Result<()> { + if let Err(err) = parser.parse(args, &mut io::stdout(), &mut io::stderr()) { + Err(Error::ArgparseError(err)) + } else { + Ok(()) + } +} + +/// Inquire the status of the nitrokey. +fn status(args: Vec) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Print the status of the connected Nitrokey Storage"); + parse(&parser, args)?; + + commands::status() +} + +/// Open the encrypted volume on the nitrokey. +fn open(args: Vec) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Opens the encrypted volume on a Nitrokey Storage"); + parse(&parser, args)?; + + commands::open() +} + +/// Close the previously opened encrypted volume. +fn close(args: Vec) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Closes the encrypted volume on a Nitrokey Storage"); + parse(&parser, args)?; + + commands::close() +} + +/// Clear the PIN stored when opening the nitrokey's encrypted volume. +fn clear(args: Vec) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Clears the cached passphrase"); + parse(&parser, args)?; + + commands::clear() +} + +/// Parse the command-line arguments and return the selected command and +/// the remaining arguments for the command. +fn parse_arguments(args: Vec) -> Result<(Command, Vec)> { + let mut command = Command::Status; + let mut subargs = vec![]; + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Provides access to a Nitrokey device"); + let _ = parser.refer(&mut command).required().add_argument( + "command", + argparse::Store, + "The command to execute (clear|close|open|status)", + ); + let _ = parser.refer(&mut subargs).add_argument( + "arguments", + argparse::List, + "The arguments for the command", + ); + parser.stop_on_first_argument(true); + parse(&parser, args)?; + drop(parser); + + subargs.insert(0, format!("nitrocli {}", command)); + Ok((command, subargs)) +} + +/// Parse the command-line arguments and execute the selected command. +pub fn handle_arguments(args: Vec) -> Result<()> { + let (command, args) = parse_arguments(args)?; + command.execute(args) +} diff --git a/nitrocli/src/error.rs b/nitrocli/src/error.rs index d755cac..d86a635 100644 --- a/nitrocli/src/error.rs +++ b/nitrocli/src/error.rs @@ -23,6 +23,7 @@ use std::string; #[derive(Debug)] pub enum Error { + ArgparseError(i32), IoError(io::Error), Utf8Error(string::FromUtf8Error), Error(String), @@ -43,6 +44,7 @@ impl From for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { + Error::ArgparseError(_) => write!(f, "Could not parse arguments"), Error::Utf8Error(_) => write!(f, "Encountered UTF-8 conversion error"), Error::IoError(ref e) => write!(f, "IO error: {}", e.get_ref().unwrap()), Error::Error(ref e) => write!(f, "{}", e), diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index 86e9188..8a20494 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -68,6 +68,7 @@ //! Nitrocli is a program providing a command line interface to certain //! commands of the Nitrokey Storage device. +mod args; mod commands; mod error; mod pinentry; @@ -75,43 +76,27 @@ mod pinentry; use std::process; use std::result; -use nitrokey; - use crate::error::Error; type Result = result::Result; -// A macro for generating a match of the different supported commands. -// Each supplied command is converted into a string and matched against. -macro_rules! commands { - ( $str:expr, [ $( $command:expr), *] ) => { - match &*$str.to_string() { - $( - stringify!($command) => { - if let Err(err) = $command() { - println!("{}", err); - return 1 - } - return 0 - }, - )* - x => { - println!("Invalid command: {}", x); - println!("Available commands: {}", stringify!( $($command)* )); - return 1 - }, - } - } -} - fn run() -> i32 { - let argv: Vec = std::env::args().collect(); - if argv.len() != 2 { - println!("Usage: {} ", argv[0]); - return 1; + let args = std::env::args().collect(); + match args::handle_arguments(args) { + Ok(()) => 0, + Err(err) => match err { + Error::ArgparseError(err) => match err { + // argparse printed the help message + 0 => 0, + // argparse printed an error message + _ => 1, + }, + _ => { + println!("{}", err); + 1 + } + }, } - - commands!(&argv[1], [commands::status, commands::open, commands::close, commands::clear]); } fn main() { -- cgit v1.2.1