From bbdfe1983654c61ce98211d15ea613e30dbc9cc7 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 14 May 2025 09:45:51 -0300 Subject: [PATCH] feat: add compression and digest support with database schema updates --- Cargo.toml | 2 +- src/compression_engine.rs | 9 ++- src/compression_engine/gzip.rs | 2 +- src/compression_engine/program.rs | 2 +- src/db.rs | 18 +++--- src/digest_engine.rs | 3 +- src/digest_engine/none.rs | 6 +- src/digest_engine/program.rs | 13 ++-- src/digest_engine/sha2.rs | 4 +- src/main.rs | 6 +- src/modes/common.rs | 2 +- src/modes/delete.rs | 4 +- src/modes/diff.rs | 8 +-- src/modes/info.rs | 12 ++-- src/modes/list.rs | 26 +++----- src/modes/save.rs | 14 ++-- src/modes/status.rs | 38 +++++------ src/modes/update.rs | 19 +++--- src/tests.rs | 104 ++++++++++++++++++++++++++---- 19 files changed, 181 insertions(+), 111 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6944281..c5ef965 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "keep" version = "0.1.0" edition = "2024" -description = "Keep and manage temporary files with automatic metadata generation" +description = "Keep and manage temporary files with automatic compression and metadata generation" readme = "README.md" categories = ["command-line-utilities"] diff --git a/src/compression_engine.rs b/src/compression_engine.rs index ebe99d9..98aa84b 100755 --- a/src/compression_engine.rs +++ b/src/compression_engine.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use std::io; use std::io::{Read, Write}; use std::path::PathBuf; @@ -128,7 +128,9 @@ lazy_static! { }; } -pub fn get_compression_engine(compression_type: CompressionType) -> Result> { +pub fn get_compression_engine( + compression_type: CompressionType, +) -> Result> { match compression_type { CompressionType::LZ4 => Ok(Box::new(CompressionEngineLZ4::new())), CompressionType::GZip => Ok(Box::new(CompressionEngineGZip::new())), @@ -144,7 +146,8 @@ pub fn get_compression_engine(compression_type: CompressionType) -> Result CompressionType { let mut default = CompressionType::None; for compression_type in CompressionType::iter() { - let compression_engine = get_compression_engine(compression_type.clone()).expect("Missing engine"); + let compression_engine = + get_compression_engine(compression_type.clone()).expect("Missing engine"); if compression_engine.is_supported() { default = compression_type; break; diff --git a/src/compression_engine/gzip.rs b/src/compression_engine/gzip.rs index 5449e4b..b205b84 100644 --- a/src/compression_engine/gzip.rs +++ b/src/compression_engine/gzip.rs @@ -5,9 +5,9 @@ use std::io; use std::io::{Read, Write}; use std::path::PathBuf; +use flate2::Compression; use flate2::read::GzDecoder; use flate2::write::GzEncoder; -use flate2::Compression; use crate::compression_engine::CompressionEngine; diff --git a/src/compression_engine/program.rs b/src/compression_engine/program.rs index a447e24..fda5dee 100644 --- a/src/compression_engine/program.rs +++ b/src/compression_engine/program.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use log::*; use std::env; use std::fs; diff --git a/src/db.rs b/src/db.rs index 5eb8f35..28af934 100644 --- a/src/db.rs +++ b/src/db.rs @@ -3,7 +3,7 @@ use chrono::prelude::*; use lazy_static::lazy_static; use log::*; use rusqlite::{Connection, OpenFlags}; -use rusqlite_migration::{Migrations, M}; +use rusqlite_migration::{M, Migrations}; use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; @@ -32,12 +32,8 @@ lazy_static! { FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE, PRIMARY KEY(id, name));" ), - M::up( - "ALTER TABLE items ADD COLUMN digest_type TEXT NOT NULL DEFAULT 'none';" - ), - M::up( - "ALTER TABLE items ADD COLUMN digest_value TEXT NULL;" - ), + M::up("ALTER TABLE items ADD COLUMN digest_type TEXT NOT NULL DEFAULT 'none';"), + M::up("ALTER TABLE items ADD COLUMN digest_value TEXT NULL;"), ]); } @@ -97,7 +93,13 @@ pub fn update_item(conn: &Connection, item: Item) -> Result<()> { debug!("DB: Updating item: {:?}", item); conn.execute( "UPDATE items SET size=?2, compression=?3, digest_type=?4, digest_value=?5 WHERE id=?1", - (item.id, item.size, item.compression, item.digest_type, item.digest_value), + ( + item.id, + item.size, + item.compression, + item.digest_type, + item.digest_value, + ), )?; Ok(()) } diff --git a/src/digest_engine.rs b/src/digest_engine.rs index 1d47af9..e3ad9a9 100644 --- a/src/digest_engine.rs +++ b/src/digest_engine.rs @@ -31,7 +31,7 @@ pub trait DigestEngine { } fn create(&self) -> Result>; fn finalize(&mut self) -> io::Result; - + // Update the digest with new data fn update(&mut self, data: &[u8]); } @@ -64,7 +64,6 @@ lazy_static! { }; } - pub fn get_digest_engine(digest_type: DigestType) -> Box { match digest_type { DigestType::Sha256 => Box::new(DigestEngineSha256::new()), diff --git a/src/digest_engine/none.rs b/src/digest_engine/none.rs index 647bc21..c6e5606 100644 --- a/src/digest_engine/none.rs +++ b/src/digest_engine/none.rs @@ -1,6 +1,6 @@ +use crate::digest_engine::DigestEngine; use anyhow::Result; use std::io::{self, Write}; -use crate::digest_engine::DigestEngine; #[derive(Debug, Eq, PartialEq, Clone, Default)] pub struct DigestEngineNone {} @@ -15,11 +15,11 @@ impl DigestEngine for DigestEngineNone { fn create(&self) -> Result> { Ok(Box::new(DummyWriter::new())) } - + fn finalize(&mut self) -> io::Result { Ok("none".to_string()) } - + fn update(&mut self, _data: &[u8]) {} } diff --git a/src/digest_engine/program.rs b/src/digest_engine/program.rs index c126bff..0f94b92 100644 --- a/src/digest_engine/program.rs +++ b/src/digest_engine/program.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Result, Context}; +use crate::digest_engine::ProgramWriter; +use anyhow::{Context, Result, anyhow}; use log::*; use std::env; use std::fs; use std::io; use std::io::Write; -use crate::digest_engine::ProgramWriter; use std::os::unix::fs::PermissionsExt; use std::process::{Command, Stdio}; @@ -34,17 +34,14 @@ impl DigestEngine for DigestEngineProgram { fn is_supported(&self) -> bool { self.supported } - + fn create(&self) -> Result> { debug!("DIGEST: Writting using {:?}", *self); let program = self.program.clone(); let args = self.args.clone(); - debug!( - "DIGEST: Executing command: {:?} {:?}", - program, args - ); + debug!("DIGEST: Executing command: {:?} {:?}", program, args); let mut process = Command::new(program.clone()) .args(args.clone()) @@ -65,7 +62,7 @@ impl DigestEngine for DigestEngineProgram { fn finalize(&mut self) -> io::Result { Ok("program".to_string()) } - + fn update(&mut self, _data: &[u8]) { // This is handled by the ProgramWriter implementation } diff --git a/src/digest_engine/sha2.rs b/src/digest_engine/sha2.rs index 14ac4ed..a28ed49 100644 --- a/src/digest_engine/sha2.rs +++ b/src/digest_engine/sha2.rs @@ -1,7 +1,7 @@ use anyhow::Result; -use std::io::Write; use sha2::{Digest, Sha256}; use std::io; +use std::io::Write; use crate::digest_engine::DigestEngine; @@ -54,7 +54,7 @@ impl DigestEngine for DigestEngineSha256 { let result = self.hasher.clone().finalize(); Ok(format!("{:x}", result)) } - + fn update(&mut self, data: &[u8]) { self.hasher.update(data); } diff --git a/src/main.rs b/src/main.rs index ba1e7a9..7e0e486 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use anyhow::{anyhow, Context, Error, Result}; +use anyhow::{Context, Error, Result, anyhow}; use clap::*; use log::*; mod modes; @@ -102,7 +102,9 @@ struct ModeArgs { #[derive(Parser, Debug)] struct ItemArgs { #[arg(help_heading("Item Options"), short, long, conflicts_with_all(["get", "delete", "status"]))] - #[arg(help("Set metadata for the item using the format KEY=[VALUE], the metadata will be removed if VALUE is not provided"))] + #[arg(help( + "Set metadata for the item using the format KEY=[VALUE], the metadata will be removed if VALUE is not provided" + ))] meta: Vec, #[arg(help_heading("Item Options"), long, env("KEEP_DIGEST"))] diff --git a/src/modes/common.rs b/src/modes/common.rs index 49c406f..b876755 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -1,4 +1,4 @@ -use humansize::{FormatSizeOptions, BINARY}; +use humansize::{BINARY, FormatSizeOptions}; use log::debug; use prettytable::format::TableFormat; use regex::Regex; diff --git a/src/modes/delete.rs b/src/modes/delete.rs index 0ca67a2..635a48f 100644 --- a/src/modes/delete.rs +++ b/src/modes/delete.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use std::fs; use std::path::PathBuf; use crate::db; -use clap::error::ErrorKind; use clap::Command; +use clap::error::ErrorKind; use log::{debug, warn}; use rusqlite::Connection; diff --git a/src/modes/diff.rs b/src/modes/diff.rs index edcc8a5..924c1b3 100644 --- a/src/modes/diff.rs +++ b/src/modes/diff.rs @@ -3,11 +3,11 @@ use libc::c_int; use std::path::PathBuf; use std::str::FromStr; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use clap::Command; +use nix::Error as NixError; use nix::fcntl::FdFlag; use nix::unistd::{close, pipe}; -use nix::Error as NixError; use std::io::Read; use std::os::fd::FromRawFd; use std::process::Stdio; @@ -133,8 +133,8 @@ pub fn mode_diff( ) { use std::io::BufWriter; let mut buffered_pipe_writer = BufWriter::new(pipe_writer_raw); - let engine = get_compression_engine(compression_type) - .expect("Unable to get compression engine"); + let engine = + get_compression_engine(compression_type).expect("Unable to get compression engine"); log::debug!("THREAD: Sending item to diff"); engine .copy(item_path, &mut buffered_pipe_writer) diff --git a/src/modes/info.rs b/src/modes/info.rs index eb5f484..994644a 100644 --- a/src/modes/info.rs +++ b/src/modes/info.rs @@ -1,12 +1,12 @@ +use crate::db::Item; use crate::modes::common::format_size; use anyhow::anyhow; -use clap::error::ErrorKind; use clap::Command; +use clap::error::ErrorKind; use std::path::PathBuf; use std::str::FromStr; -use crate::db::Item; -use crate::compression_engine::{get_compression_engine, CompressionType}; +use crate::compression_engine::{CompressionType, get_compression_engine}; use crate::db::{get_item, get_item_last, get_item_matching}; use crate::modes::common::get_format_box_chars_no_border_line_separator; use chrono::prelude::*; @@ -123,8 +123,8 @@ fn show_item( // Corrected logic for file_magic_cell: // compression_type_val is already the successfully parsed CompressionType. // The .expect() here will panic if get_compression_engine returns an Err. - let compression_engine = - get_compression_engine(compression_type_val.clone()).expect("Unable to get compression engine"); + let compression_engine = get_compression_engine(compression_type_val.clone()) + .expect("Unable to get compression engine"); let magic_result = compression_engine.magic(item_path_buf.clone()); // Use cloned item_path_buf let file_magic_cell = match magic_result { @@ -162,7 +162,7 @@ fn show_item( Cell::new("Digest Value").with_style(Attr::Bold), Cell::new(&dv_str), ])); - }, + } None => { /* Do nothing if None, as per original logic */ } } diff --git a/src/modes/list.rs b/src/modes/list.rs index 516c978..c74cd33 100644 --- a/src/modes/list.rs +++ b/src/modes/list.rs @@ -141,30 +141,24 @@ pub fn mode_list( }, ColumnType::Compression => { Cell::new(&string_column(item.compression.to_string(), column_width)) - }, + } ColumnType::DigestType => { Cell::new(&string_column(item.digest_type.to_string(), column_width)) - }, - ColumnType::DigestValue => { - match item.digest_value { - Some(ref value) => Cell::new(&string_column(value.to_string(), column_width)), - None => Cell::new("Missing") - .with_style(Attr::ForegroundColor(color::RED)) - .with_style(Attr::Bold), - } + } + ColumnType::DigestValue => match item.digest_value { + Some(ref value) => Cell::new(&string_column(value.to_string(), column_width)), + None => Cell::new("Missing") + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }, ColumnType::FileSize => match item_path.metadata() { Ok(metadata) => Cell::new_align( - &size_column( - metadata.len(), - args.options.human_readable, - column_width, - ), + &size_column(metadata.len(), args.options.human_readable, column_width), Alignment::RIGHT, ), Err(_) => Cell::new_align("Missing", Alignment::RIGHT) - .with_style(Attr::ForegroundColor(color::RED)) - .with_style(Attr::Bold), + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }, ColumnType::FilePath => Cell::new(&string_column( item_path.clone().into_os_string().into_string().unwrap(), diff --git a/src/modes/save.rs b/src/modes/save.rs index 344c278..56dd6ba 100644 --- a/src/modes/save.rs +++ b/src/modes/save.rs @@ -1,18 +1,18 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use is_terminal::IsTerminal; use std::collections::HashMap; use std::io::{self, Read}; use std::str::FromStr; -use clap::error::ErrorKind; use clap::Command; +use clap::error::ErrorKind; use log::debug; -use std::path::PathBuf; use rusqlite::Connection; +use std::path::PathBuf; +use crate::compression_engine::{CompressionType, get_compression_engine}; use crate::db::{self}; -use crate::digest_engine::{get_digest_engine, DigestType}; -use crate::compression_engine::{get_compression_engine, CompressionType}; +use crate::digest_engine::{DigestType, get_digest_engine}; use crate::modes::common::get_meta_from_env; use chrono::Utc; @@ -133,8 +133,8 @@ pub fn mode_save( let mut stdout = io::stdout().lock(); let mut buffer = [0; libc::BUFSIZ as usize]; - let compression_engine = get_compression_engine(compression_type.clone()) - .expect("Unable to get compression engine"); + let compression_engine = + get_compression_engine(compression_type.clone()).expect("Unable to get compression engine"); let mut item_out: Box = compression_engine .create(item_path.clone()) diff --git a/src/modes/status.rs b/src/modes/status.rs index ff51799..9b2c02a 100644 --- a/src/modes/status.rs +++ b/src/modes/status.rs @@ -4,9 +4,9 @@ use std::path::PathBuf; use strum::IntoEnumIterator; use crate::compression_engine; -use crate::compression_engine::program::CompressionEngineProgram; -use crate::compression_engine::CompressionType; use crate::compression_engine::COMPRESSION_PROGRAMS; +use crate::compression_engine::CompressionType; +use crate::compression_engine::program::CompressionEngineProgram; use crate::FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR; use crate::FORMAT_NO_BORDER_LINE_SEPARATOR; @@ -68,7 +68,7 @@ fn build_compression_table() -> Table { b->"Decompress")); let default_type = compression_engine::default_compression_type(); - + for compression_type in CompressionType::iter() { let compression_program: CompressionEngineProgram = match &COMPRESSION_PROGRAMS[compression_type.clone()] { @@ -109,9 +109,9 @@ fn build_compression_table() -> Table { fn build_digest_table() -> Table { use crate::digest_engine; - use crate::digest_engine::program::DigestEngineProgram; - use crate::digest_engine::DigestType; use crate::digest_engine::DIGEST_PROGRAMS; + use crate::digest_engine::DigestType; + use crate::digest_engine::program::DigestEngineProgram; let mut digest_table = Table::new(); if std::io::stdout().is_terminal() { @@ -128,17 +128,16 @@ fn build_digest_table() -> Table { b->"Args")); let default_type = digest_engine::default_digest_type(); - + for digest_type in DigestType::iter() { - let digest_program: DigestEngineProgram = - match &DIGEST_PROGRAMS[digest_type.clone()] { - Some(digest_program) => digest_program.clone(), - None => DigestEngineProgram { - program: "".to_string(), - args: Vec::new(), - supported: true, - }, - }; + let digest_program: DigestEngineProgram = match &DIGEST_PROGRAMS[digest_type.clone()] { + Some(digest_program) => digest_program.clone(), + None => DigestEngineProgram { + program: "".to_string(), + args: Vec::new(), + supported: true, + }, + }; let is_default = digest_type == default_type; @@ -172,15 +171,12 @@ pub fn mode_status( db_path: PathBuf, ) -> Result<(), anyhow::Error> { println!("PATHS:"); - build_path_table(data_path, db_path) - .printstd(); + build_path_table(data_path, db_path).printstd(); println!(); println!("COMPRESSION:"); - build_compression_table() - .printstd(); + build_compression_table().printstd(); println!(); println!("DIGEST:"); - build_digest_table() - .printstd(); + build_digest_table().printstd(); Ok(()) } diff --git a/src/modes/update.rs b/src/modes/update.rs index 7bdb34b..506202e 100644 --- a/src/modes/update.rs +++ b/src/modes/update.rs @@ -4,10 +4,10 @@ use std::str::FromStr; use crate::compression_engine::{CompressionType, get_compression_engine}; use crate::db; -use clap::error::ErrorKind; -use clap::Command; -use log::{debug, info}; use crate::digest_engine; +use clap::Command; +use clap::error::ErrorKind; +use log::{debug, info}; use rusqlite::Connection; pub fn mode_update( @@ -46,9 +46,9 @@ pub fn mode_update( }; let compression_type = CompressionType::from_str(&item.compression)?; - let compression_engine = get_compression_engine(compression_type) - .expect("Unable to get compression engine"); - + let compression_engine = + get_compression_engine(compression_type).expect("Unable to get compression engine"); + if item.size.is_none() { info!("Updating unknown stream size"); let item_file_metadata = item_path.metadata(); @@ -66,7 +66,6 @@ pub fn mode_update( } } - if item.digest_value.is_none() { let digest_type = digest_engine::DigestType::from_str(&item.digest_type)?; @@ -81,7 +80,7 @@ pub fn mode_update( // Create and initialize digest engine let mut digest_engine = digest_engine::get_digest_engine(digest_type); - + // Read file content and update digest let mut reader = compression_engine.open(item_path)?; let mut buffer = [0; 4096]; @@ -92,10 +91,10 @@ pub fn mode_update( } digest_engine.update(&buffer[..bytes_read]); } - + // Get final digest value let digest_value = digest_engine.finalize()?; - + // Update item with new digest value item.digest_value = Some(digest_value); db::update_item(conn, item.clone())?; diff --git a/src/tests.rs b/src/tests.rs index a9d44fc..6a562d5 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -118,7 +118,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); let cmd = format!("{} -g 1", keep_cmd); let output = run_sh(cmd.as_str()); @@ -129,7 +135,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); let cmd = format!("{} 1", keep_cmd); let output = run_sh(cmd.as_str()); @@ -140,7 +152,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); let cmd = format!("{} --get", keep_cmd); let output = run_sh(cmd.as_str()); @@ -151,7 +169,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_B), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_B); + assert!( + output_str.contains(INPUT_B), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_B + ); let cmd = format!("{} --get tag_a", keep_cmd); let output = run_sh(cmd.as_str()); @@ -162,7 +186,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); let cmd = format!("{} --get tag_b", keep_cmd); let output = run_sh(cmd.as_str()); @@ -173,7 +203,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_B), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_B); + assert!( + output_str.contains(INPUT_B), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_B + ); let cmd = format!("{} --get tag", keep_cmd); let output = run_sh(cmd.as_str()); @@ -184,7 +220,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_B), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_B); + assert!( + output_str.contains(INPUT_B), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_B + ); }); } @@ -292,9 +334,21 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_B), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_B); + assert!( + output_str.contains(INPUT_B), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_B + ); let cmd = format!("{} --diff tag_a tag_b", keep_cmd); let output = run_sh(cmd.as_str()); @@ -373,7 +427,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); // Test with lz4 compression let cmd = format!("echo {} | {} -c lz4 lz4", INPUT_A, keep_cmd); @@ -396,7 +456,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); // Test with gzip compression let cmd = format!("echo {} | {} -c gzip gzip", INPUT_A, keep_cmd); @@ -419,7 +485,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); // Test with bzip2 compression let cmd = format!("echo {} | {} -c bzip2 bzip2", INPUT_A, keep_cmd); @@ -442,7 +514,13 @@ mod tests { output.status ); let output_str = String::from_utf8_lossy(&output.stdout).to_string(); - assert!(output_str.contains(INPUT_A), "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", cmd, output_str, INPUT_A); + assert!( + output_str.contains(INPUT_A), + "Command output does not contain expected string. Command: {} Output: {} Expected: \"{}\"", + cmd, + output_str, + INPUT_A + ); }); } }