mirror of
https://github.com/darakian/ddh.git
synced 2026-09-10 07:06:21 -04:00
commit
6c60d41d11
|
|
@ -11,7 +11,7 @@ documentation = "https://docs.rs/ddh"
|
|||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = "3"
|
||||
rayon = "1.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
|
|
|||
15
src/lib.rs
15
src/lib.rs
|
|
@ -27,11 +27,16 @@ enum ChannelPackage {
|
|||
pub fn deduplicate_dirs<P: AsRef<Path> + Sync>(
|
||||
search_dirs: Vec<P>,
|
||||
) -> (Vec<Fileinfo>, Vec<(PathBuf, std::io::Error)>) {
|
||||
deduplicate_dirs_with_min(search_dirs, 0)
|
||||
}
|
||||
|
||||
pub fn deduplicate_dirs_with_min<P: AsRef<Path> + Sync>(
|
||||
search_dirs: Vec<P>, min_size: u64) -> (Vec<Fileinfo>, Vec<(PathBuf, std::io::Error)>) {
|
||||
let (sender, receiver) = channel();
|
||||
search_dirs
|
||||
.par_iter()
|
||||
.for_each_with(sender, |s, search_dir| {
|
||||
traverse_and_spawn(search_dir.as_ref(), s.clone());
|
||||
traverse_and_spawn(search_dir.as_ref(), s.clone(), min_size);
|
||||
});
|
||||
let mut files_of_lengths: IntMap<u64, Vec<Fileinfo>> = IntMap::default();
|
||||
let mut errors = Vec::new();
|
||||
|
|
@ -56,7 +61,7 @@ pub fn deduplicate_dirs<P: AsRef<Path> + Sync>(
|
|||
(complete_files, errors)
|
||||
}
|
||||
|
||||
fn traverse_and_spawn(current_path: impl AsRef<Path>, sender: Sender<ChannelPackage>) {
|
||||
fn traverse_and_spawn(current_path: impl AsRef<Path>, sender: Sender<ChannelPackage>, min_size: u64) {
|
||||
let current_path_metadata = match fs::symlink_metadata(¤t_path) {
|
||||
Err(e) => {
|
||||
sender
|
||||
|
|
@ -76,7 +81,7 @@ fn traverse_and_spawn(current_path: impl AsRef<Path>, sender: Sender<ChannelPack
|
|||
Ok(canonical_path) => canonical_path,
|
||||
};
|
||||
match current_path_metadata {
|
||||
meta if meta.is_file() => {
|
||||
meta if meta.is_file() && meta.len() >= min_size => {
|
||||
sender
|
||||
.send(ChannelPackage::Success(Fileinfo::new(
|
||||
None,
|
||||
|
|
@ -99,10 +104,10 @@ fn traverse_and_spawn(current_path: impl AsRef<Path>, sender: Sender<ChannelPack
|
|||
.is_file()
|
||||
});
|
||||
files.par_iter().for_each_with(sender.clone(), |sender, x| {
|
||||
traverse_and_spawn(&x.path(), sender.clone())
|
||||
traverse_and_spawn(&x.path(), sender.clone(), min_size)
|
||||
});
|
||||
dirs.into_par_iter().for_each_with(sender, |sender, x| {
|
||||
traverse_and_spawn(x.path().as_path(), sender.clone());
|
||||
traverse_and_spawn(x.path().as_path(), sender.clone(), min_size);
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
|||
42
src/main.rs
42
src/main.rs
|
|
@ -19,55 +19,65 @@ pub enum Verbosity {
|
|||
All,
|
||||
}
|
||||
|
||||
static DDH_ABOUT: &str = "Compare and contrast directories.\nExample invocation: ddh /home/jon/downloads /home/jon/documents -f duplicates\nExample pipe: ddh ~/Downloads/ -o no -v all -f json | someJsonParser.bin";
|
||||
|
||||
fn main() {
|
||||
let arguments = App::new("Directory Difference hTool")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about("Compare and contrast directories.\nExample invocation: ddh /home/jon/downloads /home/jon/documents -f duplicates\nExample pipe: ddh ~/Downloads/ -o no -v all -f json | someJsonParser.bin")
|
||||
.arg(Arg::with_name("directories")
|
||||
.short("d")
|
||||
.about(DDH_ABOUT)
|
||||
.arg(Arg::new("directories")
|
||||
.short('d')
|
||||
.long("directories")
|
||||
.value_name("Directories")
|
||||
.help("Directories to parse")
|
||||
.min_values(1)
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.index(1))
|
||||
.arg(Arg::with_name("Blocksize")
|
||||
.short("bs")
|
||||
.takes_value(true))
|
||||
.arg(Arg::new("Blocksize")
|
||||
.short('b')
|
||||
.long("blocksize")
|
||||
.case_insensitive(true)
|
||||
.ignore_case(true)
|
||||
.takes_value(true)
|
||||
.max_values(1)
|
||||
.possible_values(&["B", "K", "M", "G"])
|
||||
.help("Sets the display blocksize to Bytes, Kilobytes, Megabytes or Gigabytes. Default is Kilobytes."))
|
||||
.arg(Arg::with_name("Verbosity")
|
||||
.short("v")
|
||||
.arg(Arg::new("Verbosity")
|
||||
.short('v')
|
||||
.long("verbosity")
|
||||
.possible_values(&["quiet", "duplicates", "all"])
|
||||
.case_insensitive(true)
|
||||
.ignore_case(true)
|
||||
.takes_value(true)
|
||||
.help("Sets verbosity for printed output."))
|
||||
.arg(Arg::with_name("Output")
|
||||
.short("o")
|
||||
.arg(Arg::new("Output")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.takes_value(true)
|
||||
.max_values(1)
|
||||
.help("Sets file to save all output. Use 'no' for no file output."))
|
||||
.arg(Arg::with_name("Format")
|
||||
.short("f")
|
||||
.arg(Arg::new("Format")
|
||||
.short('f')
|
||||
.long("format")
|
||||
.possible_values(&["standard", "json", "off"])
|
||||
.takes_value(true)
|
||||
.max_values(1)
|
||||
.help("Sets output format."))
|
||||
.arg(Arg::new("Minimum")
|
||||
.short('m')
|
||||
.long("minimum")
|
||||
.takes_value(true)
|
||||
.max_values(1)
|
||||
.default_value("0")
|
||||
.validator(|s| s.parse::<u64>())
|
||||
.help("Minimum file size in bytes to consider."))
|
||||
.get_matches();
|
||||
|
||||
//let (sender, receiver) = channel();
|
||||
let search_dirs: Vec<_> = arguments.values_of("directories").unwrap().collect();
|
||||
let min_size: u64 = arguments.value_of("Minimum").unwrap().parse::<u64>().unwrap_or(0);
|
||||
|
||||
let (complete_files, read_errors): (Vec<Fileinfo>, Vec<(_, _)>) =
|
||||
ddh::deduplicate_dirs(search_dirs);
|
||||
ddh::deduplicate_dirs_with_min(search_dirs, min_size);
|
||||
let (shared_files, unique_files): (Vec<&Fileinfo>, Vec<&Fileinfo>) = complete_files
|
||||
.par_iter()
|
||||
.partition(|&x| x.get_paths().len() > 1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue