From cf352af7d597b59731a0dfae34690f5966ae34c1 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Fri, 1 Sep 2023 21:01:06 +0000 Subject: [PATCH] Refactor compression into multiple files --- src/compression.rs | 191 +++---------------------------------- src/compression/lz4.rs | 47 +++++++++ src/compression/none.rs | 39 ++++++++ src/compression/program.rs | 104 ++++++++++++++++++++ src/main.rs | 9 +- 5 files changed, 210 insertions(+), 180 deletions(-) create mode 100644 src/compression/lz4.rs create mode 100644 src/compression/none.rs create mode 100644 src/compression/program.rs diff --git a/src/compression.rs b/src/compression.rs index 932e7fa..2ed52f0 100755 --- a/src/compression.rs +++ b/src/compression.rs @@ -1,19 +1,20 @@ -use anyhow::{Context, Result, anyhow}; +use anyhow::{Result}; use strum::IntoEnumIterator; -use std::fs::File; -use std::io; -use std::io::Write; -use std::process::{Command,Stdio}; use std::path::PathBuf; -use std::env; -use std::fs; -use std::os::unix::fs::PermissionsExt; -use log::*; +use std::io::Write; + use lazy_static::lazy_static; extern crate enum_map; use enum_map::enum_map; use enum_map::{EnumMap,Enum}; +pub mod none; +pub mod lz4; +pub mod program; + +use none::CompressionEngineNone; +use lz4::CompressionEngineLZ4; +use program::CompressionProgram; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)] #[strum(ascii_case_insensitive)] @@ -27,12 +28,10 @@ pub enum CompressionType { } -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct CompressionProgram { - pub program: String, - pub compress: Vec, - pub decompress: Vec, - pub supported: bool +pub trait CompressionEngine { + fn is_supported(&self) -> bool; + fn cat(&self, file_path: PathBuf) -> Result<()>; + fn create(&self, file_path: PathBuf) -> Result>; } @@ -47,168 +46,6 @@ lazy_static! { }; } -impl CompressionProgram { - pub fn new(program: &str, compress: Vec<&str>, decompress: Vec<&str>) -> CompressionProgram { - let program_path = get_program_path(program); - let supported = program_path.is_ok(); - - CompressionProgram { - program: program_path.unwrap_or(program.to_string()), - compress: compress.iter().map(|s| {s.to_string()}).collect(), - decompress: decompress.iter().map(|s| {s.to_string()}).collect(), - supported - } - } -} - -pub trait CompressionEngine { - fn is_supported(&self) -> bool; - fn cat(&self, file_path: PathBuf) -> Result<()>; - fn create(&self, file_path: PathBuf) -> Result>; -} - -impl CompressionEngine for CompressionProgram { - fn is_supported(&self) -> bool { - self.supported - } - - fn cat(&self, file_path: PathBuf) -> Result<()> { - debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); - let program = self.program.clone(); - let args = self.decompress.clone(); - - debug!("COMPRESSION: Executing command: {:?} {:?} writing to {:?}", program, args, file_path); - - let file = File::open(file_path).context("Unable to open file for reading")?; - - let mut process = Command::new(program.clone()) - .args(args.clone()) - .stdin(file) - .spawn() - .context(anyhow!("Unable to spawn child process: {:?} {:?}", program, args))?; - - let result = process.wait() - .context(anyhow!("Unable to wait for child process: {:?} {:?}", program, args))?; - - if result.success() { - Ok(()) - } else { - Err(anyhow!("Decompression program returned {}", result)) - } - } - - fn create(&self, file_path: PathBuf) -> Result> { - debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); - - let program = self.program.clone(); - let args = self.compress.clone(); - - debug!("COMPRESSION: Executing command: {:?} {:?} writing to {:?}", program, args, file_path); - - let file = File::create(file_path).context("Unable to open file for writing")?; - - let process = Command::new(program.clone()) - .args(args.clone()) - .stdin(Stdio::piped()) - .stdout(file) - .spawn() - .context(anyhow!("Problem spawning child process: {:?} {:?}", program, args))?; - - Ok(Box::new(process.stdin.unwrap())) - } -} - - - -#[derive(Debug, Eq, PartialEq, Clone, Default)] -pub struct CompressionEngineNone {} - -impl CompressionEngineNone { - pub fn new() -> CompressionEngineNone { - CompressionEngineNone {} - } -} - -impl CompressionEngine for CompressionEngineNone { - fn is_supported(&self) -> bool { - true - } - - fn cat(&self, file_path: PathBuf) -> Result<()> { - debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); - let mut stdout = io::stdout().lock(); - let mut file = File::open(file_path)?; - - io::copy(&mut file, &mut stdout)?; - stdout.flush()?; - - Ok(()) - } - - fn create(&self, file_path: PathBuf) -> Result> { - debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); - Ok(Box::new(File::create(file_path)?)) - } -} - - - -#[derive(Debug, Eq, PartialEq, Clone, Default)] -pub struct CompressionEngineLZ4 {} - -impl CompressionEngineLZ4 { - pub fn new() -> CompressionEngineLZ4 { - CompressionEngineLZ4 {} - } -} - -impl CompressionEngine for CompressionEngineLZ4 { - fn is_supported(&self) -> bool { - true - } - - fn cat(&self, file_path: PathBuf) -> Result<()> { - debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); - - let mut stdout = io::stdout().lock(); - let file = File::open(file_path)?; - let mut lz4_read = lz4_flex::frame::FrameDecoder::new(file); - - io::copy(&mut lz4_read, &mut stdout)?; - stdout.flush()?; - - Ok(()) - } - - fn create(&self, file_path: PathBuf) -> Result> { - debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); - - let file = File::create(file_path)?; - let lz4_write = lz4_flex::frame::FrameEncoder::new(file).auto_finish(); - - Ok(Box::new(lz4_write)) - } -} - - -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)) -} - pub fn get_engine(compression_type: CompressionType) -> Result> { match compression_type { diff --git a/src/compression/lz4.rs b/src/compression/lz4.rs new file mode 100644 index 0000000..d4559ad --- /dev/null +++ b/src/compression/lz4.rs @@ -0,0 +1,47 @@ +use anyhow::Result; +use std::fs::File; +use std::io; +use std::io::Write; +use std::path::PathBuf; +use log::*; +extern crate enum_map; + +use crate::compression::CompressionEngine; + +#[derive(Debug, Eq, PartialEq, Clone, Default)] +pub struct CompressionEngineLZ4 {} + +impl CompressionEngineLZ4 { + pub fn new() -> CompressionEngineLZ4 { + CompressionEngineLZ4 {} + } +} + +impl CompressionEngine for CompressionEngineLZ4 { + fn is_supported(&self) -> bool { + true + } + + fn cat(&self, file_path: PathBuf) -> Result<()> { + debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); + + let mut stdout = io::stdout().lock(); + let file = File::open(file_path)?; + let mut lz4_read = lz4_flex::frame::FrameDecoder::new(file); + + io::copy(&mut lz4_read, &mut stdout)?; + stdout.flush()?; + + Ok(()) + } + + fn create(&self, file_path: PathBuf) -> Result> { + debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); + + let file = File::create(file_path)?; + let lz4_write = lz4_flex::frame::FrameEncoder::new(file).auto_finish(); + + Ok(Box::new(lz4_write)) + } +} + diff --git a/src/compression/none.rs b/src/compression/none.rs new file mode 100644 index 0000000..d95ebad --- /dev/null +++ b/src/compression/none.rs @@ -0,0 +1,39 @@ +use anyhow::Result; +use std::fs::File; +use std::io; +use std::io::Write; +use std::path::PathBuf; +use log::*; + +use crate::compression::CompressionEngine; + +#[derive(Debug, Eq, PartialEq, Clone, Default)] +pub struct CompressionEngineNone {} + +impl CompressionEngineNone { + pub fn new() -> CompressionEngineNone { + CompressionEngineNone {} + } +} + +impl CompressionEngine for CompressionEngineNone { + fn is_supported(&self) -> bool { + true + } + + fn cat(&self, file_path: PathBuf) -> Result<()> { + debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); + let mut stdout = io::stdout().lock(); + let mut file = File::open(file_path)?; + + io::copy(&mut file, &mut stdout)?; + stdout.flush()?; + + Ok(()) + } + + fn create(&self, file_path: PathBuf) -> Result> { + debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); + Ok(Box::new(File::create(file_path)?)) + } +} diff --git a/src/compression/program.rs b/src/compression/program.rs new file mode 100644 index 0000000..2cb679d --- /dev/null +++ b/src/compression/program.rs @@ -0,0 +1,104 @@ +use anyhow::{Context, Result, anyhow}; +use std::fs::File; +use std::io::Write; +use std::process::{Command,Stdio}; +use std::path::PathBuf; +use std::env; +use std::fs; +use std::os::unix::fs::PermissionsExt; +use log::*; + +use crate::compression::CompressionEngine; + + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct CompressionProgram { + pub program: String, + pub compress: Vec, + pub decompress: Vec, + pub supported: bool +} + + +impl CompressionProgram { + pub fn new(program: &str, compress: Vec<&str>, decompress: Vec<&str>) -> CompressionProgram { + let program_path = get_program_path(program); + let supported = program_path.is_ok(); + + CompressionProgram { + program: program_path.unwrap_or(program.to_string()), + compress: compress.iter().map(|s| {s.to_string()}).collect(), + decompress: decompress.iter().map(|s| {s.to_string()}).collect(), + supported + } + } +} + +impl CompressionEngine for CompressionProgram { + fn is_supported(&self) -> bool { + self.supported + } + + fn cat(&self, file_path: PathBuf) -> Result<()> { + debug!("COMPRESSION: Outputting {:?} to STDOUT using {:?}", file_path, *self); + let program = self.program.clone(); + let args = self.decompress.clone(); + + debug!("COMPRESSION: Executing command: {:?} {:?} writing to {:?}", program, args, file_path); + + let file = File::open(file_path).context("Unable to open file for reading")?; + + let mut process = Command::new(program.clone()) + .args(args.clone()) + .stdin(file) + .spawn() + .context(anyhow!("Unable to spawn child process: {:?} {:?}", program, args))?; + + let result = process.wait() + .context(anyhow!("Unable to wait for child process: {:?} {:?}", program, args))?; + + if result.success() { + Ok(()) + } else { + Err(anyhow!("Decompression program returned {}", result)) + } + } + + fn create(&self, file_path: PathBuf) -> Result> { + debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); + + let program = self.program.clone(); + let args = self.compress.clone(); + + debug!("COMPRESSION: Executing command: {:?} {:?} writing to {:?}", program, args, file_path); + + let file = File::create(file_path).context("Unable to open file for writing")?; + + let process = Command::new(program.clone()) + .args(args.clone()) + .stdin(Stdio::piped()) + .stdout(file) + .spawn() + .context(anyhow!("Problem spawning child process: {:?} {:?}", program, args))?; + + Ok(Box::new(process.stdin.unwrap())) + } +} + +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/main.rs b/src/main.rs index bdad4ee..9d5a713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,10 +29,13 @@ use chrono::prelude::*; #[macro_use] extern crate lazy_static; -use crate::compression::CompressionType; pub mod compression; pub mod db; +use compression::CompressionType; +use compression::program::CompressionProgram; + + use humansize::{format_size, BINARY}; use is_terminal::IsTerminal; @@ -651,9 +654,9 @@ fn mode_status(_cmd: &mut Command, args: Args, data_path: PathBuf, db_path: Path }; for compression_type in CompressionType::iter() { - let compression_program: compression::CompressionProgram = match &compression::COMPRESSION_PROGRAMS[compression_type.clone()] { + let compression_program: CompressionProgram = match &compression::COMPRESSION_PROGRAMS[compression_type.clone()] { Some(compression_program) => compression_program.clone(), - None => compression::CompressionProgram { + None => CompressionProgram { program: "".to_string(), compress: Vec::new(), decompress: Vec::new(),