From 0f33b4d6b52bad3a0352fae58f49553a203f3b4b Mon Sep 17 00:00:00 2001 From: sreedev Date: Mon, 9 Jan 2023 11:13:29 -0500 Subject: [PATCH 1/4] added interactive param --- src/params.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/params.rs b/src/params.rs index 0148b9b..d5f79f6 100644 --- a/src/params.rs +++ b/src/params.rs @@ -12,9 +12,12 @@ pub struct Params { /// Run Deduplicator on dir different from pwd #[arg(long)] pub dir: Option, - /// Don't use cache for indexing files (default = true) + /// Don't use cache for indexing files (default = false) #[arg(long, short)] pub nocache: bool, + /// Delete files interactively + #[arg(long, short)] + pub interactive: bool } impl Params { From 3fd4869869dff78b3796a4382cce8c84ad573db7 Mon Sep 17 00:00:00 2001 From: sreedev Date: Tue, 10 Jan 2023 20:13:45 -0500 Subject: [PATCH 2/4] UI setup complete --- src/app/file_manager.rs | 7 +++ src/app/mod.rs | 9 +++- src/output.rs | 97 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 src/app/file_manager.rs diff --git a/src/app/file_manager.rs b/src/app/file_manager.rs new file mode 100644 index 0000000..9fdd0bb --- /dev/null +++ b/src/app/file_manager.rs @@ -0,0 +1,7 @@ +use crate::database::File; +use anyhow::Result; + +pub fn delete_files(files: Vec) -> Result<()> { + println!("{:?} DELETED", files); + Ok(()) +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 3a8b102..aa85541 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,7 +1,10 @@ +#![allow(unused)] + mod event_handler; mod events; mod formatter; mod ui; +pub mod file_manager; use std::{io, thread, time::Duration}; @@ -32,7 +35,11 @@ impl App { // Self::init_render_loop(&mut term)?; // Self::cleanup(&mut term)?; - output::print(duplicates, app_args); /* TODO: APP TUI INIT FUNCTION */ + match app_args.interactive { + true => output::interactive(duplicates, app_args), + false => output::print(duplicates, app_args) /* TODO: APP TUI INIT FUNCTION */ + } + Ok(()) } diff --git a/src/output.rs b/src/output.rs index 0fedfea..b70070c 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,14 +1,17 @@ -use std::{collections::HashMap, fs}; +use std::{collections::HashMap, fs, io}; +use std::io::Write; use anyhow::Result; use chrono::offset::Utc; use chrono::DateTime; use colored::Colorize; use humansize::{format_size, DECIMAL}; +use itertools::Itertools; +use crate::app::file_manager; use crate::database::File; use crate::params::Params; -use prettytable::{row, Cell, Row, format, Table}; +use prettytable::{format, row, Cell, Row, Table}; fn format_path(path: &str, opts: &Params) -> Result { let display_path = path.replace(&opts.get_directory()?, ""); @@ -50,16 +53,102 @@ fn group_duplicates(duplicates: Vec) -> HashMap> { duplicate_mapper } +fn print_meta_info(duplicates: &Vec, opts: &Params) { + println!("Deduplicator v{}", std::env!("CARGO_PKG_VERSION")); +} + +fn scan_group_instruction() -> Result { + println!("\nEnter the indices of the files you want to delete."); + println!("You can enter multiple files using commas to seperate file indices."); + println!("example: 1,2"); + print!("\n> "); + std::io::stdout().flush()?; + let mut user_input = String::new(); + io::stdin().read_line(&mut user_input)?; + + Ok(user_input) +} + +fn scan_group_confirmation() -> Result { + print!("\nconfirm? [Y/n]: "); + std::io::stdout().flush()?; + let mut user_input = String::new(); + io::stdin().read_line(&mut user_input)?; + + match user_input.trim() { + "Y" | "y" => Ok(true), + _ => Ok(false) + } +} + +fn process_group_action(duplicates: &Vec, dup_index: usize, dup_size: usize, table: Table) { + println!("\nDuplicate Set {} of {}\n", dup_index + 1, dup_size); + table.printstd(); + let files_to_delete = scan_group_instruction().unwrap_or_default(); + let parsed_file_indices = files_to_delete + .trim() + .split(',') + .filter(|element| !element.is_empty()) + .map(|index| index.parse::().unwrap_or_default()) + .collect_vec(); + + if parsed_file_indices + .clone() + .into_iter() + .any(|index| index > (duplicates.len() - 1)) + { + println!("Err: File Index Out of Bounds!"); + return process_group_action(duplicates, dup_index, dup_size, table); + } + + print!("{esc}[2J{esc}[1;1H", esc = 27 as char); + + let files_to_delete = parsed_file_indices + .into_iter() + .map(|index| duplicates[index].clone()); + + println!("\n{}", "The following files will be deleted:".red()); + files_to_delete.clone().enumerate().for_each(|(index, file)| { + println!("{}: {}", index.to_string().blue(), file.path); + }); + + match scan_group_confirmation().unwrap() { + true => { file_manager::delete_files(files_to_delete.collect_vec()); }, + false => println!("{}", "\nCancelled Delete Operation.".red()) + } +} + +pub fn interactive(duplicates: Vec, opts: &Params) { + print_meta_info(&duplicates, opts); + let grouped_duplicates = group_duplicates(duplicates); + + grouped_duplicates.iter().enumerate().for_each(|(gindex, (hash, group))| { + let mut itable = Table::new(); + itable.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); + itable.set_titles(row!["index", "filename", "size", "updated_at"]); + group.iter().enumerate().for_each(|(index, file)| { + itable.add_row(row![ + index, + format_path(&file.path, opts).unwrap_or_default().blue(), + file_size(&file.path).unwrap_or_default().red(), + modified_time(&file.path).unwrap_or_default().yellow() + ]); + }); + + process_group_action(group, gindex, grouped_duplicates.len(), itable); + }); +} + pub fn print(duplicates: Vec, opts: &Params) { + print_meta_info(&duplicates, opts); + let mut output_table = Table::new(); let grouped_duplicates: HashMap> = group_duplicates(duplicates); output_table.set_titles(row!["hash", "duplicates"]); grouped_duplicates.iter().for_each(|(hash, group)| { let mut inner_table = Table::new(); - // inner_table.set_format(inner_table_format); inner_table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); - //inner_table.set_titles(row!["filename", "size", "updated_at"]); group.iter().for_each(|file| { inner_table.add_row(row![ format_path(&file.path, opts).unwrap_or_default().blue(), From 471e60fa6cf4de727bf870dafcbcbea8db9fb480 Mon Sep 17 00:00:00 2001 From: sreedev Date: Tue, 10 Jan 2023 20:23:53 -0500 Subject: [PATCH 3/4] added delete options --- src/app/file_manager.rs | 9 ++++++++- src/output.rs | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app/file_manager.rs b/src/app/file_manager.rs index 9fdd0bb..4f57d81 100644 --- a/src/app/file_manager.rs +++ b/src/app/file_manager.rs @@ -1,7 +1,14 @@ use crate::database::File; use anyhow::Result; +use colored::Colorize; pub fn delete_files(files: Vec) -> Result<()> { - println!("{:?} DELETED", files); + files.into_iter().for_each(|file| { + match std::fs::remove_file(file.path.clone()) { + Ok(_) => println!("{}: {}", "DELETED".green(), file.path), + Err(e) => println!("{}: {}", "FAILED".red(), file.path) + } + }); + Ok(()) } diff --git a/src/output.rs b/src/output.rs index b70070c..74a1282 100644 --- a/src/output.rs +++ b/src/output.rs @@ -97,12 +97,14 @@ fn process_group_action(duplicates: &Vec, dup_index: usize, dup_size: usiz .into_iter() .any(|index| index > (duplicates.len() - 1)) { - println!("Err: File Index Out of Bounds!"); + println!("{}", "Err: File Index Out of Bounds!".red()); return process_group_action(duplicates, dup_index, dup_size, table); } print!("{esc}[2J{esc}[1;1H", esc = 27 as char); + if parsed_file_indices.is_empty() { return } + let files_to_delete = parsed_file_indices .into_iter() .map(|index| duplicates[index].clone()); From 533f81f724dbae5acc9a1181956f5ff77b2095f0 Mon Sep 17 00:00:00 2001 From: sreedev Date: Tue, 10 Jan 2023 20:26:03 -0500 Subject: [PATCH 4/4] version 0.0.7 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index becd524..33c4314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,7 +303,7 @@ dependencies = [ [[package]] name = "deduplicator" -version = "0.0.6" +version = "0.0.7" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index a4f2297..ba61aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deduplicator" -version = "0.0.6" +version = "0.0.7" edition = "2021" description = "find,filter,delete Duplicates" license = "MIT"