feat: add compression and digest support with database schema updates
This commit is contained in:
committed by
Andrew Phillips (aider)
parent
9b61a37036
commit
bbdfe19836
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
18
src/db.rs
18
src/db.rs
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub trait DigestEngine {
|
|||||||
}
|
}
|
||||||
fn create(&self) -> Result<Box<dyn Write>>;
|
fn create(&self) -> Result<Box<dyn Write>>;
|
||||||
fn finalize(&mut self) -> io::Result<String>;
|
fn finalize(&mut self) -> io::Result<String>;
|
||||||
|
|
||||||
// Update the digest with new data
|
// Update the digest with new data
|
||||||
fn update(&mut self, data: &[u8]);
|
fn update(&mut self, data: &[u8]);
|
||||||
}
|
}
|
||||||
@@ -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()),
|
||||||
|
|||||||
@@ -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 {}
|
||||||
@@ -15,11 +15,11 @@ impl DigestEngine for DigestEngineNone {
|
|||||||
fn create(&self) -> Result<Box<dyn Write>> {
|
fn create(&self) -> Result<Box<dyn Write>> {
|
||||||
Ok(Box::new(DummyWriter::new()))
|
Ok(Box::new(DummyWriter::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(&mut self) -> io::Result<String> {
|
fn finalize(&mut self) -> io::Result<String> {
|
||||||
Ok("none".to_string())
|
Ok("none".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _data: &[u8]) {}
|
fn update(&mut self, _data: &[u8]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|
||||||
@@ -34,17 +34,14 @@ impl DigestEngine for DigestEngineProgram {
|
|||||||
fn is_supported(&self) -> bool {
|
fn is_supported(&self) -> bool {
|
||||||
self.supported
|
self.supported
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&self) -> Result<Box<dyn Write>> {
|
fn create(&self) -> Result<Box<dyn Write>> {
|
||||||
debug!("DIGEST: Writting using {:?}", *self);
|
debug!("DIGEST: Writting using {:?}", *self);
|
||||||
|
|
||||||
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())
|
||||||
@@ -65,7 +62,7 @@ impl DigestEngine for DigestEngineProgram {
|
|||||||
fn finalize(&mut self) -> io::Result<String> {
|
fn finalize(&mut self) -> io::Result<String> {
|
||||||
Ok("program".to_string())
|
Ok("program".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _data: &[u8]) {
|
fn update(&mut self, _data: &[u8]) {
|
||||||
// This is handled by the ProgramWriter implementation
|
// This is handled by the ProgramWriter implementation
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ impl DigestEngine for DigestEngineSha256 {
|
|||||||
let result = self.hasher.clone().finalize();
|
let result = self.hasher.clone().finalize();
|
||||||
Ok(format!("{:x}", result))
|
Ok(format!("{:x}", result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, data: &[u8]) {
|
fn update(&mut self, data: &[u8]) {
|
||||||
self.hasher.update(data);
|
self.hasher.update(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"))]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -68,7 +68,7 @@ fn build_compression_table() -> Table {
|
|||||||
b->"Decompress"));
|
b->"Decompress"));
|
||||||
|
|
||||||
let default_type = compression_engine::default_compression_type();
|
let default_type = compression_engine::default_compression_type();
|
||||||
|
|
||||||
for compression_type in CompressionType::iter() {
|
for compression_type in CompressionType::iter() {
|
||||||
let compression_program: CompressionEngineProgram =
|
let compression_program: CompressionEngineProgram =
|
||||||
match &COMPRESSION_PROGRAMS[compression_type.clone()] {
|
match &COMPRESSION_PROGRAMS[compression_type.clone()] {
|
||||||
@@ -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() {
|
||||||
@@ -128,17 +128,16 @@ fn build_digest_table() -> Table {
|
|||||||
b->"Args"));
|
b->"Args"));
|
||||||
|
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +46,9 @@ 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");
|
||||||
let item_file_metadata = item_path.metadata();
|
let item_file_metadata = item_path.metadata();
|
||||||
@@ -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)?;
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ pub fn mode_update(
|
|||||||
|
|
||||||
// Create and initialize digest engine
|
// Create and initialize digest engine
|
||||||
let mut digest_engine = digest_engine::get_digest_engine(digest_type);
|
let mut digest_engine = digest_engine::get_digest_engine(digest_type);
|
||||||
|
|
||||||
// Read file content and update digest
|
// Read file content and update digest
|
||||||
let mut reader = compression_engine.open(item_path)?;
|
let mut reader = compression_engine.open(item_path)?;
|
||||||
let mut buffer = [0; 4096];
|
let mut buffer = [0; 4096];
|
||||||
@@ -92,10 +91,10 @@ pub fn mode_update(
|
|||||||
}
|
}
|
||||||
digest_engine.update(&buffer[..bytes_read]);
|
digest_engine.update(&buffer[..bytes_read]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get final digest value
|
// Get final digest value
|
||||||
let digest_value = digest_engine.finalize()?;
|
let digest_value = digest_engine.finalize()?;
|
||||||
|
|
||||||
// Update item with new digest value
|
// Update item with new digest value
|
||||||
item.digest_value = Some(digest_value);
|
item.digest_value = Some(digest_value);
|
||||||
db::update_item(conn, item.clone())?;
|
db::update_item(conn, item.clone())?;
|
||||||
|
|||||||
104
src/tests.rs
104
src/tests.rs
@@ -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
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user