From ac2bfc709f57094bac54b299f0a307b765e9b674 Mon Sep 17 00:00:00 2001
From: Daniel Mueller <deso@posteo.net>
Date: Sat, 19 Jan 2019 18:37:21 -0800
Subject: Introduce SecretEntry trait

Now that we have introduced the notion of a secret abstracting over
whether something is a PIN or a password in terms of terminology, we
need to define what makes a secret in code. From the pinentry module's
perspective, the commonality between the two is that they both can be
entered through a dialog containing a prompt and a description, and they
can be cached.
This change introduces a trait, SecretEntry, that defines methods
representing those properties. Right now only the existing PinEntry
struct implements this trait.
---
 nitrocli/src/pinentry.rs | 35 +++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/nitrocli/src/pinentry.rs b/nitrocli/src/pinentry.rs
index 94218c4..f7a400c 100644
--- a/nitrocli/src/pinentry.rs
+++ b/nitrocli/src/pinentry.rs
@@ -17,6 +17,7 @@
 // * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 // *************************************************************************
 
+use std::fmt;
 use std::process;
 use std::str;
 
@@ -31,6 +32,16 @@ Enum! {PinType, [
   User => "user"
 ]}
 
+/// A trait representing a secret to be entered by the user.
+pub trait SecretEntry: fmt::Debug {
+  /// The cache ID to use for this secret.
+  fn cache_id(&self) -> String;
+  /// The prompt to display when asking for the secret.
+  fn prompt(&self) -> &'static str;
+  /// The description to display when asking for the secret.
+  fn description(&self, mode: Mode) -> String;
+}
+
 #[derive(Debug)]
 pub struct PinEntry {
   pin_type: PinType,
@@ -52,6 +63,12 @@ impl PinEntry {
     })
   }
 
+  pub fn pin_type(&self) -> PinType {
+    self.pin_type
+  }
+}
+
+impl SecretEntry for PinEntry {
   fn cache_id(&self) -> String {
     let model = self.model.to_string().to_lowercase();
     let suffix = format!("{}:{}", model, self.serial);
@@ -88,10 +105,6 @@ impl PinEntry {
       self.serial,
     )
   }
-
-  pub fn pin_type(&self) -> PinType {
-    self.pin_type
-  }
 }
 
 /// Secret entry mode for pinentry.
@@ -116,7 +129,7 @@ impl Mode {
   }
 }
 
-fn parse_pinentry_pin<R>(response: R) -> Result<String, Error>
+fn parse_pinentry_pin<R>(response: R) -> crate::Result<String>
 where
   R: AsRef<str>,
 {
@@ -150,7 +163,10 @@ where
 /// the entry dialog. The mode describes the context of the pinentry
 /// dialog. It is used to choose an appropriate description and to
 /// decide whether a quality bar is shown in the dialog.
-pub fn inquire(entry: &PinEntry, mode: Mode, error_msg: Option<&str>) -> crate::Result<String> {
+pub fn inquire<E>(entry: &E, mode: Mode, error_msg: Option<&str>) -> crate::Result<String>
+where
+  E: SecretEntry,
+{
   let cache_id = entry.cache_id();
   let error_msg = error_msg
     .map(|msg| msg.replace(" ", "+"))
@@ -205,7 +221,7 @@ pub fn choose(entry: &PinEntry) -> crate::Result<String> {
   }
 }
 
-fn parse_pinentry_response<R>(response: R) -> Result<(), Error>
+fn parse_pinentry_response<R>(response: R) -> crate::Result<()>
 where
   R: AsRef<str>,
 {
@@ -220,7 +236,10 @@ where
 }
 
 /// Clear the cached secret represented by the given entry.
-pub fn clear(entry: &PinEntry) -> Result<(), Error> {
+pub fn clear<E>(entry: &E) -> crate::Result<()>
+where
+  E: SecretEntry,
+{
   let command = format!("CLEAR_PASSPHRASE {}", entry.cache_id());
   let output = process::Command::new("gpg-connect-agent")
     .arg(command)
-- 
cgit v1.2.3