From f2ace56200f0229a4dcbd1767f48a39e1daa343d Mon Sep 17 00:00:00 2001
From: Daniel Mueller <deso@posteo.net>
Date: Sat, 11 Apr 2020 15:00:06 -0700
Subject: Rename arg_defs.rs to args.rs

We have never been fully satisfied with the name arg_defs. Now that we
have gotten rid of the formerly used args module, this change renames
arg_defs to args.
---
 src/arg_defs.rs       | 433 --------------------------------------------------
 src/args.rs           | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/commands.rs       |  95 ++++++-----
 src/main.rs           |   6 +-
 src/pinentry.rs       |  24 +--
 src/tests/otp.rs      |   8 +-
 var/shell-complete.rs |   2 +-
 7 files changed, 499 insertions(+), 502 deletions(-)
 delete mode 100644 src/arg_defs.rs
 create mode 100644 src/args.rs

diff --git a/src/arg_defs.rs b/src/arg_defs.rs
deleted file mode 100644
index 133ef19..0000000
--- a/src/arg_defs.rs
+++ /dev/null
@@ -1,433 +0,0 @@
-// arg_defs.rs
-
-// *************************************************************************
-// * Copyright (C) 2020 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/>. *
-// *************************************************************************
-
-/// Provides access to a Nitrokey device
-#[derive(structopt::StructOpt)]
-#[structopt(name = "nitrocli")]
-pub struct Args {
-  /// Increases the log level (can be supplied multiple times)
-  #[structopt(short, long, global = true, parse(from_occurrences))]
-  pub verbose: u8,
-  /// Selects the device model to connect to
-  #[structopt(short, long, global = true, possible_values = &DeviceModel::all_str())]
-  pub model: Option<DeviceModel>,
-  #[structopt(subcommand)]
-  pub cmd: Command,
-}
-
-/// The available Nitrokey models.
-#[allow(unused_doc_comments)]
-Enum! {DeviceModel, [
-  Pro => "pro",
-  Storage => "storage",
-]}
-
-impl DeviceModel {
-  pub fn as_user_facing_str(&self) -> &str {
-    match self {
-      DeviceModel::Pro => "Pro",
-      DeviceModel::Storage => "Storage",
-    }
-  }
-}
-
-impl From<DeviceModel> for nitrokey::Model {
-  fn from(model: DeviceModel) -> nitrokey::Model {
-    match model {
-      DeviceModel::Pro => nitrokey::Model::Pro,
-      DeviceModel::Storage => nitrokey::Model::Storage,
-    }
-  }
-}
-
-/// A top-level command for nitrocli.
-#[allow(unused_doc_comments)]
-Command! {Command, [
-  /// Reads or writes the device configuration
-  Config(ConfigArgs) => |ctx, args: ConfigArgs| args.subcmd.execute(ctx),
-  /// Interacts with the device's encrypted volume
-  Encrypted(EncryptedArgs) => |ctx, args: EncryptedArgs| args.subcmd.execute(ctx),
-  /// Interacts with the device's hidden volume
-  Hidden(HiddenArgs) => |ctx, args: HiddenArgs| args.subcmd.execute(ctx),
-  /// Lists the attached Nitrokey devices
-  List(ListArgs) => |ctx, args: ListArgs| crate::commands::list(ctx, args.no_connect),
-  /// Locks the connected Nitrokey device
-  Lock => crate::commands::lock,
-  /// Accesses one-time passwords
-  Otp(OtpArgs) => |ctx, args: OtpArgs| args.subcmd.execute(ctx),
-  /// Manages the Nitrokey PINs
-  Pin(PinArgs) => |ctx, args: PinArgs| args.subcmd.execute(ctx),
-  /// Accesses the password safe
-  Pws(PwsArgs) => |ctx, args: PwsArgs| args.subcmd.execute(ctx),
-  /// Performs a factory reset
-  Reset => crate::commands::reset,
-  /// Prints the status of the connected Nitrokey device
-  Status => crate::commands::status,
-  /// Interacts with the device's unencrypted volume
-  Unencrypted(UnencryptedArgs) => |ctx, args: UnencryptedArgs| args.subcmd.execute(ctx),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct ConfigArgs {
-  #[structopt(subcommand)]
-  subcmd: ConfigCommand,
-}
-
-Command! {ConfigCommand, [
-  /// Prints the Nitrokey configuration
-  Get => crate::commands::config_get,
-  /// Changes the Nitrokey configuration
-  Set(ConfigSetArgs) => crate::commands::config_set,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct ConfigSetArgs {
-  /// Sets the numlock option to the given HOTP slot
-  #[structopt(short = "n", long)]
-  pub numlock: Option<u8>,
-  /// Unsets the numlock option
-  #[structopt(short = "N", long, conflicts_with("numlock"))]
-  pub no_numlock: bool,
-  /// Sets the capslock option to the given HOTP slot
-  #[structopt(short = "c", long)]
-  pub capslock: Option<u8>,
-  /// Unsets the capslock option
-  #[structopt(short = "C", long, conflicts_with("capslock"))]
-  pub no_capslock: bool,
-  /// Sets the scrollock option to the given HOTP slot
-  #[structopt(short = "s", long)]
-  pub scrollock: Option<u8>,
-  /// Unsets the scrollock option
-  #[structopt(short = "S", long, conflicts_with("scrollock"))]
-  pub no_scrollock: bool,
-  /// Requires the user PIN to generate one-time passwords
-  #[structopt(short = "o", long)]
-  pub otp_pin: bool,
-  /// Allows one-time password generation without PIN
-  #[structopt(short = "O", long, conflicts_with("otp-pin"))]
-  pub no_otp_pin: bool,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum ConfigOption<T> {
-  Enable(T),
-  Disable,
-  Ignore,
-}
-
-impl<T> ConfigOption<T> {
-  pub fn try_from(disable: bool, value: Option<T>, name: &'static str) -> Result<Self, String> {
-    if disable {
-      if value.is_some() {
-        Err(format!(
-          "--{name} and --no-{name} are mutually exclusive",
-          name = name
-        ))
-      } else {
-        Ok(ConfigOption::Disable)
-      }
-    } else {
-      match value {
-        Some(value) => Ok(ConfigOption::Enable(value)),
-        None => Ok(ConfigOption::Ignore),
-      }
-    }
-  }
-
-  pub fn or(self, default: Option<T>) -> Option<T> {
-    match self {
-      ConfigOption::Enable(value) => Some(value),
-      ConfigOption::Disable => None,
-      ConfigOption::Ignore => default,
-    }
-  }
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct EncryptedArgs {
-  #[structopt(subcommand)]
-  subcmd: EncryptedCommand,
-}
-
-Command! {EncryptedCommand, [
-  /// Closes the encrypted volume on a Nitrokey Storage
-  Close => crate::commands::encrypted_close,
-  /// Opens the encrypted volume on a Nitrokey Storage
-  Open => crate::commands::encrypted_open,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct HiddenArgs {
-  #[structopt(subcommand)]
-  subcmd: HiddenCommand,
-}
-
-Command! {HiddenCommand, [
-  /// Closes the hidden volume on a Nitrokey Storage
-  Close => crate::commands::hidden_close,
-  /// Creates a hidden volume on a Nitrokey Storage
-  Create(HiddenCreateArgs) => |ctx, args: HiddenCreateArgs| {
-    crate::commands::hidden_create(ctx, args.slot, args.start, args.end)
-  },
-  /// Opens the hidden volume on a Nitrokey Storage
-  Open => crate::commands::hidden_open,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct HiddenCreateArgs {
-  /// The hidden volume slot to use
-  pub slot: u8,
-  /// The start location of the hidden volume as a percentage of the encrypted volume's size (0-99)
-  pub start: u8,
-  /// The end location of the hidden volume as a percentage of the encrypted volume's size (1-100)
-  pub end: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct ListArgs {
-  /// Only print the information that is available without connecting to a device
-  #[structopt(short, long)]
-  pub no_connect: bool,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct OtpArgs {
-  #[structopt(subcommand)]
-  subcmd: OtpCommand,
-}
-
-Command! {OtpCommand, [
-  /// Clears a one-time password slot
-  Clear(OtpClearArgs) => |ctx, args: OtpClearArgs| {
-    crate::commands::otp_clear(ctx, args.slot, args.algorithm)
-  },
-  /// Generates a one-time password
-  Get(OtpGetArgs) => |ctx, args: OtpGetArgs| {
-    crate::commands::otp_get(ctx, args.slot, args.algorithm, args.time)
-  },
-  /// Configures a one-time password slot
-  Set(OtpSetArgs) => crate::commands::otp_set,
-  /// Prints the status of the one-time password slots
-  Status(OtpStatusArgs) => |ctx, args: OtpStatusArgs| crate::commands::otp_status(ctx, args.all),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct OtpClearArgs {
-  /// The OTP algorithm to use
-  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
-              possible_values = &OtpAlgorithm::all_str())]
-  pub algorithm: OtpAlgorithm,
-  /// The OTP slot to clear
-  pub slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct OtpGetArgs {
-  /// The OTP algorithm to use
-  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
-              possible_values = &OtpAlgorithm::all_str())]
-  pub algorithm: OtpAlgorithm,
-  /// The time to use for TOTP generation (Unix timestamp) [default: system time]
-  #[structopt(short, long)]
-  pub time: Option<u64>,
-  /// The OTP slot to use
-  pub slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct OtpSetArgs {
-  /// The OTP algorithm to use
-  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
-              possible_values = &OtpAlgorithm::all_str())]
-  pub algorithm: OtpAlgorithm,
-  /// The number of digits to use for the one-time password
-  #[structopt(short, long, default_value = OtpMode::SixDigits.as_ref(),
-              possible_values = &OtpMode::all_str())]
-  pub digits: OtpMode,
-  /// The counter value for HOTP
-  #[structopt(short, long, default_value = "0")]
-  pub counter: u64,
-  /// The time window for TOTP
-  #[structopt(short, long, default_value = "30")]
-  pub time_window: u16,
-  /// The format of the secret
-  #[structopt(short, long, default_value = OtpSecretFormat::Hex.as_ref(),
-              possible_values = &OtpSecretFormat::all_str())]
-  pub format: OtpSecretFormat,
-  /// The OTP slot to use
-  pub slot: u8,
-  /// The name of the slot
-  pub name: String,
-  /// The secret to store on the slot as a hexadecimal string (or in the format set with the
-  /// --format option)
-  pub secret: String,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct OtpStatusArgs {
-  /// Shows slots that are not programmed
-  #[structopt(short, long)]
-  pub all: bool,
-}
-
-Enum! {OtpAlgorithm, [
-  Hotp => "hotp",
-  Totp => "totp",
-]}
-
-Enum! {OtpMode, [
-  SixDigits => "6",
-  EightDigits => "8",
-]}
-
-impl From<OtpMode> for nitrokey::OtpMode {
-  fn from(mode: OtpMode) -> Self {
-    match mode {
-      OtpMode::SixDigits => nitrokey::OtpMode::SixDigits,
-      OtpMode::EightDigits => nitrokey::OtpMode::EightDigits,
-    }
-  }
-}
-
-Enum! {OtpSecretFormat, [
-  Ascii => "ascii",
-  Base32 => "base32",
-  Hex => "hex",
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PinArgs {
-  #[structopt(subcommand)]
-  subcmd: PinCommand,
-}
-
-Command! {PinCommand, [
-  /// Clears the cached PINs
-  Clear => crate::commands::pin_clear,
-  /// Changes a PIN
-  Set(PinSetArgs) => |ctx, args: PinSetArgs| crate::commands::pin_set(ctx, args.pintype),
-  /// Unblocks and resets the user PIN
-  Unblock => crate::commands::pin_unblock,
-]}
-
-/// PIN type requested from pinentry.
-///
-/// The available PIN types correspond to the PIN types used by the Nitrokey devices:  user and
-/// admin.
-#[allow(unused_doc_comments)]
-Enum! {PinType, [
-  Admin => "admin",
-  User => "user",
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PinSetArgs {
-  /// The PIN type to change
-  #[structopt(name = "type", possible_values = &PinType::all_str())]
-  pub pintype: PinType,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PwsArgs {
-  #[structopt(subcommand)]
-  subcmd: PwsCommand,
-}
-
-Command! {PwsCommand, [
-  /// Clears a password safe slot
-  Clear(PwsClearArgs) => |ctx, args: PwsClearArgs| crate::commands::pws_clear(ctx, args.slot),
-  /// Reads a password safe slot
-  Get(PwsGetArgs) => |ctx, args: PwsGetArgs| {
-    crate::commands::pws_get(ctx, args.slot, args.name, args.login, args.password, args.quiet)
-  },
-  /// Writes a password safe slot
-  Set(PwsSetArgs) => |ctx, args: PwsSetArgs| {
-    crate::commands::pws_set(ctx, args.slot, &args.name, &args.login, &args.password)
-  },
-  /// Prints the status of the password safe slots
-  Status(PwsStatusArgs) => |ctx, args: PwsStatusArgs| crate::commands::pws_status(ctx, args.all),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PwsClearArgs {
-  /// The PWS slot to clear
-  pub slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PwsGetArgs {
-  /// Shows the name stored on the slot
-  #[structopt(short, long)]
-  pub name: bool,
-  /// Shows the login stored on the slot
-  #[structopt(short, long)]
-  pub login: bool,
-  /// Shows the password stored on the slot
-  #[structopt(short, long)]
-  pub password: bool,
-  /// Prints the stored data without description
-  #[structopt(short, long)]
-  pub quiet: bool,
-  /// The PWS slot to read
-  pub slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PwsSetArgs {
-  /// The PWS slot to write
-  pub slot: u8,
-  /// The name to store on the slot
-  pub name: String,
-  /// The login to store on the slot
-  pub login: String,
-  /// The password to store on the slot
-  pub password: String,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct PwsStatusArgs {
-  /// Shows slots that are not programmed
-  #[structopt(short, long)]
-  pub all: bool,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct UnencryptedArgs {
-  #[structopt(subcommand)]
-  subcmd: UnencryptedCommand,
-}
-
-Command! {UnencryptedCommand, [
-  /// Changes the configuration of the unencrypted volume on a Nitrokey Storage
-  Set(UnencryptedSetArgs) => |ctx, args: UnencryptedSetArgs| {
-    crate::commands::unencrypted_set(ctx, args.mode)
-  },
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-pub struct UnencryptedSetArgs {
-  /// The mode to change to
-  #[structopt(name = "type", possible_values = &UnencryptedVolumeMode::all_str())]
-  pub mode: UnencryptedVolumeMode,
-}
-
-Enum! {UnencryptedVolumeMode, [
-  ReadWrite => "read-write",
-  ReadOnly => "read-only",
-]}
diff --git a/src/args.rs b/src/args.rs
new file mode 100644
index 0000000..20392a8
--- /dev/null
+++ b/src/args.rs
@@ -0,0 +1,433 @@
+// args.rs
+
+// *************************************************************************
+// * Copyright (C) 2020 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/>. *
+// *************************************************************************
+
+/// Provides access to a Nitrokey device
+#[derive(structopt::StructOpt)]
+#[structopt(name = "nitrocli")]
+pub struct Args {
+  /// Increases the log level (can be supplied multiple times)
+  #[structopt(short, long, global = true, parse(from_occurrences))]
+  pub verbose: u8,
+  /// Selects the device model to connect to
+  #[structopt(short, long, global = true, possible_values = &DeviceModel::all_str())]
+  pub model: Option<DeviceModel>,
+  #[structopt(subcommand)]
+  pub cmd: Command,
+}
+
+/// The available Nitrokey models.
+#[allow(unused_doc_comments)]
+Enum! {DeviceModel, [
+  Pro => "pro",
+  Storage => "storage",
+]}
+
+impl DeviceModel {
+  pub fn as_user_facing_str(&self) -> &str {
+    match self {
+      DeviceModel::Pro => "Pro",
+      DeviceModel::Storage => "Storage",
+    }
+  }
+}
+
+impl From<DeviceModel> for nitrokey::Model {
+  fn from(model: DeviceModel) -> nitrokey::Model {
+    match model {
+      DeviceModel::Pro => nitrokey::Model::Pro,
+      DeviceModel::Storage => nitrokey::Model::Storage,
+    }
+  }
+}
+
+/// A top-level command for nitrocli.
+#[allow(unused_doc_comments)]
+Command! {Command, [
+  /// Reads or writes the device configuration
+  Config(ConfigArgs) => |ctx, args: ConfigArgs| args.subcmd.execute(ctx),
+  /// Interacts with the device's encrypted volume
+  Encrypted(EncryptedArgs) => |ctx, args: EncryptedArgs| args.subcmd.execute(ctx),
+  /// Interacts with the device's hidden volume
+  Hidden(HiddenArgs) => |ctx, args: HiddenArgs| args.subcmd.execute(ctx),
+  /// Lists the attached Nitrokey devices
+  List(ListArgs) => |ctx, args: ListArgs| crate::commands::list(ctx, args.no_connect),
+  /// Locks the connected Nitrokey device
+  Lock => crate::commands::lock,
+  /// Accesses one-time passwords
+  Otp(OtpArgs) => |ctx, args: OtpArgs| args.subcmd.execute(ctx),
+  /// Manages the Nitrokey PINs
+  Pin(PinArgs) => |ctx, args: PinArgs| args.subcmd.execute(ctx),
+  /// Accesses the password safe
+  Pws(PwsArgs) => |ctx, args: PwsArgs| args.subcmd.execute(ctx),
+  /// Performs a factory reset
+  Reset => crate::commands::reset,
+  /// Prints the status of the connected Nitrokey device
+  Status => crate::commands::status,
+  /// Interacts with the device's unencrypted volume
+  Unencrypted(UnencryptedArgs) => |ctx, args: UnencryptedArgs| args.subcmd.execute(ctx),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ConfigArgs {
+  #[structopt(subcommand)]
+  subcmd: ConfigCommand,
+}
+
+Command! {ConfigCommand, [
+  /// Prints the Nitrokey configuration
+  Get => crate::commands::config_get,
+  /// Changes the Nitrokey configuration
+  Set(ConfigSetArgs) => crate::commands::config_set,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ConfigSetArgs {
+  /// Sets the numlock option to the given HOTP slot
+  #[structopt(short = "n", long)]
+  pub numlock: Option<u8>,
+  /// Unsets the numlock option
+  #[structopt(short = "N", long, conflicts_with("numlock"))]
+  pub no_numlock: bool,
+  /// Sets the capslock option to the given HOTP slot
+  #[structopt(short = "c", long)]
+  pub capslock: Option<u8>,
+  /// Unsets the capslock option
+  #[structopt(short = "C", long, conflicts_with("capslock"))]
+  pub no_capslock: bool,
+  /// Sets the scrollock option to the given HOTP slot
+  #[structopt(short = "s", long)]
+  pub scrollock: Option<u8>,
+  /// Unsets the scrollock option
+  #[structopt(short = "S", long, conflicts_with("scrollock"))]
+  pub no_scrollock: bool,
+  /// Requires the user PIN to generate one-time passwords
+  #[structopt(short = "o", long)]
+  pub otp_pin: bool,
+  /// Allows one-time password generation without PIN
+  #[structopt(short = "O", long, conflicts_with("otp-pin"))]
+  pub no_otp_pin: bool,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ConfigOption<T> {
+  Enable(T),
+  Disable,
+  Ignore,
+}
+
+impl<T> ConfigOption<T> {
+  pub fn try_from(disable: bool, value: Option<T>, name: &'static str) -> Result<Self, String> {
+    if disable {
+      if value.is_some() {
+        Err(format!(
+          "--{name} and --no-{name} are mutually exclusive",
+          name = name
+        ))
+      } else {
+        Ok(ConfigOption::Disable)
+      }
+    } else {
+      match value {
+        Some(value) => Ok(ConfigOption::Enable(value)),
+        None => Ok(ConfigOption::Ignore),
+      }
+    }
+  }
+
+  pub fn or(self, default: Option<T>) -> Option<T> {
+    match self {
+      ConfigOption::Enable(value) => Some(value),
+      ConfigOption::Disable => None,
+      ConfigOption::Ignore => default,
+    }
+  }
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct EncryptedArgs {
+  #[structopt(subcommand)]
+  subcmd: EncryptedCommand,
+}
+
+Command! {EncryptedCommand, [
+  /// Closes the encrypted volume on a Nitrokey Storage
+  Close => crate::commands::encrypted_close,
+  /// Opens the encrypted volume on a Nitrokey Storage
+  Open => crate::commands::encrypted_open,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct HiddenArgs {
+  #[structopt(subcommand)]
+  subcmd: HiddenCommand,
+}
+
+Command! {HiddenCommand, [
+  /// Closes the hidden volume on a Nitrokey Storage
+  Close => crate::commands::hidden_close,
+  /// Creates a hidden volume on a Nitrokey Storage
+  Create(HiddenCreateArgs) => |ctx, args: HiddenCreateArgs| {
+    crate::commands::hidden_create(ctx, args.slot, args.start, args.end)
+  },
+  /// Opens the hidden volume on a Nitrokey Storage
+  Open => crate::commands::hidden_open,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct HiddenCreateArgs {
+  /// The hidden volume slot to use
+  pub slot: u8,
+  /// The start location of the hidden volume as a percentage of the encrypted volume's size (0-99)
+  pub start: u8,
+  /// The end location of the hidden volume as a percentage of the encrypted volume's size (1-100)
+  pub end: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ListArgs {
+  /// Only print the information that is available without connecting to a device
+  #[structopt(short, long)]
+  pub no_connect: bool,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpArgs {
+  #[structopt(subcommand)]
+  subcmd: OtpCommand,
+}
+
+Command! {OtpCommand, [
+  /// Clears a one-time password slot
+  Clear(OtpClearArgs) => |ctx, args: OtpClearArgs| {
+    crate::commands::otp_clear(ctx, args.slot, args.algorithm)
+  },
+  /// Generates a one-time password
+  Get(OtpGetArgs) => |ctx, args: OtpGetArgs| {
+    crate::commands::otp_get(ctx, args.slot, args.algorithm, args.time)
+  },
+  /// Configures a one-time password slot
+  Set(OtpSetArgs) => crate::commands::otp_set,
+  /// Prints the status of the one-time password slots
+  Status(OtpStatusArgs) => |ctx, args: OtpStatusArgs| crate::commands::otp_status(ctx, args.all),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpClearArgs {
+  /// The OTP algorithm to use
+  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+              possible_values = &OtpAlgorithm::all_str())]
+  pub algorithm: OtpAlgorithm,
+  /// The OTP slot to clear
+  pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpGetArgs {
+  /// The OTP algorithm to use
+  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+              possible_values = &OtpAlgorithm::all_str())]
+  pub algorithm: OtpAlgorithm,
+  /// The time to use for TOTP generation (Unix timestamp) [default: system time]
+  #[structopt(short, long)]
+  pub time: Option<u64>,
+  /// The OTP slot to use
+  pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpSetArgs {
+  /// The OTP algorithm to use
+  #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+              possible_values = &OtpAlgorithm::all_str())]
+  pub algorithm: OtpAlgorithm,
+  /// The number of digits to use for the one-time password
+  #[structopt(short, long, default_value = OtpMode::SixDigits.as_ref(),
+              possible_values = &OtpMode::all_str())]
+  pub digits: OtpMode,
+  /// The counter value for HOTP
+  #[structopt(short, long, default_value = "0")]
+  pub counter: u64,
+  /// The time window for TOTP
+  #[structopt(short, long, default_value = "30")]
+  pub time_window: u16,
+  /// The format of the secret
+  #[structopt(short, long, default_value = OtpSecretFormat::Hex.as_ref(),
+              possible_values = &OtpSecretFormat::all_str())]
+  pub format: OtpSecretFormat,
+  /// The OTP slot to use
+  pub slot: u8,
+  /// The name of the slot
+  pub name: String,
+  /// The secret to store on the slot as a hexadecimal string (or in the format set with the
+  /// --format option)
+  pub secret: String,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpStatusArgs {
+  /// Shows slots that are not programmed
+  #[structopt(short, long)]
+  pub all: bool,
+}
+
+Enum! {OtpAlgorithm, [
+  Hotp => "hotp",
+  Totp => "totp",
+]}
+
+Enum! {OtpMode, [
+  SixDigits => "6",
+  EightDigits => "8",
+]}
+
+impl From<OtpMode> for nitrokey::OtpMode {
+  fn from(mode: OtpMode) -> Self {
+    match mode {
+      OtpMode::SixDigits => nitrokey::OtpMode::SixDigits,
+      OtpMode::EightDigits => nitrokey::OtpMode::EightDigits,
+    }
+  }
+}
+
+Enum! {OtpSecretFormat, [
+  Ascii => "ascii",
+  Base32 => "base32",
+  Hex => "hex",
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PinArgs {
+  #[structopt(subcommand)]
+  subcmd: PinCommand,
+}
+
+Command! {PinCommand, [
+  /// Clears the cached PINs
+  Clear => crate::commands::pin_clear,
+  /// Changes a PIN
+  Set(PinSetArgs) => |ctx, args: PinSetArgs| crate::commands::pin_set(ctx, args.pintype),
+  /// Unblocks and resets the user PIN
+  Unblock => crate::commands::pin_unblock,
+]}
+
+/// PIN type requested from pinentry.
+///
+/// The available PIN types correspond to the PIN types used by the Nitrokey devices:  user and
+/// admin.
+#[allow(unused_doc_comments)]
+Enum! {PinType, [
+  Admin => "admin",
+  User => "user",
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PinSetArgs {
+  /// The PIN type to change
+  #[structopt(name = "type", possible_values = &PinType::all_str())]
+  pub pintype: PinType,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsArgs {
+  #[structopt(subcommand)]
+  subcmd: PwsCommand,
+}
+
+Command! {PwsCommand, [
+  /// Clears a password safe slot
+  Clear(PwsClearArgs) => |ctx, args: PwsClearArgs| crate::commands::pws_clear(ctx, args.slot),
+  /// Reads a password safe slot
+  Get(PwsGetArgs) => |ctx, args: PwsGetArgs| {
+    crate::commands::pws_get(ctx, args.slot, args.name, args.login, args.password, args.quiet)
+  },
+  /// Writes a password safe slot
+  Set(PwsSetArgs) => |ctx, args: PwsSetArgs| {
+    crate::commands::pws_set(ctx, args.slot, &args.name, &args.login, &args.password)
+  },
+  /// Prints the status of the password safe slots
+  Status(PwsStatusArgs) => |ctx, args: PwsStatusArgs| crate::commands::pws_status(ctx, args.all),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsClearArgs {
+  /// The PWS slot to clear
+  pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsGetArgs {
+  /// Shows the name stored on the slot
+  #[structopt(short, long)]
+  pub name: bool,
+  /// Shows the login stored on the slot
+  #[structopt(short, long)]
+  pub login: bool,
+  /// Shows the password stored on the slot
+  #[structopt(short, long)]
+  pub password: bool,
+  /// Prints the stored data without description
+  #[structopt(short, long)]
+  pub quiet: bool,
+  /// The PWS slot to read
+  pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsSetArgs {
+  /// The PWS slot to write
+  pub slot: u8,
+  /// The name to store on the slot
+  pub name: String,
+  /// The login to store on the slot
+  pub login: String,
+  /// The password to store on the slot
+  pub password: String,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsStatusArgs {
+  /// Shows slots that are not programmed
+  #[structopt(short, long)]
+  pub all: bool,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct UnencryptedArgs {
+  #[structopt(subcommand)]
+  subcmd: UnencryptedCommand,
+}
+
+Command! {UnencryptedCommand, [
+  /// Changes the configuration of the unencrypted volume on a Nitrokey Storage
+  Set(UnencryptedSetArgs) => |ctx, args: UnencryptedSetArgs| {
+    crate::commands::unencrypted_set(ctx, args.mode)
+  },
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct UnencryptedSetArgs {
+  /// The mode to change to
+  #[structopt(name = "type", possible_values = &UnencryptedVolumeMode::all_str())]
+  pub mode: UnencryptedVolumeMode,
+}
+
+Enum! {UnencryptedVolumeMode, [
+  ReadWrite => "read-write",
+  ReadOnly => "read-only",
+]}
diff --git a/src/commands.rs b/src/commands.rs
index 208c039..a2b6004 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -31,7 +31,7 @@ use nitrokey::Device;
 use nitrokey::GenerateOtp;
 use nitrokey::GetPasswordSafe;
 
-use crate::arg_defs;
+use crate::args;
 use crate::error;
 use crate::error::Error;
 use crate::pinentry;
@@ -89,7 +89,7 @@ where
   set_log_level(ctx);
 
   if let Some(model) = ctx.model {
-    if model != arg_defs::DeviceModel::Storage {
+    if model != args::DeviceModel::Storage {
       return Err(Error::from(
         "This command is only available on the Nitrokey Storage",
       ));
@@ -109,7 +109,7 @@ where
   F: FnMut(&mut ExecCtx<'_>, nitrokey::PasswordSafe<'_, '_>) -> Result<()>,
 {
   with_device(ctx, |ctx, mut device| {
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?;
     try_with_pin_and_data(
       ctx,
       &pin_entry,
@@ -133,7 +133,7 @@ where
 fn authenticate<'mgr, D, A, F>(
   ctx: &mut ExecCtx<'_>,
   device: D,
-  pin_type: arg_defs::PinType,
+  pin_type: args::PinType,
   msg: &'static str,
   op: F,
 ) -> Result<A>
@@ -154,7 +154,7 @@ where
   authenticate(
     ctx,
     device,
-    arg_defs::PinType::User,
+    args::PinType::User,
     "Could not authenticate as user",
     |_ctx, device, pin| device.authenticate_user(pin),
   )
@@ -168,7 +168,7 @@ where
   authenticate(
     ctx,
     device,
-    arg_defs::PinType::Admin,
+    args::PinType::Admin,
     "Could not authenticate as admin",
     |_ctx, device, pin| device.authenticate_admin(pin),
   )
@@ -256,8 +256,8 @@ where
     // Ideally we would not clone here, but that would require us to
     // restrict op to work with an immutable ExecCtx, which is not
     // possible given that some clients print data.
-    arg_defs::PinType::Admin => ctx.admin_pin.clone(),
-    arg_defs::PinType::User => ctx.user_pin.clone(),
+    args::PinType::Admin => ctx.admin_pin.clone(),
+    args::PinType::User => ctx.user_pin.clone(),
   };
 
   if let Some(pin) = pin {
@@ -410,7 +410,7 @@ pub fn list(ctx: &mut ExecCtx<'_>, no_connect: bool) -> Result<()> {
 /// Perform a factory reset.
 pub fn reset(ctx: &mut ExecCtx<'_>) -> Result<()> {
   with_device(ctx, |ctx, mut device| {
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::Admin, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::Admin, &device)?;
 
     // To force the user to enter the admin PIN before performing a
     // factory reset, we clear the pinentry cache for the admin PIN.
@@ -432,12 +432,12 @@ pub fn reset(ctx: &mut ExecCtx<'_>) -> Result<()> {
 }
 
 /// Change the configuration of the unencrypted volume.
-pub fn unencrypted_set(ctx: &mut ExecCtx<'_>, mode: arg_defs::UnencryptedVolumeMode) -> Result<()> {
+pub fn unencrypted_set(ctx: &mut ExecCtx<'_>, mode: args::UnencryptedVolumeMode) -> Result<()> {
   with_storage_device(ctx, |ctx, mut device| {
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::Admin, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::Admin, &device)?;
     let mode = match mode {
-      arg_defs::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite,
-      arg_defs::UnencryptedVolumeMode::ReadOnly => nitrokey::VolumeMode::ReadOnly,
+      args::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite,
+      args::UnencryptedVolumeMode::ReadOnly => nitrokey::VolumeMode::ReadOnly,
     };
 
     // The unencrypted volume may reconnect, so be sure to flush caches to
@@ -456,7 +456,7 @@ pub fn unencrypted_set(ctx: &mut ExecCtx<'_>, mode: arg_defs::UnencryptedVolumeM
 /// Open the encrypted volume on the Nitrokey.
 pub fn encrypted_open(ctx: &mut ExecCtx<'_>) -> Result<()> {
   with_storage_device(ctx, |ctx, mut device| {
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?;
 
     // We may forcefully close a hidden volume, if active, so be sure to
     // flush caches to disk.
@@ -567,10 +567,10 @@ pub fn config_get(ctx: &mut ExecCtx<'_>) -> Result<()> {
 }
 
 /// Write the Nitrokey configuration.
-pub fn config_set(ctx: &mut ExecCtx<'_>, args: arg_defs::ConfigSetArgs) -> Result<()> {
-  let numlock = arg_defs::ConfigOption::try_from(args.no_numlock, args.numlock, "numlock")?;
-  let capslock = arg_defs::ConfigOption::try_from(args.no_capslock, args.capslock, "capslock")?;
-  let scrollock = arg_defs::ConfigOption::try_from(args.no_scrollock, args.scrollock, "scrollock")?;
+pub fn config_set(ctx: &mut ExecCtx<'_>, args: args::ConfigSetArgs) -> Result<()> {
+  let numlock = args::ConfigOption::try_from(args.no_numlock, args.numlock, "numlock")?;
+  let capslock = args::ConfigOption::try_from(args.no_capslock, args.capslock, "capslock")?;
+  let scrollock = args::ConfigOption::try_from(args.no_scrollock, args.scrollock, "scrollock")?;
   let otp_pin = if args.otp_pin {
     Some(true)
   } else if args.no_otp_pin {
@@ -605,13 +605,13 @@ pub fn lock(ctx: &mut ExecCtx<'_>) -> Result<()> {
   })
 }
 
-fn get_otp<T>(slot: u8, algorithm: arg_defs::OtpAlgorithm, device: &mut T) -> Result<String>
+fn get_otp<T>(slot: u8, algorithm: args::OtpAlgorithm, device: &mut T) -> Result<String>
 where
   T: GenerateOtp,
 {
   match algorithm {
-    arg_defs::OtpAlgorithm::Hotp => device.get_hotp_code(slot),
-    arg_defs::OtpAlgorithm::Totp => device.get_totp_code(slot),
+    args::OtpAlgorithm::Hotp => device.get_hotp_code(slot),
+    args::OtpAlgorithm::Totp => device.get_totp_code(slot),
   }
   .map_err(|err| get_error("Could not generate OTP", err))
 }
@@ -627,11 +627,11 @@ fn get_unix_timestamp() -> Result<u64> {
 pub fn otp_get(
   ctx: &mut ExecCtx<'_>,
   slot: u8,
-  algorithm: arg_defs::OtpAlgorithm,
+  algorithm: args::OtpAlgorithm,
   time: Option<u64>,
 ) -> Result<()> {
   with_device(ctx, |ctx, mut device| {
-    if algorithm == arg_defs::OtpAlgorithm::Totp {
+    if algorithm == args::OtpAlgorithm::Totp {
       device
         .set_time(
           match time {
@@ -688,7 +688,7 @@ fn prepare_base32_secret(secret: &str) -> Result<String> {
 }
 
 /// Configure a one-time password slot on the Nitrokey device.
-pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<()> {
+pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: args::OtpSetArgs) -> Result<()> {
   let mut data = nitrokey::OtpSlotData {
     number: args.slot,
     name: mem::take(&mut args.name),
@@ -700,9 +700,9 @@ pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<
 
   with_device(ctx, |ctx, device| {
     let secret = match args.format {
-      arg_defs::OtpSecretFormat::Ascii => prepare_ascii_secret(&data.secret)?,
-      arg_defs::OtpSecretFormat::Base32 => prepare_base32_secret(&data.secret)?,
-      arg_defs::OtpSecretFormat::Hex => {
+      args::OtpSecretFormat::Ascii => prepare_ascii_secret(&data.secret)?,
+      args::OtpSecretFormat::Base32 => prepare_base32_secret(&data.secret)?,
+      args::OtpSecretFormat::Hex => {
         // We need to ensure to provide a string with an even number of
         // characters in it, just because that's what libnitrokey
         // expects. So prepend a '0' if that is not the case.
@@ -718,8 +718,8 @@ pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<
     let data = nitrokey::OtpSlotData { secret, ..data };
     let mut device = authenticate_admin(ctx, device)?;
     match args.algorithm {
-      arg_defs::OtpAlgorithm::Hotp => device.write_hotp_slot(data, args.counter),
-      arg_defs::OtpAlgorithm::Totp => device.write_totp_slot(data, args.time_window),
+      args::OtpAlgorithm::Hotp => device.write_hotp_slot(data, args.counter),
+      args::OtpAlgorithm::Totp => device.write_totp_slot(data, args.time_window),
     }
     .map_err(|err| get_error("Could not write OTP slot", err))?;
     Ok(())
@@ -727,12 +727,12 @@ pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<
 }
 
 /// Clear an OTP slot.
-pub fn otp_clear(ctx: &mut ExecCtx<'_>, slot: u8, algorithm: arg_defs::OtpAlgorithm) -> Result<()> {
+pub fn otp_clear(ctx: &mut ExecCtx<'_>, slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> {
   with_device(ctx, |ctx, device| {
     let mut device = authenticate_admin(ctx, device)?;
     match algorithm {
-      arg_defs::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot),
-      arg_defs::OtpAlgorithm::Totp => device.erase_totp_slot(slot),
+      args::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot),
+      args::OtpAlgorithm::Totp => device.erase_totp_slot(slot),
     }
     .map_err(|err| get_error("Could not clear OTP slot", err))?;
     Ok(())
@@ -741,15 +741,15 @@ pub fn otp_clear(ctx: &mut ExecCtx<'_>, slot: u8, algorithm: arg_defs::OtpAlgori
 
 fn print_otp_status(
   ctx: &mut ExecCtx<'_>,
-  algorithm: arg_defs::OtpAlgorithm,
+  algorithm: args::OtpAlgorithm,
   device: &nitrokey::DeviceWrapper<'_>,
   all: bool,
 ) -> Result<()> {
   let mut slot: u8 = 0;
   loop {
     let result = match algorithm {
-      arg_defs::OtpAlgorithm::Hotp => device.get_hotp_slot_name(slot),
-      arg_defs::OtpAlgorithm::Totp => device.get_totp_slot_name(slot),
+      args::OtpAlgorithm::Hotp => device.get_hotp_slot_name(slot),
+      args::OtpAlgorithm::Totp => device.get_totp_slot_name(slot),
     };
     slot = match slot.checked_add(1) {
       Some(slot) => slot,
@@ -777,8 +777,8 @@ fn print_otp_status(
 pub fn otp_status(ctx: &mut ExecCtx<'_>, all: bool) -> Result<()> {
   with_device(ctx, |ctx, device| {
     println!(ctx, "alg\tslot\tname")?;
-    print_otp_status(ctx, arg_defs::OtpAlgorithm::Hotp, &device, all)?;
-    print_otp_status(ctx, arg_defs::OtpAlgorithm::Totp, &device, all)?;
+    print_otp_status(ctx, args::OtpAlgorithm::Hotp, &device, all)?;
+    print_otp_status(ctx, args::OtpAlgorithm::Totp, &device, all)?;
     Ok(())
   })
 }
@@ -786,11 +786,8 @@ pub fn otp_status(ctx: &mut ExecCtx<'_>, all: bool) -> Result<()> {
 /// Clear the PIN stored by various operations.
 pub fn pin_clear(ctx: &mut ExecCtx<'_>) -> Result<()> {
   with_device(ctx, |_ctx, device| {
-    pinentry::clear(&pinentry::PinEntry::from(
-      arg_defs::PinType::Admin,
-      &device,
-    )?)?;
-    pinentry::clear(&pinentry::PinEntry::from(arg_defs::PinType::User, &device)?)?;
+    pinentry::clear(&pinentry::PinEntry::from(args::PinType::Admin, &device)?)?;
+    pinentry::clear(&pinentry::PinEntry::from(args::PinType::User, &device)?)?;
     Ok(())
   })
 }
@@ -801,14 +798,14 @@ pub fn pin_clear(ctx: &mut ExecCtx<'_>) -> Result<()> {
 /// given PIN type, it will be used.
 fn choose_pin(ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, new: bool) -> Result<String> {
   let new_pin = match pin_entry.pin_type() {
-    arg_defs::PinType::Admin => {
+    args::PinType::Admin => {
       if new {
         &ctx.new_admin_pin
       } else {
         &ctx.admin_pin
       }
     }
-    arg_defs::PinType::User => {
+    args::PinType::User => {
       if new {
         &ctx.new_user_pin
       } else {
@@ -828,7 +825,7 @@ fn choose_pin(ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, new: bool)
 }
 
 /// Change a PIN.
-pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: arg_defs::PinType) -> Result<()> {
+pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: args::PinType) -> Result<()> {
   with_device(ctx, |ctx, mut device| {
     let pin_entry = pinentry::PinEntry::from(pin_type, &device)?;
     let new_pin = choose_pin(ctx, &pin_entry, true)?;
@@ -838,8 +835,8 @@ pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: arg_defs::PinType) -> Result<()>
       &pin_entry,
       "Could not change the PIN",
       |current_pin| match pin_type {
-        arg_defs::PinType::Admin => device.change_admin_pin(&current_pin, &new_pin),
-        arg_defs::PinType::User => device.change_user_pin(&current_pin, &new_pin),
+        args::PinType::Admin => device.change_admin_pin(&current_pin, &new_pin),
+        args::PinType::User => device.change_user_pin(&current_pin, &new_pin),
       },
     )?;
 
@@ -853,9 +850,9 @@ pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: arg_defs::PinType) -> Result<()>
 /// Unblock and reset the user PIN.
 pub fn pin_unblock(ctx: &mut ExecCtx<'_>) -> Result<()> {
   with_device(ctx, |ctx, mut device| {
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?;
     let user_pin = choose_pin(ctx, &pin_entry, false)?;
-    let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::Admin, &device)?;
+    let pin_entry = pinentry::PinEntry::from(args::PinType::Admin, &device)?;
 
     try_with_pin(
       ctx,
diff --git a/src/main.rs b/src/main.rs
index a2c4f48..27097c9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -68,7 +68,7 @@ mod redefine;
 #[macro_use]
 mod arg_util;
 
-mod arg_defs;
+mod args;
 mod commands;
 mod error;
 mod pinentry;
@@ -116,7 +116,7 @@ where
 #[allow(missing_debug_implementations)]
 pub struct ExecCtx<'io> {
   /// The Nitrokey model to use.
-  pub model: Option<arg_defs::DeviceModel>,
+  pub model: Option<args::DeviceModel>,
   /// See `RunCtx::stdout`.
   pub stdout: &'io mut dyn io::Write,
   /// See `RunCtx::stderr`.
@@ -147,7 +147,7 @@ impl<'io> Stdio for ExecCtx<'io> {
 fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec<String>) -> Result<()> {
   use structopt::StructOpt;
 
-  match arg_defs::Args::from_iter_safe(args.iter()) {
+  match args::Args::from_iter_safe(args.iter()) {
     Ok(args) => {
       let mut ctx = ExecCtx {
         model: args.model,
diff --git a/src/pinentry.rs b/src/pinentry.rs
index 47c8844..d1387f4 100644
--- a/src/pinentry.rs
+++ b/src/pinentry.rs
@@ -23,7 +23,7 @@ use std::io;
 use std::process;
 use std::str;
 
-use crate::arg_defs;
+use crate::args;
 use crate::error::Error;
 use crate::ExecCtx;
 
@@ -43,13 +43,13 @@ pub trait SecretEntry: fmt::Debug {
 
 #[derive(Debug)]
 pub struct PinEntry {
-  pin_type: arg_defs::PinType,
+  pin_type: args::PinType,
   model: nitrokey::Model,
   serial: nitrokey::SerialNumber,
 }
 
 impl PinEntry {
-  pub fn from<'mgr, D>(pin_type: arg_defs::PinType, device: &D) -> crate::Result<Self>
+  pub fn from<'mgr, D>(pin_type: args::PinType, device: &D) -> crate::Result<Self>
   where
     D: nitrokey::Device<'mgr>,
   {
@@ -62,7 +62,7 @@ impl PinEntry {
     })
   }
 
-  pub fn pin_type(&self) -> arg_defs::PinType {
+  pub fn pin_type(&self) -> args::PinType {
     self.pin_type
   }
 }
@@ -72,16 +72,16 @@ impl SecretEntry for PinEntry {
     let model = self.model.to_string().to_lowercase();
     let suffix = format!("{}:{}", model, self.serial);
     let cache_id = match self.pin_type {
-      arg_defs::PinType::Admin => format!("nitrocli:admin:{}", suffix),
-      arg_defs::PinType::User => format!("nitrocli:user:{}", suffix),
+      args::PinType::Admin => format!("nitrocli:admin:{}", suffix),
+      args::PinType::User => format!("nitrocli:user:{}", suffix),
     };
     Some(cache_id.into())
   }
 
   fn prompt(&self) -> CowStr {
     match self.pin_type {
-      arg_defs::PinType::Admin => "Admin PIN",
-      arg_defs::PinType::User => "User PIN",
+      args::PinType::Admin => "Admin PIN",
+      args::PinType::User => "User PIN",
     }
     .into()
   }
@@ -90,12 +90,12 @@ impl SecretEntry for PinEntry {
     format!(
       "{} for\rNitrokey {} {}",
       match self.pin_type {
-        arg_defs::PinType::Admin => match mode {
+        args::PinType::Admin => match mode {
           Mode::Choose => "Please enter a new admin PIN",
           Mode::Confirm => "Please confirm the new admin PIN",
           Mode::Query => "Please enter the admin PIN",
         },
-        arg_defs::PinType::User => match mode {
+        args::PinType::User => match mode {
           Mode::Choose => "Please enter a new user PIN",
           Mode::Confirm => "Please confirm the new user PIN",
           Mode::Query => "Please enter the user PIN",
@@ -109,8 +109,8 @@ impl SecretEntry for PinEntry {
 
   fn min_len(&self) -> u8 {
     match self.pin_type {
-      arg_defs::PinType::Admin => 8,
-      arg_defs::PinType::User => 6,
+      args::PinType::Admin => 8,
+      args::PinType::User => 6,
     }
   }
 }
diff --git a/src/tests/otp.rs b/src/tests/otp.rs
index 4ce1156..f923170 100644
--- a/src/tests/otp.rs
+++ b/src/tests/otp.rs
@@ -1,7 +1,7 @@
 // otp.rs
 
 // *************************************************************************
-// * Copyright (C) 2019 Daniel Mueller (deso@posteo.net)                   *
+// * Copyright (C) 2019-2020 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  *
@@ -19,7 +19,7 @@
 
 use super::*;
 
-use crate::arg_defs;
+use crate::args;
 
 #[test_device]
 fn set_invalid_slot_raw(model: nitrokey::Model) {
@@ -101,8 +101,8 @@ fn set_get_totp(model: nitrokey::Model) -> crate::Result<()> {
 #[test_device]
 fn set_totp_uneven_chars(model: nitrokey::Model) -> crate::Result<()> {
   let secrets = [
-    (arg_defs::OtpSecretFormat::Hex, "123"),
-    (arg_defs::OtpSecretFormat::Base32, "FBILDWWGA2"),
+    (args::OtpSecretFormat::Hex, "123"),
+    (args::OtpSecretFormat::Base32, "FBILDWWGA2"),
   ];
 
   for (format, secret) in &secrets {
diff --git a/var/shell-complete.rs b/var/shell-complete.rs
index c69a426..4793ed3 100644
--- a/var/shell-complete.rs
+++ b/var/shell-complete.rs
@@ -39,7 +39,7 @@ mod nitrocli {
     };
   }
 
-  include!("../src/arg_defs.rs");
+  include!("../src/args.rs");
 }
 
 /// Generate a bash completion script for nitrocli.
-- 
cgit v1.2.3