You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.9 KiB
188 lines
5.9 KiB
//! Argument parsing and handling
|
|
use super::*;
|
|
use std::num::NonZeroUsize;
|
|
use std::fmt;
|
|
|
|
use config::Config;
|
|
|
|
/// Executable name
|
|
pub fn program_name() -> &'static str
|
|
{
|
|
lazy_static! {
|
|
static ref NAME: String = std::env::args().next().unwrap();
|
|
}
|
|
&NAME[..]
|
|
}
|
|
|
|
#[cfg(feature="splash")]
|
|
/// Print splash screen
|
|
#[inline] pub fn splash()
|
|
{
|
|
eprintln!("dirstat version {}", env!("CARGO_PKG_VERSION"));
|
|
eprintln!("Made by {} with <3.\n Licensed with GPL v3.0 +", env!("CARGO_PKG_AUTHORS"));
|
|
}
|
|
|
|
const OPTIONS_NORMAL: &'static [&'static str] = &[
|
|
"--recursive <number> Set max directory recursion depth limit (1 = No recursion (default), 0 = Unlimited recursion).",
|
|
"-r Set unlimited directory recursion depth. (same as `--recursive 0`).",
|
|
"--threads <number> Limit the maximum number of tasks allowed to process concurrently (Set to 0 for unlimited.)",
|
|
"-M Set number of parallel running tasks to unlimited. (Same as `--threads 0`). (default).",
|
|
"-m Limit number of parallel tasks to the number of active CPU processors.",
|
|
"-q Quiet mode. Don't output info messages about successful `stat`ing.",
|
|
"-Q Silent mode. Don't output any messages.",
|
|
"-v Verbose mode. Output extra information.",
|
|
#[cfg(feature="inspect")] "--save <file> Dump the collected data to this file for further inspection.",
|
|
#[cfg(feature="inspect")] "-D Dump the collected data to `stdout` (see `--save`.)",
|
|
#[cfg(feature="inspect")] "--save-raw <file> Dump the collected data to this file uncompressed. (see `--save`.)",
|
|
#[cfg(feature="inspect")] "-Dr Dump the collected data to standard output uncompressed. (see `--save-raw`.)",
|
|
"- Stop parsing arguments, treat all the rest as paths.",
|
|
];
|
|
|
|
fn get_opt_normal() -> impl fmt::Display
|
|
{
|
|
#[derive(Debug)]
|
|
struct Opt;
|
|
|
|
impl fmt::Display for Opt
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
for line in OPTIONS_NORMAL.iter()
|
|
{
|
|
writeln!(f, " {}", line)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
Opt
|
|
}
|
|
|
|
/// Print usage message
|
|
pub fn usage()
|
|
{
|
|
#[cfg(feature="splash")]
|
|
{
|
|
splash(); println!();
|
|
}
|
|
|
|
println!("{} [OPTIONS] [-] <paths...>", program_name());
|
|
println!("{} --help", program_name());
|
|
println!(r#"
|
|
OPTIONS:
|
|
{}
|
|
|
|
--help Print this message and exit.
|
|
|
|
NOTES:
|
|
The first time a non-option argument is encountered, the program stops parsing arguments and assumes the rest of the arguments are paths.
|
|
If parallelism is set to unlimited, there can be a huge syscall overhead. It is recommended to use `-m`.
|
|
|
|
Symlinks are ignored while collection stat data. They will fail with message 'Unknown file type'. Symlinks are generally very small in the actual data they contain themselves, so this is *usually* unimportant.
|
|
"#, get_opt_normal());
|
|
}
|
|
|
|
/// Print usage message then exit with code 1.
|
|
pub fn help() -> !
|
|
{
|
|
usage();
|
|
|
|
std::process::exit(1)
|
|
}
|
|
|
|
/// Which mode to run in
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum Mode
|
|
{
|
|
Normal(Config),
|
|
Help,
|
|
}
|
|
|
|
/// Parse command-line arguments
|
|
#[inline] pub fn parse_args() -> eyre::Result<Mode>
|
|
{
|
|
parse(std::env::args().skip(1))
|
|
}
|
|
|
|
fn parse<I: IntoIterator<Item=String>>(args: I) -> eyre::Result<Mode>
|
|
{
|
|
let suggestion_intended_arg = || "If this was intended as a path instead of an option, use option `-` before it.";
|
|
|
|
let mut args = args.into_iter();
|
|
let mut cfg = Config::default();
|
|
|
|
let mut reading = true;
|
|
while let Some(opt) = args.next()
|
|
{
|
|
if reading {
|
|
match opt.trim()
|
|
{
|
|
"--help" => return Ok(Mode::Help),
|
|
"-" => reading = false,
|
|
|
|
"--threads" => {
|
|
let max = args.next().ok_or(eyre!("`--threads` expects a parameter"))
|
|
.with_suggestion(suggestion_intended_arg.clone())?;
|
|
cfg.max_tasks = NonZeroUsize::new(max.parse::<usize>()
|
|
.wrap_err(eyre!("`--threads` expects a non-negative number"))
|
|
.with_suggestion(suggestion_intended_arg.clone())
|
|
.with_section(move || max.header("Parameter given was"))?);
|
|
},
|
|
"-M" => cfg.max_tasks = None, // this is the default, but it is possible an earlier command mutated it, so doing nothing here would be a bug for that corner case
|
|
"-m" => {
|
|
cfg.max_tasks = config::max_tasks_cpus();
|
|
},
|
|
"-q" => {
|
|
cfg.output_level = config::OutputLevel::Quiet;
|
|
},
|
|
"-Q" => {
|
|
cfg.output_level = config::OutputLevel::Silent;
|
|
},
|
|
"-v" => {
|
|
cfg.output_level = config::OutputLevel::Verbose;
|
|
},
|
|
#[cfg(feature="inspect")] "-D" => {
|
|
cfg.serialise_output = Some(config::OutputSerialisationMode::Stdout);
|
|
},
|
|
#[cfg(feature="inspect")] "-Dr" => {
|
|
cfg.serialise_output = Some(config::OutputSerialisationMode::RawStdout);
|
|
},
|
|
#[cfg(feature="inspect")] "--save" => {
|
|
let file = args.next().ok_or(eyre!("`--save` expects a filename parameter"))
|
|
.with_suggestion(suggestion_intended_arg.clone())?;
|
|
cfg.serialise_output = Some(config::OutputSerialisationMode::File(file.into()));
|
|
},
|
|
#[cfg(feature="inspect")] "--save-raw" => {
|
|
let file = args.next().ok_or(eyre!("`--save-raw` expects a filename parameter"))
|
|
.with_suggestion(suggestion_intended_arg.clone())?;
|
|
#[cfg(feature="prealloc")] {
|
|
cfg.serialise_output = Some(config::OutputSerialisationMode::PreallocFile(file.into()));
|
|
}
|
|
#[cfg(not(feature="prealloc"))] {
|
|
cfg.serialise_output = Some(config::OutputSerialisationMode::RawFile(file.into()));
|
|
}
|
|
},
|
|
"--recursive" => {
|
|
let max = args.next().ok_or(eyre!("`--recursive` expects a parameter"))
|
|
.with_suggestion(suggestion_intended_arg.clone())?;
|
|
|
|
cfg.recursive = max.parse::<usize>()
|
|
.wrap_err(eyre!("`--recursive` expects a non-negative number"))
|
|
.with_suggestion(suggestion_intended_arg.clone())
|
|
.with_section(move || max.header("Parameter given was"))?.into();
|
|
},
|
|
"-r" => cfg.recursive = config::Recursion::Unlimited,
|
|
|
|
_ => {
|
|
cfg.paths.push(opt.into());
|
|
reading = false;
|
|
}
|
|
}
|
|
continue;
|
|
} else {
|
|
cfg.paths.push(opt.into());
|
|
}
|
|
}
|
|
Ok(Mode::Normal(cfg))
|
|
}
|