|
1 |
| -use std::{collections::HashMap, fs}; |
| 1 | +use std::{collections::HashMap, fs, io}; |
| 2 | +use std::io::Write; |
2 | 3 |
|
3 | 4 | use anyhow::Result;
|
4 | 5 | use chrono::offset::Utc;
|
5 | 6 | use chrono::DateTime;
|
6 | 7 | use colored::Colorize;
|
7 | 8 | use humansize::{format_size, DECIMAL};
|
| 9 | +use itertools::Itertools; |
8 | 10 |
|
| 11 | +use crate::app::file_manager; |
9 | 12 | use crate::database::File;
|
10 | 13 | use crate::params::Params;
|
11 |
| -use prettytable::{row, Cell, Row, format, Table}; |
| 14 | +use prettytable::{format, row, Cell, Row, Table}; |
12 | 15 |
|
13 | 16 | fn format_path(path: &str, opts: &Params) -> Result<String> {
|
14 | 17 | let display_path = path.replace(&opts.get_directory()?, "");
|
@@ -50,16 +53,104 @@ fn group_duplicates(duplicates: Vec<File>) -> HashMap<String, Vec<File>> {
|
50 | 53 | duplicate_mapper
|
51 | 54 | }
|
52 | 55 |
|
| 56 | +fn print_meta_info(duplicates: &Vec<File>, opts: &Params) { |
| 57 | + println!("Deduplicator v{}", std::env!("CARGO_PKG_VERSION")); |
| 58 | +} |
| 59 | + |
| 60 | +fn scan_group_instruction() -> Result<String> { |
| 61 | + println!("\nEnter the indices of the files you want to delete."); |
| 62 | + println!("You can enter multiple files using commas to seperate file indices."); |
| 63 | + println!("example: 1,2"); |
| 64 | + print!("\n> "); |
| 65 | + std::io::stdout().flush()?; |
| 66 | + let mut user_input = String::new(); |
| 67 | + io::stdin().read_line(&mut user_input)?; |
| 68 | + |
| 69 | + Ok(user_input) |
| 70 | +} |
| 71 | + |
| 72 | +fn scan_group_confirmation() -> Result<bool> { |
| 73 | + print!("\nconfirm? [Y/n]: "); |
| 74 | + std::io::stdout().flush()?; |
| 75 | + let mut user_input = String::new(); |
| 76 | + io::stdin().read_line(&mut user_input)?; |
| 77 | + |
| 78 | + match user_input.trim() { |
| 79 | + "Y" | "y" => Ok(true), |
| 80 | + _ => Ok(false) |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +fn process_group_action(duplicates: &Vec<File>, dup_index: usize, dup_size: usize, table: Table) { |
| 85 | + println!("\nDuplicate Set {} of {}\n", dup_index + 1, dup_size); |
| 86 | + table.printstd(); |
| 87 | + let files_to_delete = scan_group_instruction().unwrap_or_default(); |
| 88 | + let parsed_file_indices = files_to_delete |
| 89 | + .trim() |
| 90 | + .split(',') |
| 91 | + .filter(|element| !element.is_empty()) |
| 92 | + .map(|index| index.parse::<usize>().unwrap_or_default()) |
| 93 | + .collect_vec(); |
| 94 | + |
| 95 | + if parsed_file_indices |
| 96 | + .clone() |
| 97 | + .into_iter() |
| 98 | + .any(|index| index > (duplicates.len() - 1)) |
| 99 | + { |
| 100 | + println!("{}", "Err: File Index Out of Bounds!".red()); |
| 101 | + return process_group_action(duplicates, dup_index, dup_size, table); |
| 102 | + } |
| 103 | + |
| 104 | + print!("{esc}[2J{esc}[1;1H", esc = 27 as char); |
| 105 | + |
| 106 | + if parsed_file_indices.is_empty() { return } |
| 107 | + |
| 108 | + let files_to_delete = parsed_file_indices |
| 109 | + .into_iter() |
| 110 | + .map(|index| duplicates[index].clone()); |
| 111 | + |
| 112 | + println!("\n{}", "The following files will be deleted:".red()); |
| 113 | + files_to_delete.clone().enumerate().for_each(|(index, file)| { |
| 114 | + println!("{}: {}", index.to_string().blue(), file.path); |
| 115 | + }); |
| 116 | + |
| 117 | + match scan_group_confirmation().unwrap() { |
| 118 | + true => { file_manager::delete_files(files_to_delete.collect_vec()); }, |
| 119 | + false => println!("{}", "\nCancelled Delete Operation.".red()) |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +pub fn interactive(duplicates: Vec<File>, opts: &Params) { |
| 124 | + print_meta_info(&duplicates, opts); |
| 125 | + let grouped_duplicates = group_duplicates(duplicates); |
| 126 | + |
| 127 | + grouped_duplicates.iter().enumerate().for_each(|(gindex, (hash, group))| { |
| 128 | + let mut itable = Table::new(); |
| 129 | + itable.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); |
| 130 | + itable.set_titles(row!["index", "filename", "size", "updated_at"]); |
| 131 | + group.iter().enumerate().for_each(|(index, file)| { |
| 132 | + itable.add_row(row![ |
| 133 | + index, |
| 134 | + format_path(&file.path, opts).unwrap_or_default().blue(), |
| 135 | + file_size(&file.path).unwrap_or_default().red(), |
| 136 | + modified_time(&file.path).unwrap_or_default().yellow() |
| 137 | + ]); |
| 138 | + }); |
| 139 | + |
| 140 | + process_group_action(group, gindex, grouped_duplicates.len(), itable); |
| 141 | + }); |
| 142 | +} |
| 143 | + |
53 | 144 | pub fn print(duplicates: Vec<File>, opts: &Params) {
|
| 145 | + print_meta_info(&duplicates, opts); |
| 146 | + |
54 | 147 | let mut output_table = Table::new();
|
55 | 148 | let grouped_duplicates: HashMap<String, Vec<File>> = group_duplicates(duplicates);
|
56 | 149 |
|
57 | 150 | output_table.set_titles(row!["hash", "duplicates"]);
|
58 | 151 | grouped_duplicates.iter().for_each(|(hash, group)| {
|
59 | 152 | let mut inner_table = Table::new();
|
60 |
| - // inner_table.set_format(inner_table_format); |
61 | 153 | inner_table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
62 |
| - //inner_table.set_titles(row!["filename", "size", "updated_at"]); |
63 | 154 | group.iter().for_each(|file| {
|
64 | 155 | inner_table.add_row(row![
|
65 | 156 | format_path(&file.path, opts).unwrap_or_default().blue(),
|
|
0 commit comments