aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-12-18 00:39:24 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-23 12:04:57 -0800
commit048c97adcedab552e8c5b33567a06de4cb5c0f81 (patch)
tree8271dde240632509260c074dd3ae9554b3dda442
parent32126d545532971302c0a8d512b5a8ec8226ed33 (diff)
downloadnitrocli-048c97adcedab552e8c5b33567a06de4cb5c0f81.tar.gz
nitrocli-048c97adcedab552e8c5b33567a06de4cb5c0f81.tar.bz2
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.
-rw-r--r--nitrocli/CHANGELOG.md3
-rw-r--r--nitrocli/src/args.rs153
-rw-r--r--nitrocli/src/error.rs2
-rw-r--r--nitrocli/src/main.rs47
4 files changed, 173 insertions, 32 deletions
diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md
index 1beabf2..2d43788 100644
--- a/nitrocli/CHANGELOG.md
+++ b/nitrocli/CHANGELOG.md
@@ -12,7 +12,8 @@ Unreleased
version of the crate, and minimum version of `rustc` required
- Fixed wrong messages in the pinentry dialog that were caused by unescaped
spaces in a string
-- Added `argparse` dependency in version `0.2.2`
+- Use the `argparse` crate to parse the command-line arguments
+ - Added `argparse` dependency in version `0.2.2`
0.1.3
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 <http://www.gnu.org/licenses/>. *
+// *************************************************************************
+
+use std::fmt;
+use std::io;
+use std::result;
+use std::str;
+
+use crate::commands;
+use crate::error::Error;
+
+type Result<T> = result::Result<T, Error>;
+
+/// 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<String>) -> 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<Self, Self::Err> {
+ 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<String>) -> 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<String>) -> 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<String>) -> 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<String>) -> 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<String>) -> 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<String>) -> Result<(Command, Vec<String>)> {
+ 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<String>) -> 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<string::FromUtf8Error> 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<T> = result::Result<T, Error>;
-// 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<String> = std::env::args().collect();
- if argv.len() != 2 {
- println!("Usage: {} <command>", 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() {