Projects STRLCPY deduplicator Commits dd4b0513
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■
    Cargo.lock
    skipped 302 lines
    303 303   
    304 304  [[package]]
    305 305  name = "deduplicator"
    306  -version = "0.0.6"
     306 +version = "0.0.7"
    307 307  dependencies = [
    308 308   "anyhow",
    309 309   "chrono",
    skipped 867 lines
  • ■ ■ ■ ■
    Cargo.toml
    1 1  [package]
    2 2  name = "deduplicator"
    3  -version = "0.0.6"
     3 +version = "0.0.7"
    4 4  edition = "2021"
    5 5  description = "find,filter,delete Duplicates"
    6 6  license = "MIT"
    skipped 21 lines
  • ■ ■ ■ ■ ■ ■
    src/app/file_manager.rs
     1 +use crate::database::File;
     2 +use anyhow::Result;
     3 +use colored::Colorize;
     4 + 
     5 +pub fn delete_files(files: Vec<File>) -> Result<()> {
     6 + files.into_iter().for_each(|file| {
     7 + match std::fs::remove_file(file.path.clone()) {
     8 + Ok(_) => println!("{}: {}", "DELETED".green(), file.path),
     9 + Err(e) => println!("{}: {}", "FAILED".red(), file.path)
     10 + }
     11 + });
     12 + 
     13 + Ok(())
     14 +}
     15 + 
  • ■ ■ ■ ■ ■
    src/app/mod.rs
     1 +#![allow(unused)]
     2 + 
    1 3  mod event_handler;
    2 4  mod events;
    3 5  mod formatter;
    4 6  mod ui;
     7 +pub mod file_manager;
    5 8   
    6 9  use std::{io, thread, time::Duration};
    7 10   
    skipped 24 lines
    32 35   // Self::init_render_loop(&mut term)?;
    33 36   // Self::cleanup(&mut term)?;
    34 37   
    35  - output::print(duplicates, app_args); /* TODO: APP TUI INIT FUNCTION */
     38 + match app_args.interactive {
     39 + true => output::interactive(duplicates, app_args),
     40 + false => output::print(duplicates, app_args) /* TODO: APP TUI INIT FUNCTION */
     41 + }
     42 +
    36 43   Ok(())
    37 44   }
    38 45   
    skipped 45 lines
  • ■ ■ ■ ■ ■ ■
    src/output.rs
    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()?, "");
    skipped 35 lines
    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(),
    skipped 10 lines
  • ■ ■ ■ ■
    src/params.rs
    skipped 11 lines
    12 12   /// Run Deduplicator on dir different from pwd
    13 13   #[arg(long)]
    14 14   pub dir: Option<PathBuf>,
    15  - /// Don't use cache for indexing files (default = true)
     15 + /// Don't use cache for indexing files (default = false)
    16 16   #[arg(long, short)]
    17 17   pub nocache: bool,
     18 + /// Delete files interactively
     19 + #[arg(long, short)]
     20 + pub interactive: bool
    18 21  }
    19 22   
    20 23  impl Params {
    skipped 18 lines
Please wait...
Page is in error, reload to recover