From 4ee0715e390c9603ad80f4185cdf00662f789cd6 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Mon, 12 May 2025 20:32:58 -0300 Subject: [PATCH] refactor: update digest engine to use Result and Write, add is_supported method --- src/digest_engine.rs | 37 ++++++++++++++----- src/digest_engine/none.rs | 13 +++---- src/digest_engine/program.rs | 70 ++++++++++++++++++++++++++++++++---- src/digest_engine/sha2.rs | 43 +++++++--------------- src/modes/save.rs | 12 +++---- 5 files changed, 115 insertions(+), 60 deletions(-) diff --git a/src/digest_engine.rs b/src/digest_engine.rs index 859671b..c8c7b98 100644 --- a/src/digest_engine.rs +++ b/src/digest_engine.rs @@ -1,15 +1,22 @@ +use anyhow::{anyhow, Context, Result}; use std::io; +use std::io::{Read, Write}; +use log::*; + +use lazy_static::lazy_static; + +extern crate enum_map; +use enum_map::enum_map; +use enum_map::{Enum, EnumMap}; pub mod none; -pub mod sha2_digest; pub mod program; +pub mod sha2; use crate::digest_engine::none::DigestEngineNone; -use crate::digest_engine::sha2_digest::DigestEngineSha256; use crate::digest_engine::program::DigestEngineProgram; - -use enum_map::Enum; +use crate::digest_engine::sha2::DigestEngineSha256; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)] #[strum(ascii_case_insensitive)] @@ -20,17 +27,29 @@ pub enum DigestType { } pub trait DigestEngine { - fn create(&self) -> Box; - - fn update(&mut self, data: &[u8]) -> io::Result<()>; - + fn is_supported(&self) -> bool { + true + } + fn create(&self) -> Result>; fn finalize(&mut self) -> io::Result; } +lazy_static! { + pub static ref DIGEST_PROGRAMS: EnumMap> = enum_map! { + DigestType::Sha256 => None, + DigestType::Md5 => { + let program = DigestEngineProgram::new("bzip2", vec![]); + if program.supported { Some(program) } else { None } + } + DigestType::None => None + }; +} + + pub fn get_engine(digest_type: DigestType) -> Box { match digest_type { DigestType::Sha256 => Box::new(DigestEngineSha256::new()), - DigestType::Md5 => Box::new(DigestEngineProgram::new("md5sum")), + DigestType::Md5 => Box::new(DigestEngineProgram::new("md5sum", vec![])), DigestType::None => Box::new(DigestEngineNone::new()), } } diff --git a/src/digest_engine/none.rs b/src/digest_engine/none.rs index c0386b9..0cdcf8d 100644 --- a/src/digest_engine/none.rs +++ b/src/digest_engine/none.rs @@ -1,5 +1,7 @@ -use std::io; +use anyhow::Result; use crate::digest_engine::DigestEngine; +use std::io::{Read, Write}; +use std::io; #[derive(Debug, Eq, PartialEq, Clone, Default)] pub struct DigestEngineNone {} @@ -11,14 +13,9 @@ impl DigestEngineNone { } impl DigestEngine for DigestEngineNone { - fn create(&self) -> Box { - Box::new(Self::new()) + fn create(&self) -> Result> { + Ok(Box::new(Self::new())) } - - fn update(&mut self, _data: &[u8]) -> io::Result<()> { - Ok(()) - } - fn finalize(&mut self) -> io::Result { Ok("none".to_string()) } diff --git a/src/digest_engine/program.rs b/src/digest_engine/program.rs index 9b77a65..0de559e 100644 --- a/src/digest_engine/program.rs +++ b/src/digest_engine/program.rs @@ -1,27 +1,83 @@ +use anyhow::{anyhow, Context, Result}; +use log::*; +use std::env; +use std::fs; +use std::fs::File; use std::io; +use std::io::{Read, Write}; +use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; +use std::process::{Command, Stdio}; + use crate::digest_engine::DigestEngine; #[derive(Debug, Eq, PartialEq, Clone)] pub struct DigestEngineProgram { - program: String, + pub program: String, + pub args: Vec, + pub supported: bool, } impl DigestEngineProgram { - pub fn new(program: &str) -> DigestEngineProgram { - DigestEngineProgram { program: program.to_string() } + pub fn new(program: &str, args: Vec<&str>) -> DigestEngineProgram { + let program_path = get_program_path(program); + let supported = program_path.is_ok(); + DigestEngineProgram { + program: program_path.unwrap_or(program.to_string()), + args: args.iter().map(|s| s.to_string()).collect(), + supported, + } } } impl DigestEngine for DigestEngineProgram { - fn create(&self) -> Box { - Box::new(Self::new(&self.program)) + fn is_supported(&self) -> bool { + self.supported } + fn create(&self) -> Result> { + debug!("DIGEST: Writting using {:?}", *self); - fn update(&mut self, _data: &[u8]) -> io::Result<()> { - Ok(()) + let program = self.program.clone(); + let args = self.args.clone(); + + debug!( + "DIGEST: Executing command: {:?} {:?}", + program, args + ); + + let process = Command::new(program.clone()) + .args(args.clone()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .context(anyhow!( + "Problem spawning child process: {:?} {:?}", + program, + args + ))?; + + Ok(Box::new(process.stdin.unwrap())) } fn finalize(&mut self) -> io::Result { Ok("program".to_string()) } } + +fn get_program_path(program: &str) -> Result { + debug!("COMPRESSION: Looking for executable: {}", program); + if let Ok(path) = env::var("PATH") { + for p in path.split(':') { + let p_str = format!("{}/{}", p, program); + let stat = fs::metadata(p_str.clone()); + if let Ok(stat) = stat { + let md = stat; + let permissions = md.permissions(); + if md.is_file() && permissions.mode() & 0o111 != 0 { + return Ok(p_str); + } + } + } + } + Err(anyhow!("Unable to find binary {} in PATH", program)) +} diff --git a/src/digest_engine/sha2.rs b/src/digest_engine/sha2.rs index 718fc52..7b8d8f9 100644 --- a/src/digest_engine/sha2.rs +++ b/src/digest_engine/sha2.rs @@ -1,5 +1,12 @@ -use std::io; +use anyhow::Result; +use log::*; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; use sha2::{Digest, Sha256}; +use std::io; + +use crate::digest_engine::DigestEngine; #[derive(Debug, Clone, Default)] pub struct DigestEngineSha256 { @@ -8,42 +15,18 @@ pub struct DigestEngineSha256 { impl DigestEngineSha256 { pub fn new() -> DigestEngineSha256 { - DigestEngineSha256 { hasher: Sha256::new() } - } - - // Manual implementation of PartialEq - fn eq(&self, other: &Self) -> bool { - let result1 = self.hasher.clone().finalize(); - let result2 = other.hasher.clone().finalize(); - result1.as_slice() == result2.as_slice() - } - - // Manual implementation of Eq - fn is_eq(&self) -> bool { - true + DigestEngineSha256 { + hasher: Sha256::new(), + } } + } -use crate::digest_engine::DigestEngine; - -impl PartialEq for DigestEngineSha256 { - fn eq(&self, other: &Self) -> bool { - self.eq(other) - } -} - -impl Eq for DigestEngineSha256 {} - impl DigestEngine for DigestEngineSha256 { - fn create(&self) -> Box { + fn create(&self) -> Result> { Box::new(Self::new()) } - fn update(&mut self, data: &[u8]) -> io::Result<()> { - self.hasher.update(data); - Ok(()) - } - fn finalize(&mut self) -> io::Result { let result = self.hasher.clone().finalize(); Ok(format!("{:x}", result)) diff --git a/src/modes/save.rs b/src/modes/save.rs index 1e4bf7c..91d622d 100644 --- a/src/modes/save.rs +++ b/src/modes/save.rs @@ -11,7 +11,7 @@ use log::debug; use rusqlite::Connection; use crate::db::{self}; -use crate::digest_engine::{DigestEngine, get_engine, DigestType}; +use crate::digest_engine::{get_engine, DigestEngine, DigestType}; use crate::modes::common::get_meta_from_env; use chrono::Utc; @@ -49,7 +49,8 @@ pub fn mode_save( use gethostname::gethostname; use std::io::Write; - let compression_type_opt = crate::compression_engine::CompressionType::from_str(&compression_name); + let compression_type_opt = + crate::compression_engine::CompressionType::from_str(&compression_name); if compression_type_opt.is_err() { cmd.error( ErrorKind::InvalidValue, @@ -62,10 +63,9 @@ pub fn mode_save( debug!("MAIN: Compression type: {}", compression_type); // Create a new digest engine - let digest_type = DigestType::from_str(&digest_name) - .unwrap_or(DigestType::Sha256); + let digest_type = DigestType::from_str(&digest_name).unwrap_or(DigestType::Sha256); let mut digest_engine = get_engine(digest_type); - + let mut item = db::Item { id: None, ts: Utc::now(), @@ -163,7 +163,7 @@ pub fn mode_save( stdout.flush()?; item_out.flush()?; - + // Finalize the digest and log the result let digest = digest_engine.finalize()?; debug!("DIGEST: {}", digest);