extern crate lrg;
extern crate clap;
extern crate humansize;
extern crate pathdiff;
use std::env;
use std::path::{PathBuf};
use std::process;
use lrg::{get_walkdir_error_str, Lrg, LrgOptions, SortBy};
use clap::{App, Arg};
use humansize::{file_size_opts, FileSize};
use pathdiff::diff_paths;
fn main() {
env_logger::init();
let matches = App::new("lrg")
.version("0.2.0")
.author("Noah Rinehart <rinehart.noah@gmail.com>")
.about("A utility to help find the largest file(s) in a directory")
.arg(Arg::with_name("NUMBER")
.short("n")
.long("number")
.value_name("NUM_ENTRIES")
.help("sets the number of files to list (default: 5)")
.takes_value(true))
.arg(Arg::with_name("RECURSIVE")
.short("r")
.long("no-recursion")
.help("will only visit files in specified directory, takes precedence over max-depth (default: false)"))
.arg(Arg::with_name("MAX_DEPTH")
.short("d")
.long("max-depth")
.value_name("MAX_DEPTH")
.help("sets the maximum depth of folders to search, unless --no-recursion specified (default: max possible)")
.takes_value(true))
.arg(Arg::with_name("FOLLOW_LINKS")
.short("l")
.long("follow-links")
.help("will follow links of files (default: false)"))
.arg(Arg::with_name("DIRECTORIES")
.short("i")
.long("directories")
.help("include directories in search (default: false)"))
.arg(Arg::with_name("ASCENDING")
.short("a")
.long("ascending")
.help("sort the results in ascending order (default: false)"))
.arg(Arg::with_name("ABSOLUTE")
.short("b")
.long("absolute")
.help("outputs files' absolute path (default: false)"))
.arg(Arg::with_name("UNITS")
.short("u")
.long("units")
.value_name("UNITS")
.help("sets the units to display: decimal for 1000KB, binary for 1024KiB, conventional for 1024KB (default: conventional)")
.takes_value(true))
.arg(Arg::with_name("FILEPATH")
.help("the path to search in")
.index(1))
.get_matches();
let current_dir = match matches.value_of("FILEPATH") {
Some(filepath) => PathBuf::from(filepath),
None => match env::current_dir() {
Ok(path) => path.as_path().to_owned(),
Err(_err) => {
println!("Error: couldn't get current directory");
process::exit(1);
}
},
};
let num_entries = match matches.value_of("NUMBER") {
Some(number) => match number.parse::<usize>() {
Ok(number) => number,
Err(_err) => {
println!("Error: couldn't parse number of files to list");
process::exit(1);
}
},
None => 5,
};
let max_depth = if matches.is_present("RECURSIVE") {
1
} else {
match matches.value_of("MAX_DEPTH") {
Some(depth) => match depth.parse::<usize>() {
Ok(depth) => depth,
Err(_err) => {
println!("Error: couldn't parse max depth");
process::exit(1);
}
},
None => ::std::usize::MAX,
}
};
let follow_links = matches.is_present("FOLLOW_LINKS");
let include_dirs = matches.is_present("DIRECTORIES");
let sort_value = if matches.is_present("ASCENDING") {
SortBy::Ascending
} else {
SortBy::Descending
};
let output_absolute = matches.is_present("ABSOLUTE");
let units = match matches.value_of("UNITS") {
Some(unit) => {
match unit {
"decimal" => file_size_opts::DECIMAL,
"binary" => file_size_opts::BINARY,
"conventional" => file_size_opts::CONVENTIONAL,
_ => {
println!("Error: couldn't parse units");
process::exit(1);
},
}
},
None => file_size_opts::CONVENTIONAL,
};
let options = LrgOptions {
max_depth,
follow_links,
include_dirs,
..LrgOptions::default()
};
let entries = Lrg::new(¤t_dir, &options)
.sort_by(&sort_value)
.get_entries();
if entries.is_empty() {
println!("lrg: no files found");
process::exit(1);
}
let hs_options = file_size_opts::FileSizeOpts {
allow_negative: true,
..units
};
for (i, entry) in entries.iter().enumerate() {
if i == num_entries {
break;
}
let display_path = if output_absolute {
format!("{}", entry.path().display())
} else {
format!("{}", diff_paths(entry.path(), ¤t_dir).unwrap_or_else(|| PathBuf::new()).display())
};
match entry.metadata() {
Ok(meta) => {
println!(
"{}: {}",
meta.len().file_size(&hs_options).unwrap(),
display_path
);
}
Err(err) => {
let error_message = get_walkdir_error_str(&err);
println!(
"lrg: cannot get metadata of '{}': {}",
display_path,
error_message
);
}
}
}
}