use anyhow::{Context, Result, anyhow}; use std::fs::File; use std::io::{Read,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 CompressionEngineProgram { pub program: String, pub compress: Vec, pub decompress: Vec, pub supported: bool } impl CompressionEngineProgram { pub fn new(program: &str, compress: Vec<&str>, decompress: Vec<&str>) -> CompressionEngineProgram { let program_path = get_program_path(program); let supported = program_path.is_ok(); CompressionEngineProgram { 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 CompressionEngineProgram { fn is_supported(&self) -> bool { self.supported } fn open(&self, file_path: PathBuf) -> Result> { debug!("COMPRESSION: Opening {:?} using {:?}", file_path, *self); let program = self.program.clone(); let args = self.decompress.clone(); debug!("COMPRESSION: Executing command: {:?} {:?} reading from {:?}", program, args, file_path); let file = File::open(file_path).context("Unable to open file for reading")?; let process = Command::new(program.clone()) .args(args.clone()) .stdin(file) .stdout(Stdio::piped()) .spawn() .context(anyhow!("Unable to spawn child process: {:?} {:?}", program, args))?; Ok(Box::new(process.stdout.unwrap())) } 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 copy(&self, file_path: PathBuf, writer: &mut dyn Write) -> Result<()> { let mut reader = self.open(file_path)?; io::copy(&mut reader, writer)?; writer.flush()?; Ok(()) } } 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)) }