use clap::{crate_version, App, AppSettings, Arg, SubCommand};
pub fn build_app() -> App<'static, 'static> {
let debug_arg = Arg::with_name("debug")
.long("debug")
.short("d")
.takes_value(false)
.hidden(true);
let quiet_arg = Arg::with_name("quiet")
.long("quiet")
.short("q")
.takes_value(false)
.long_help("Hide the progress bar and other output from stderr (defaults to true if filenames are piped in)");
let force_arg = Arg::with_name("force")
.long("force")
.short("f")
.takes_value(false)
.long_help("Don't prompt before removing and linking files");
let dry_run_arg = Arg::with_name("dry-run")
.long("dry-run")
.takes_value(false)
.help("Don't actually remove and link files, but show what would be done");
let prefer_arg = Arg::with_name("prefer")
.long("prefer")
.takes_value(true)
.number_of_values(1)
.possible_values(&["oldest", "newest"])
.help(
"When selecting a target for linking prefer keeping the given target (default oldest)",
);
let estimate_arg = Arg::with_name("estimate")
.long("estimate")
.takes_value(false)
.long_help("Only perform an sampling analysis to determine duplicates");
let output_format_arg = Arg::with_name("output-format")
.long("output-format")
.takes_value(true)
.possible_values(&["json", "csv"])
.long_help("Only perform an sampling analysis to determine duplicates");
let no_sampling_arg = Arg::with_name("no-sampling")
.long("no-sampling")
.takes_value(false)
.help("Replace the first sampling analysis with a complete read analysis");
let fail_on_duplicate_arg = Arg::with_name("error-on-duplicate")
.long("error-on-duplicate")
.short("E")
.takes_value(false)
.long_help("Exits with exit code 2 if any duplicate is found");
let threads_arg = Arg::with_name("threads")
.long("threads")
.short("j")
.takes_value(true)
.value_name("num")
.hidden_short_help(true)
.long_help(
"The number of threads to use for reading files (default: number \
of available CPU cores * 2)",
);
let read_buffer_arg = Arg::with_name("read-buffer-size")
.long("buffer-size")
.hidden(true)
.takes_value(true)
.value_name("bytes")
.number_of_values(1)
.help("The size of the read buffer")
.long_help(
"Change the size of the read buffer to align with the block size of your file system. \
Can currently only be set globally, and defaults to 16KiB. \n
Examples:\n\
--buffer-size 10MiB\n\
--buffer-size 4096b",
);
let start_block_size_arg = Arg::with_name("start-block-size")
.long("start-size")
.hidden(true)
.takes_value(true)
.value_name("bytes")
.number_of_values(1)
.help("The initial amount of bytes to read before comparing files");
let path_arg = Arg::with_name("path")
.multiple(true)
.help("The root directory for the filesystem search (optional)")
.long_help(
"The directory where the filesystem search is rooted (optional). If \
omitted, search the current working directory.",
);
let min_size_arg = Arg::with_name("min-size")
.long("min-size")
.takes_value(true)
.value_name("bytes")
.number_of_values(1)
.help("The minimum size of files to consider for comparison (inclusive)")
.long_help(
"Set the minimum size of the files to consider for comparison.\n\
Currently all files, including empty ones, are scanned which may yield\n\
too many results. By setting a minimum the search space can be reduced.\n\n\
Examples:\n \
--min-size 1B\n \
--min-size 100MB",
);
let max_size_arg = Arg::with_name("max-size")
.long("max-size")
.takes_value(true)
.value_name("bytes")
.number_of_values(1)
.help("The maximum size of files to consider for comparison (inclusive)")
.long_help(
"Set the maximum size of the files to consider for comparison.\n\
Examples:\n \
--max-size 1B\n \
--max-size 100MB",
);
let shared_args = vec![
debug_arg,
quiet_arg,
threads_arg,
read_buffer_arg,
start_block_size_arg,
path_arg,
min_size_arg,
max_size_arg,
no_sampling_arg,
];
let cmp = SubCommand::with_name("cmp")
.about("Compare files and summarize the amount of duplicate data between the provided locations")
.usage("dsc cmp [FLAGS/OPTIONS] [<path>...]")
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.args(&shared_args)
.arg(fail_on_duplicate_arg)
.arg(estimate_arg);
let link = SubCommand::with_name("link")
.about("Remove duplicate files on the same device by replacing duplicates with hard links")
.usage("dsc link [FLAGS/OPTIONS] [<path>...]")
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.args(&shared_args)
.arg(force_arg)
.arg(prefer_arg)
.arg(dry_run_arg);
let report = SubCommand::with_name("report")
.about("Output the duplicates in a machine readable format")
.usage("dsc report [FLAGS/OPTIONS] [<path>...]")
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.args(&shared_args)
.arg(output_format_arg);
App::new("dsc")
.subcommands(vec![cmp, report, link])
.version(crate_version!())
.usage("dsc <subcommand> [FLAGS/OPTIONS] [<path>...]")
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.after_help(
"Note: `dsc <subcommand> -h` prints a short and concise overview while `dsc <subcommand> --help` gives all \
details.",
)
}