]> code.octet-stream.net Git - hashgood/blobdiff - src/main.rs
Exit with non-zero exit code if a verification fails
[hashgood] / src / main.rs
index 2f1e7f321c7328e6e0772b0392f4b1d3b384d28e..b611b14c9c6ed6f04a52c7921ed37241fd109679 100644 (file)
@@ -1,5 +1,5 @@
 use std::error::Error;
 use std::error::Error;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process;
 use structopt::StructOpt;
 
 use std::process;
 use structopt::StructOpt;
 
@@ -12,10 +12,16 @@ mod display;
 /// Collect candidate hashes based on options and match them against a calculated hash
 mod verify;
 
 /// Collect candidate hashes based on options and match them against a calculated hash
 mod verify;
 
+/// Problem running the program
+const EXIT_ERR: i32 = 1;
+/// Verification was performed and was not a match
+const EXIT_MISMATCH: i32 = 2;
+
 #[derive(StructOpt)]
 #[structopt(name = "hashgood")]
 pub struct Opt {
     /// Read the hash from the clipboard
 #[derive(StructOpt)]
 #[structopt(name = "hashgood")]
 pub struct Opt {
     /// Read the hash from the clipboard
+    #[cfg(feature = "paste")]
     #[structopt(short = "p", long = "paste")]
     paste: bool,
 
     #[structopt(short = "p", long = "paste")]
     paste: bool,
 
@@ -36,6 +42,19 @@ pub struct Opt {
     hash: Option<String>,
 }
 
     hash: Option<String>,
 }
 
+impl Opt {
+    fn get_paste(&self) -> bool {
+        #[cfg(feature = "paste")]
+        {
+            self.paste
+        }
+        #[cfg(not(feature = "paste"))]
+        {
+            false
+        }
+    }
+}
+
 /// Types of supported digest algorithm
 #[derive(Debug, PartialEq, Copy, Clone)]
 pub enum Algorithm {
 /// Types of supported digest algorithm
 #[derive(Debug, PartialEq, Copy, Clone)]
 pub enum Algorithm {
@@ -57,11 +76,12 @@ impl Algorithm {
 }
 
 /// The method by which one or more hashes were supplied to verify the calculated digest
 }
 
 /// The method by which one or more hashes were supplied to verify the calculated digest
+#[derive(Debug, PartialEq)]
 pub enum VerificationSource {
     CommandArgument,
     Clipboard,
 pub enum VerificationSource {
     CommandArgument,
     Clipboard,
-    RawFile(PathBuf),
-    DigestsFile(PathBuf),
+    RawFile(String),
+    DigestsFile(String),
 }
 
 /// A complete standalone hash result
 }
 
 /// A complete standalone hash result
@@ -72,7 +92,7 @@ pub struct Hash {
 }
 
 impl Hash {
 }
 
 impl Hash {
-    pub fn new(alg: Algorithm, bytes: Vec<u8>, path: &PathBuf) -> Self {
+    pub fn new(alg: Algorithm, bytes: Vec<u8>, path: &Path) -> Self {
         // Taking the filename component should always work?
         // If not, just fall back to the full path
         let filename = match path.file_name() {
         // Taking the filename component should always work?
         // If not, just fall back to the full path
         let filename = match path.file_name() {
@@ -88,6 +108,7 @@ impl Hash {
 }
 
 /// A possible hash to match against. The algorithm is assumed.
 }
 
 /// A possible hash to match against. The algorithm is assumed.
+#[derive(Debug, PartialEq)]
 pub struct CandidateHash {
     bytes: Vec<u8>,
     filename: Option<String>,
 pub struct CandidateHash {
     bytes: Vec<u8>,
     filename: Option<String>,
@@ -95,6 +116,7 @@ pub struct CandidateHash {
 
 /// A list of candidate hashes that our input could potentially match. At this point it is
 /// assumed that we will be verifying a digest of a particular, single algorithm.
 
 /// A list of candidate hashes that our input could potentially match. At this point it is
 /// assumed that we will be verifying a digest of a particular, single algorithm.
+#[derive(Debug, PartialEq)]
 pub struct CandidateHashes {
     alg: Algorithm,
     hashes: Vec<CandidateHash>,
 pub struct CandidateHashes {
     alg: Algorithm,
     hashes: Vec<CandidateHash>,
@@ -102,6 +124,7 @@ pub struct CandidateHashes {
 }
 
 /// Summary of an atetmpt to match the calculated digest against candidates
 }
 
 /// Summary of an atetmpt to match the calculated digest against candidates
+#[derive(PartialEq)]
 pub enum MatchLevel {
     Ok,
     Maybe,
 pub enum MatchLevel {
     Ok,
     Maybe,
@@ -130,7 +153,7 @@ pub struct Verification<'a> {
 fn main() {
     hashgood().unwrap_or_else(|e| {
         eprintln!("Error: {}", e);
 fn main() {
     hashgood().unwrap_or_else(|e| {
         eprintln!("Error: {}", e);
-        process::exit(1);
+        process::exit(EXIT_ERR);
     });
 }
 
     });
 }
 
@@ -138,7 +161,7 @@ fn main() {
 fn hashgood() -> Result<(), Box<dyn Error>> {
     let opt = get_verified_options()?;
     let candidates = verify::get_candidate_hashes(&opt)?;
 fn hashgood() -> Result<(), Box<dyn Error>> {
     let opt = get_verified_options()?;
     let candidates = verify::get_candidate_hashes(&opt)?;
-    let input = calculate::get_input_reader(&opt.input)?;
+    let input = calculate::get_input_reader(opt.input.as_path())?;
     if let Some(c) = candidates {
         // If we have a candidate hash of a particular type, use that specific algorithm
         let hashes = calculate::create_digests(&[c.alg], input)?;
     if let Some(c) = candidates {
         // If we have a candidate hash of a particular type, use that specific algorithm
         let hashes = calculate::create_digests(&[c.alg], input)?;
@@ -147,6 +170,7 @@ fn hashgood() -> Result<(), Box<dyn Error>> {
             if c.alg == alg {
                 let hash = Hash::new(alg, bytes, &opt.input);
                 let verification = verify::verify_hash(&hash, &c);
             if c.alg == alg {
                 let hash = Hash::new(alg, bytes, &opt.input);
                 let verification = verify::verify_hash(&hash, &c);
+                let successful_match = verification.match_level == MatchLevel::Ok;
                 display::print_hash(
                     &hash,
                     verification.comparison_hash,
                 display::print_hash(
                     &hash,
                     verification.comparison_hash,
@@ -155,6 +179,9 @@ fn hashgood() -> Result<(), Box<dyn Error>> {
                 )?;
                 display::print_messages(verification.messages, opt.no_colour)?;
                 display::print_match_level(verification.match_level, opt.no_colour)?;
                 )?;
                 display::print_messages(verification.messages, opt.no_colour)?;
                 display::print_match_level(verification.match_level, opt.no_colour)?;
+                if !successful_match {
+                    process::exit(EXIT_MISMATCH);
+                }
             }
         }
     } else {
             }
         }
     } else {
@@ -179,12 +206,12 @@ fn hashgood() -> Result<(), Box<dyn Error>> {
 fn get_verified_options() -> Result<Opt, String> {
     let opt = Opt::from_args();
     let hash_methods =
 fn get_verified_options() -> Result<Opt, String> {
     let opt = Opt::from_args();
     let hash_methods =
-        opt.hash.is_some() as i32 + opt.paste as i32 + opt.hash_file.is_some() as i32;
+        opt.hash.is_some() as i32 + opt.get_paste() as i32 + opt.hash_file.is_some() as i32;
     if hash_methods > 1 {
         if opt.hash.is_some() {
             eprintln!("* specified as command line argument");
         }
     if hash_methods > 1 {
         if opt.hash.is_some() {
             eprintln!("* specified as command line argument");
         }
-        if opt.paste {
+        if opt.get_paste() {
             eprintln!("* paste from clipboard (-p)")
         }
         if opt.hash_file.is_some() {
             eprintln!("* paste from clipboard (-p)")
         }
         if opt.hash_file.is_some() {