refactor: update digest engine to use Result and Write, add is_supported method

This commit is contained in:
Andrew Phillips
2025-05-12 20:32:58 -03:00
committed by Andrew Phillips (aider)
parent 1aba71a3d6
commit 4ee0715e39
5 changed files with 115 additions and 60 deletions

View File

@@ -1,15 +1,22 @@
use anyhow::{anyhow, Context, Result};
use std::io; 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 none;
pub mod sha2_digest;
pub mod program; pub mod program;
pub mod sha2;
use crate::digest_engine::none::DigestEngineNone; use crate::digest_engine::none::DigestEngineNone;
use crate::digest_engine::sha2_digest::DigestEngineSha256;
use crate::digest_engine::program::DigestEngineProgram; use crate::digest_engine::program::DigestEngineProgram;
use crate::digest_engine::sha2::DigestEngineSha256;
use enum_map::Enum;
#[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)] #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)]
#[strum(ascii_case_insensitive)] #[strum(ascii_case_insensitive)]
@@ -20,17 +27,29 @@ pub enum DigestType {
} }
pub trait DigestEngine { pub trait DigestEngine {
fn create(&self) -> Box<dyn DigestEngine>; fn is_supported(&self) -> bool {
true
fn update(&mut self, data: &[u8]) -> io::Result<()>; }
fn create(&self) -> Result<Box<dyn Write>>;
fn finalize(&mut self) -> io::Result<String>; fn finalize(&mut self) -> io::Result<String>;
} }
lazy_static! {
pub static ref DIGEST_PROGRAMS: EnumMap<DigestType, Option<DigestEngineProgram>> = 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<dyn DigestEngine> { pub fn get_engine(digest_type: DigestType) -> Box<dyn DigestEngine> {
match digest_type { match digest_type {
DigestType::Sha256 => Box::new(DigestEngineSha256::new()), 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()), DigestType::None => Box::new(DigestEngineNone::new()),
} }
} }

View File

@@ -1,5 +1,7 @@
use std::io; use anyhow::Result;
use crate::digest_engine::DigestEngine; use crate::digest_engine::DigestEngine;
use std::io::{Read, Write};
use std::io;
#[derive(Debug, Eq, PartialEq, Clone, Default)] #[derive(Debug, Eq, PartialEq, Clone, Default)]
pub struct DigestEngineNone {} pub struct DigestEngineNone {}
@@ -11,14 +13,9 @@ impl DigestEngineNone {
} }
impl DigestEngine for DigestEngineNone { impl DigestEngine for DigestEngineNone {
fn create(&self) -> Box<dyn DigestEngine> { fn create(&self) -> Result<Box<dyn Write>> {
Box::new(Self::new()) Ok(Box::new(Self::new()))
} }
fn update(&mut self, _data: &[u8]) -> io::Result<()> {
Ok(())
}
fn finalize(&mut self) -> io::Result<String> { fn finalize(&mut self) -> io::Result<String> {
Ok("none".to_string()) Ok("none".to_string())
} }

View File

@@ -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;
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; use crate::digest_engine::DigestEngine;
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub struct DigestEngineProgram { pub struct DigestEngineProgram {
program: String, pub program: String,
pub args: Vec<String>,
pub supported: bool,
} }
impl DigestEngineProgram { impl DigestEngineProgram {
pub fn new(program: &str) -> DigestEngineProgram { pub fn new(program: &str, args: Vec<&str>) -> DigestEngineProgram {
DigestEngineProgram { program: program.to_string() } 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 { impl DigestEngine for DigestEngineProgram {
fn create(&self) -> Box<dyn DigestEngine> { fn is_supported(&self) -> bool {
Box::new(Self::new(&self.program)) self.supported
} }
fn create(&self) -> Result<Box<dyn Write>> {
debug!("DIGEST: Writting using {:?}", *self);
fn update(&mut self, _data: &[u8]) -> io::Result<()> { let program = self.program.clone();
Ok(()) 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<String> { fn finalize(&mut self) -> io::Result<String> {
Ok("program".to_string()) Ok("program".to_string())
} }
} }
fn get_program_path(program: &str) -> Result<String> {
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))
}

View File

@@ -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 sha2::{Digest, Sha256};
use std::io;
use crate::digest_engine::DigestEngine;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct DigestEngineSha256 { pub struct DigestEngineSha256 {
@@ -8,42 +15,18 @@ pub struct DigestEngineSha256 {
impl DigestEngineSha256 { impl DigestEngineSha256 {
pub fn new() -> DigestEngineSha256 { pub fn new() -> DigestEngineSha256 {
DigestEngineSha256 { hasher: Sha256::new() } 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
} }
} }
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 { impl DigestEngine for DigestEngineSha256 {
fn create(&self) -> Box<dyn DigestEngine> { fn create(&self) -> Result<Box<dyn Write>> {
Box::new(Self::new()) Box::new(Self::new())
} }
fn update(&mut self, data: &[u8]) -> io::Result<()> {
self.hasher.update(data);
Ok(())
}
fn finalize(&mut self) -> io::Result<String> { fn finalize(&mut self) -> io::Result<String> {
let result = self.hasher.clone().finalize(); let result = self.hasher.clone().finalize();
Ok(format!("{:x}", result)) Ok(format!("{:x}", result))

View File

@@ -11,7 +11,7 @@ use log::debug;
use rusqlite::Connection; use rusqlite::Connection;
use crate::db::{self}; 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 crate::modes::common::get_meta_from_env;
use chrono::Utc; use chrono::Utc;
@@ -49,7 +49,8 @@ pub fn mode_save(
use gethostname::gethostname; use gethostname::gethostname;
use std::io::Write; 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() { if compression_type_opt.is_err() {
cmd.error( cmd.error(
ErrorKind::InvalidValue, ErrorKind::InvalidValue,
@@ -62,10 +63,9 @@ pub fn mode_save(
debug!("MAIN: Compression type: {}", compression_type); debug!("MAIN: Compression type: {}", compression_type);
// Create a new digest engine // Create a new digest engine
let digest_type = DigestType::from_str(&digest_name) let digest_type = DigestType::from_str(&digest_name).unwrap_or(DigestType::Sha256);
.unwrap_or(DigestType::Sha256);
let mut digest_engine = get_engine(digest_type); let mut digest_engine = get_engine(digest_type);
let mut item = db::Item { let mut item = db::Item {
id: None, id: None,
ts: Utc::now(), ts: Utc::now(),
@@ -163,7 +163,7 @@ pub fn mode_save(
stdout.flush()?; stdout.flush()?;
item_out.flush()?; item_out.flush()?;
// Finalize the digest and log the result // Finalize the digest and log the result
let digest = digest_engine.finalize()?; let digest = digest_engine.finalize()?;
debug!("DIGEST: {}", digest); debug!("DIGEST: {}", digest);