feat: add compression and digest support with database schema updates

This commit is contained in:
Andrew Phillips
2025-05-14 09:45:51 -03:00
committed by Andrew Phillips (aider)
parent 9b61a37036
commit bbdfe19836
19 changed files with 181 additions and 111 deletions

View File

@@ -2,7 +2,7 @@
name = "keep" name = "keep"
version = "0.1.0" version = "0.1.0"
edition = "2024" 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" readme = "README.md"
categories = ["command-line-utilities"] categories = ["command-line-utilities"]

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{Context, Result, anyhow};
use std::io; use std::io;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
@@ -128,7 +128,9 @@ lazy_static! {
}; };
} }
pub fn get_compression_engine(compression_type: CompressionType) -> Result<Box<dyn CompressionEngine>> { pub fn get_compression_engine(
compression_type: CompressionType,
) -> Result<Box<dyn CompressionEngine>> {
match compression_type { match compression_type {
CompressionType::LZ4 => Ok(Box::new(CompressionEngineLZ4::new())), CompressionType::LZ4 => Ok(Box::new(CompressionEngineLZ4::new())),
CompressionType::GZip => Ok(Box::new(CompressionEngineGZip::new())), CompressionType::GZip => Ok(Box::new(CompressionEngineGZip::new())),
@@ -144,7 +146,8 @@ pub fn get_compression_engine(compression_type: CompressionType) -> Result<Box<d
pub fn default_compression_type() -> CompressionType { pub fn default_compression_type() -> CompressionType {
let mut default = CompressionType::None; let mut default = CompressionType::None;
for compression_type in CompressionType::iter() { 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() { if compression_engine.is_supported() {
default = compression_type; default = compression_type;
break; break;

View File

@@ -5,9 +5,9 @@ use std::io;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
use flate2::Compression;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::GzEncoder; use flate2::write::GzEncoder;
use flate2::Compression;
use crate::compression_engine::CompressionEngine; use crate::compression_engine::CompressionEngine;

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{Context, Result, anyhow};
use log::*; use log::*;
use std::env; use std::env;
use std::fs; use std::fs;

View File

@@ -3,7 +3,7 @@ use chrono::prelude::*;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::*; use log::*;
use rusqlite::{Connection, OpenFlags}; use rusqlite::{Connection, OpenFlags};
use rusqlite_migration::{Migrations, M}; use rusqlite_migration::{M, Migrations};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
@@ -32,12 +32,8 @@ lazy_static! {
FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE, FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE,
PRIMARY KEY(id, name));" PRIMARY KEY(id, name));"
), ),
M::up( M::up("ALTER TABLE items ADD COLUMN digest_type TEXT NOT NULL DEFAULT 'none';"),
"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_value TEXT NULL;"
),
]); ]);
} }
@@ -97,7 +93,13 @@ pub fn update_item(conn: &Connection, item: Item) -> Result<()> {
debug!("DB: Updating item: {:?}", item); debug!("DB: Updating item: {:?}", item);
conn.execute( conn.execute(
"UPDATE items SET size=?2, compression=?3, digest_type=?4, digest_value=?5 WHERE id=?1", "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(()) Ok(())
} }

View File

@@ -64,7 +64,6 @@ lazy_static! {
}; };
} }
pub fn get_digest_engine(digest_type: DigestType) -> Box<dyn DigestEngine> { pub fn get_digest_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()),

View File

@@ -1,6 +1,6 @@
use crate::digest_engine::DigestEngine;
use anyhow::Result; use anyhow::Result;
use std::io::{self, Write}; use std::io::{self, Write};
use crate::digest_engine::DigestEngine;
#[derive(Debug, Eq, PartialEq, Clone, Default)] #[derive(Debug, Eq, PartialEq, Clone, Default)]
pub struct DigestEngineNone {} pub struct DigestEngineNone {}

View File

@@ -1,10 +1,10 @@
use anyhow::{anyhow, Result, Context}; use crate::digest_engine::ProgramWriter;
use anyhow::{Context, Result, anyhow};
use log::*; use log::*;
use std::env; use std::env;
use std::fs; use std::fs;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use crate::digest_engine::ProgramWriter;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@@ -41,10 +41,7 @@ impl DigestEngine for DigestEngineProgram {
let program = self.program.clone(); let program = self.program.clone();
let args = self.args.clone(); let args = self.args.clone();
debug!( debug!("DIGEST: Executing command: {:?} {:?}", program, args);
"DIGEST: Executing command: {:?} {:?}",
program, args
);
let mut process = Command::new(program.clone()) let mut process = Command::new(program.clone())
.args(args.clone()) .args(args.clone())

View File

@@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use std::io::Write;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::io; use std::io;
use std::io::Write;
use crate::digest_engine::DigestEngine; use crate::digest_engine::DigestEngine;

View File

@@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{anyhow, Context, Error, Result}; use anyhow::{Context, Error, Result, anyhow};
use clap::*; use clap::*;
use log::*; use log::*;
mod modes; mod modes;
@@ -102,7 +102,9 @@ struct ModeArgs {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct ItemArgs { struct ItemArgs {
#[arg(help_heading("Item Options"), short, long, conflicts_with_all(["get", "delete", "status"]))] #[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<KeyValue>, meta: Vec<KeyValue>,
#[arg(help_heading("Item Options"), long, env("KEEP_DIGEST"))] #[arg(help_heading("Item Options"), long, env("KEEP_DIGEST"))]

View File

@@ -1,4 +1,4 @@
use humansize::{FormatSizeOptions, BINARY}; use humansize::{BINARY, FormatSizeOptions};
use log::debug; use log::debug;
use prettytable::format::TableFormat; use prettytable::format::TableFormat;
use regex::Regex; use regex::Regex;

View File

@@ -1,10 +1,10 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{Context, Result, anyhow};
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use crate::db; use crate::db;
use clap::error::ErrorKind;
use clap::Command; use clap::Command;
use clap::error::ErrorKind;
use log::{debug, warn}; use log::{debug, warn};
use rusqlite::Connection; use rusqlite::Connection;

View File

@@ -3,11 +3,11 @@ use libc::c_int;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use clap::Command; use clap::Command;
use nix::Error as NixError;
use nix::fcntl::FdFlag; use nix::fcntl::FdFlag;
use nix::unistd::{close, pipe}; use nix::unistd::{close, pipe};
use nix::Error as NixError;
use std::io::Read; use std::io::Read;
use std::os::fd::FromRawFd; use std::os::fd::FromRawFd;
use std::process::Stdio; use std::process::Stdio;
@@ -133,8 +133,8 @@ pub fn mode_diff(
) { ) {
use std::io::BufWriter; use std::io::BufWriter;
let mut buffered_pipe_writer = BufWriter::new(pipe_writer_raw); let mut buffered_pipe_writer = BufWriter::new(pipe_writer_raw);
let engine = get_compression_engine(compression_type) let engine =
.expect("Unable to get compression engine"); get_compression_engine(compression_type).expect("Unable to get compression engine");
log::debug!("THREAD: Sending item to diff"); log::debug!("THREAD: Sending item to diff");
engine engine
.copy(item_path, &mut buffered_pipe_writer) .copy(item_path, &mut buffered_pipe_writer)

View File

@@ -1,12 +1,12 @@
use crate::db::Item;
use crate::modes::common::format_size; use crate::modes::common::format_size;
use anyhow::anyhow; use anyhow::anyhow;
use clap::error::ErrorKind;
use clap::Command; use clap::Command;
use clap::error::ErrorKind;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; 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::db::{get_item, get_item_last, get_item_matching};
use crate::modes::common::get_format_box_chars_no_border_line_separator; use crate::modes::common::get_format_box_chars_no_border_line_separator;
use chrono::prelude::*; use chrono::prelude::*;
@@ -123,8 +123,8 @@ fn show_item(
// Corrected logic for file_magic_cell: // Corrected logic for file_magic_cell:
// compression_type_val is already the successfully parsed CompressionType. // compression_type_val is already the successfully parsed CompressionType.
// The .expect() here will panic if get_compression_engine returns an Err. // The .expect() here will panic if get_compression_engine returns an Err.
let compression_engine = let compression_engine = get_compression_engine(compression_type_val.clone())
get_compression_engine(compression_type_val.clone()).expect("Unable to get compression engine"); .expect("Unable to get compression engine");
let magic_result = compression_engine.magic(item_path_buf.clone()); // Use cloned item_path_buf let magic_result = compression_engine.magic(item_path_buf.clone()); // Use cloned item_path_buf
let file_magic_cell = match magic_result { let file_magic_cell = match magic_result {
@@ -162,7 +162,7 @@ fn show_item(
Cell::new("Digest Value").with_style(Attr::Bold), Cell::new("Digest Value").with_style(Attr::Bold),
Cell::new(&dv_str), Cell::new(&dv_str),
])); ]));
}, }
None => { /* Do nothing if None, as per original logic */ } None => { /* Do nothing if None, as per original logic */ }
} }

View File

@@ -141,30 +141,24 @@ pub fn mode_list(
}, },
ColumnType::Compression => { ColumnType::Compression => {
Cell::new(&string_column(item.compression.to_string(), column_width)) Cell::new(&string_column(item.compression.to_string(), column_width))
}, }
ColumnType::DigestType => { ColumnType::DigestType => {
Cell::new(&string_column(item.digest_type.to_string(), column_width)) Cell::new(&string_column(item.digest_type.to_string(), column_width))
}, }
ColumnType::DigestValue => { ColumnType::DigestValue => match item.digest_value {
match item.digest_value { Some(ref value) => Cell::new(&string_column(value.to_string(), column_width)),
Some(ref value) => Cell::new(&string_column(value.to_string(), column_width)), None => Cell::new("Missing")
None => Cell::new("Missing") .with_style(Attr::ForegroundColor(color::RED))
.with_style(Attr::ForegroundColor(color::RED)) .with_style(Attr::Bold),
.with_style(Attr::Bold),
}
}, },
ColumnType::FileSize => match item_path.metadata() { ColumnType::FileSize => match item_path.metadata() {
Ok(metadata) => Cell::new_align( Ok(metadata) => Cell::new_align(
&size_column( &size_column(metadata.len(), args.options.human_readable, column_width),
metadata.len(),
args.options.human_readable,
column_width,
),
Alignment::RIGHT, Alignment::RIGHT,
), ),
Err(_) => Cell::new_align("Missing", Alignment::RIGHT) Err(_) => Cell::new_align("Missing", Alignment::RIGHT)
.with_style(Attr::ForegroundColor(color::RED)) .with_style(Attr::ForegroundColor(color::RED))
.with_style(Attr::Bold), .with_style(Attr::Bold),
}, },
ColumnType::FilePath => Cell::new(&string_column( ColumnType::FilePath => Cell::new(&string_column(
item_path.clone().into_os_string().into_string().unwrap(), item_path.clone().into_os_string().into_string().unwrap(),

View File

@@ -1,18 +1,18 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{Context, Result, anyhow};
use is_terminal::IsTerminal; use is_terminal::IsTerminal;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{self, Read}; use std::io::{self, Read};
use std::str::FromStr; use std::str::FromStr;
use clap::error::ErrorKind;
use clap::Command; use clap::Command;
use clap::error::ErrorKind;
use log::debug; use log::debug;
use std::path::PathBuf;
use rusqlite::Connection; use rusqlite::Connection;
use std::path::PathBuf;
use crate::compression_engine::{CompressionType, get_compression_engine};
use crate::db::{self}; use crate::db::{self};
use crate::digest_engine::{get_digest_engine, DigestType}; use crate::digest_engine::{DigestType, get_digest_engine};
use crate::compression_engine::{get_compression_engine, CompressionType};
use crate::modes::common::get_meta_from_env; use crate::modes::common::get_meta_from_env;
use chrono::Utc; use chrono::Utc;
@@ -133,8 +133,8 @@ pub fn mode_save(
let mut stdout = io::stdout().lock(); let mut stdout = io::stdout().lock();
let mut buffer = [0; libc::BUFSIZ as usize]; let mut buffer = [0; libc::BUFSIZ as usize];
let compression_engine = get_compression_engine(compression_type.clone()) let compression_engine =
.expect("Unable to get compression engine"); get_compression_engine(compression_type.clone()).expect("Unable to get compression engine");
let mut item_out: Box<dyn Write> = let mut item_out: Box<dyn Write> =
compression_engine compression_engine
.create(item_path.clone()) .create(item_path.clone())

View File

@@ -4,9 +4,9 @@ use std::path::PathBuf;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::compression_engine; 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::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_BOX_CHARS_NO_BORDER_LINE_SEPARATOR;
use crate::FORMAT_NO_BORDER_LINE_SEPARATOR; use crate::FORMAT_NO_BORDER_LINE_SEPARATOR;
@@ -109,9 +109,9 @@ fn build_compression_table() -> Table {
fn build_digest_table() -> Table { fn build_digest_table() -> Table {
use crate::digest_engine; 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::DIGEST_PROGRAMS;
use crate::digest_engine::DigestType;
use crate::digest_engine::program::DigestEngineProgram;
let mut digest_table = Table::new(); let mut digest_table = Table::new();
if std::io::stdout().is_terminal() { if std::io::stdout().is_terminal() {
@@ -130,15 +130,14 @@ fn build_digest_table() -> Table {
let default_type = digest_engine::default_digest_type(); let default_type = digest_engine::default_digest_type();
for digest_type in DigestType::iter() { for digest_type in DigestType::iter() {
let digest_program: DigestEngineProgram = let digest_program: DigestEngineProgram = match &DIGEST_PROGRAMS[digest_type.clone()] {
match &DIGEST_PROGRAMS[digest_type.clone()] { Some(digest_program) => digest_program.clone(),
Some(digest_program) => digest_program.clone(), None => DigestEngineProgram {
None => DigestEngineProgram { program: "".to_string(),
program: "".to_string(), args: Vec::new(),
args: Vec::new(), supported: true,
supported: true, },
}, };
};
let is_default = digest_type == default_type; let is_default = digest_type == default_type;
@@ -172,15 +171,12 @@ pub fn mode_status(
db_path: PathBuf, db_path: PathBuf,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
println!("PATHS:"); println!("PATHS:");
build_path_table(data_path, db_path) build_path_table(data_path, db_path).printstd();
.printstd();
println!(); println!();
println!("COMPRESSION:"); println!("COMPRESSION:");
build_compression_table() build_compression_table().printstd();
.printstd();
println!(); println!();
println!("DIGEST:"); println!("DIGEST:");
build_digest_table() build_digest_table().printstd();
.printstd();
Ok(()) Ok(())
} }

View File

@@ -4,10 +4,10 @@ use std::str::FromStr;
use crate::compression_engine::{CompressionType, get_compression_engine}; use crate::compression_engine::{CompressionType, get_compression_engine};
use crate::db; use crate::db;
use clap::error::ErrorKind;
use clap::Command;
use log::{debug, info};
use crate::digest_engine; use crate::digest_engine;
use clap::Command;
use clap::error::ErrorKind;
use log::{debug, info};
use rusqlite::Connection; use rusqlite::Connection;
pub fn mode_update( pub fn mode_update(
@@ -46,8 +46,8 @@ pub fn mode_update(
}; };
let compression_type = CompressionType::from_str(&item.compression)?; let compression_type = CompressionType::from_str(&item.compression)?;
let compression_engine = get_compression_engine(compression_type) let compression_engine =
.expect("Unable to get compression engine"); get_compression_engine(compression_type).expect("Unable to get compression engine");
if item.size.is_none() { if item.size.is_none() {
info!("Updating unknown stream size"); info!("Updating unknown stream size");
@@ -66,7 +66,6 @@ pub fn mode_update(
} }
} }
if item.digest_value.is_none() { if item.digest_value.is_none() {
let digest_type = digest_engine::DigestType::from_str(&item.digest_type)?; let digest_type = digest_engine::DigestType::from_str(&item.digest_type)?;

View File

@@ -118,7 +118,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} -g 1", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -129,7 +135,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} 1", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -140,7 +152,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} --get", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -151,7 +169,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} --get tag_a", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -162,7 +186,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} --get tag_b", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -173,7 +203,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 cmd = format!("{} --get tag", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -184,7 +220,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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(); 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 cmd = format!("{} --diff tag_a tag_b", keep_cmd);
let output = run_sh(cmd.as_str()); let output = run_sh(cmd.as_str());
@@ -373,7 +427,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 // Test with lz4 compression
let cmd = format!("echo {} | {} -c lz4 lz4", INPUT_A, keep_cmd); let cmd = format!("echo {} | {} -c lz4 lz4", INPUT_A, keep_cmd);
@@ -396,7 +456,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 // Test with gzip compression
let cmd = format!("echo {} | {} -c gzip gzip", INPUT_A, keep_cmd); let cmd = format!("echo {} | {} -c gzip gzip", INPUT_A, keep_cmd);
@@ -419,7 +485,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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 // Test with bzip2 compression
let cmd = format!("echo {} | {} -c bzip2 bzip2", INPUT_A, keep_cmd); let cmd = format!("echo {} | {} -c bzip2 bzip2", INPUT_A, keep_cmd);
@@ -442,7 +514,13 @@ mod tests {
output.status output.status
); );
let output_str = String::from_utf8_lossy(&output.stdout).to_string(); 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
);
}); });
} }
} }