- [x] parallelization
    - [x] (scanning) + (processing sw & processing hw & formatting & printing)
- [x] reduce cloning values on the heap
- [x] add a partial hashing mode (--strict)
- [x] add unit tests
- [x] add silent mode
- [x] update documentation
- [x] remove color output
- [x] progress bar improvements
    - [x] use progress bar groups
- [x] remove broken json rendering
- [x] add benchmarks
This commit is contained in:
Sreedev Kodichath
2025-07-13 19:03:14 -04:00
committed by GitHub
parent f9e86f8522
commit 58e33cc8da
13 changed files with 1085 additions and 482 deletions

View File

@@ -1,16 +1,14 @@
pub struct Formatter;
use crate::fileinfo::FileInfo;
use crate::params::Params;
use crate::{fileinfo::FileInfo, params::Params};
use anyhow::Result;
use chrono::{DateTime, Utc};
use dashmap::DashMap;
use indicatif::{ProgressBar, ProgressFinish, ProgressIterator, ProgressStyle};
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressFinish, ProgressStyle};
use pathdiff::diff_paths;
use prettytable::{format, row, Table};
use std::borrow::Cow;
use std::path::PathBuf;
use std::time::Duration;
use prettytable::{format, row, Row, Table};
use rayon::prelude::*;
use std::{borrow::Cow, path::PathBuf, sync::Arc, time::Duration};
pub struct Formatter;
impl Formatter {
pub fn human_path(
file: &FileInfo,
@@ -18,7 +16,7 @@ impl Formatter {
min_path_length: usize,
) -> Result<String> {
let base_directory: PathBuf = app_args.get_directory()?;
let relative_path = diff_paths(file.path.clone(), base_directory).unwrap_or_default();
let relative_path = diff_paths(&file.path, base_directory).unwrap_or_default();
let formatted_path = format!(
"{:<0width$}",
@@ -34,44 +32,62 @@ impl Formatter {
}
pub fn human_mtime(file: &FileInfo) -> Result<String> {
let modified_time: DateTime<Utc> = file.filemeta.modified()?.into();
let modified_time: DateTime<Utc> = file.modified.into();
Ok(modified_time.format("%Y-%m-%d %H:%M:%S").to_string())
}
pub fn generate_table(raw: DashMap<String, Vec<FileInfo>>, max_path_len: usize, app_args: &Params) -> Result<Table> {
let mut output_table = Table::new();
output_table.set_titles(row!["hash", "duplicates"]);
pub fn gen_sub_tbl(items: Vec<FileInfo>, app_args: &Params, max_path_len: u64) -> Table {
let mut inner_table = Table::new();
inner_table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
items.iter().for_each(|file| {
inner_table.add_row(row![
Self::human_path(file, app_args, max_path_len as usize).unwrap_or_default(),
Self::human_filesize(file).unwrap_or_default(),
Self::human_mtime(file).unwrap_or_default()
]);
});
inner_table
}
let progress_style = ProgressStyle::with_template(
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
)?;
pub fn generate_table(
raw: Arc<DashMap<u128, Vec<FileInfo>>>,
mpath_len: u64,
args: &Params,
) -> Result<Table> {
let progress_bar = match args.progress {
true => ProgressBar::new_spinner(),
false => ProgressBar::hidden(),
};
let progress_bar = ProgressBar::new(raw.len() as u64);
let progress_style = ProgressStyle::with_template("[{elapsed_precise}] {pos:>7} {msg}")?;
progress_bar.set_style(progress_style);
progress_bar.enable_steady_tick(Duration::from_millis(50));
progress_bar.set_message("generating output");
raw.into_iter()
let rows = raw
.par_iter_mut()
.progress_with(progress_bar)
.with_finish(ProgressFinish::WithMessage(Cow::from("output generated")))
.for_each(|(hash, group)| {
let mut inner_table = Table::new();
inner_table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
group.iter().for_each(|file| {
inner_table.add_row(row![
Self::human_path(file, app_args, max_path_len).unwrap_or_default(),
Self::human_filesize(file).unwrap_or_default(),
Self::human_mtime(file).unwrap_or_default()
]);
});
output_table.add_row(row![hash, inner_table]);
});
.filter(|i| i.value().len() > 1)
.map(|i| {
row![
i.key(),
Self::gen_sub_tbl(i.value().to_vec(), args, mpath_len)
]
})
.collect::<Vec<Row>>();
let mut output_table = Table::new();
output_table.set_titles(row!["hash", "duplicates"]);
output_table.extend(rows);
Ok(output_table)
}
pub fn print(raw: DashMap<String, Vec<FileInfo>>, max_path_len: usize, app_args: &Params) -> Result<()> {
pub fn print(
raw: Arc<DashMap<u128, Vec<FileInfo>>>,
max_path_len: u64,
app_args: &Params,
) -> Result<()> {
if raw.is_empty() {
println!("\n\nNo duplicates found matching your search criteria.\n");
return Ok(());