arg parsing

bug: paths are never recorded?
redo-gragh
Avril 4 years ago
parent e9a72f7513
commit 0e407dc375
Signed by: flanchan
GPG Key ID: 284488987C31F630

1
Cargo.lock generated

@ -78,6 +78,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"futures", "futures",
"lazy_static",
"num_cpus", "num_cpus",
"pin-project", "pin-project",
"tokio", "tokio",

@ -6,9 +6,16 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["splash"]
# Show splash screen
splash = []
[dependencies] [dependencies]
color-eyre = {version = "0.5.10", default-features=false} color-eyre = {version = "0.5.10", default-features=false}
futures = "0.3.12" futures = "0.3.12"
lazy_static = "1.4.0"
num_cpus = "1.13.0" num_cpus = "1.13.0"
pin-project = "1.0.5" pin-project = "1.0.5"
tokio = {version = "0.2", features=["full"]} tokio = {version = "0.2", features=["full"]}

@ -0,0 +1,124 @@
//! Argument parsing and handling
use super::*;
use std::num::NonZeroUsize;
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"));
}
/// Print usage message
pub fn usage()
{
#[cfg(feature="splash")]
{
splash(); println!();
}
println!("{} [OPTIONS] [-] <paths...>", program_name());
println!("{} --help", program_name());
println!(r#"
OPTIONS:
--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`).
-m Limit number of parallel tasks to the number of active CPU processors. (default).
- Stop parsing arguments, treat all the rest as paths.
--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` (which is default anyway).
"#);
}
/// 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,
"-m" => {
cfg.max_tasks = config::max_tasks_cpus(); // 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
},
"--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))
}

@ -15,7 +15,7 @@ impl Default for Recursion
#[inline] #[inline]
fn default() -> Self fn default() -> Self
{ {
Self::Unlimited Self::None
} }
} }
@ -31,9 +31,21 @@ impl Recursion
Self::Unlimited => true Self::Unlimited => true
} }
} }
}
impl From<usize> for Recursion
{
fn from(from: usize) -> Self
{
match from {
0 => Self::Unlimited,
1 => Self::None,
x => Self::Limited(unsafe {NonZeroUsize::new_unchecked(x)}),
}
}
} }
/// Configuration for this run /// Configuration for this run
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config pub struct Config
@ -43,6 +55,15 @@ pub struct Config
pub max_tasks: Option<NonZeroUsize>, pub max_tasks: Option<NonZeroUsize>,
} }
/// The default `max_tasks`
#[inline(always)] pub fn max_tasks_cpus() -> Option<NonZeroUsize>
{
lazy_static! {
static ref CPUS: usize = num_cpus::get();
}
NonZeroUsize::new(*CPUS)
}
impl Default for Config impl Default for Config
{ {
#[inline] #[inline]
@ -51,7 +72,7 @@ impl Default for Config
Self { Self {
paths: Vec::new(), paths: Vec::new(),
recursive: Default::default(), recursive: Default::default(),
max_tasks: NonZeroUsize::new(num_cpus::get()), max_tasks: max_tasks_cpus(),
} }
} }
} }

@ -2,6 +2,7 @@
#![allow(dead_code)] #![allow(dead_code)]
#[macro_use] extern crate pin_project; #[macro_use] extern crate pin_project;
#[macro_use] extern crate lazy_static;
use color_eyre::{ use color_eyre::{
eyre::{ eyre::{
@ -12,6 +13,7 @@ use color_eyre::{
}, },
Help as _, Help as _,
SectionExt as _,
}; };
#[macro_use] mod ext; #[macro_use] mod ext;
@ -20,17 +22,25 @@ pub use ext::prelude::*;
mod data; mod data;
mod config; mod config;
mod state; mod state;
mod arg;
mod work; mod work;
async fn read_config() -> eyre::Result<config::Config> async fn read_config() -> eyre::Result<config::Config>
{ {
Ok(config::Config::default()) //TODO: read config match arg::parse_args().wrap_err(eyre!("Failed to parse args"))?
{
arg::Mode::Normal(cfg) => {
#[cfg(debug_assertions)] eprintln!("Parsed config: {:#?}\n", cfg);
Ok(cfg)
},
arg::Mode::Help => arg::help(),
}
} }
#[tokio::main] #[tokio::main]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
color_eyre::install()?; color_eyre::install()?;
let state = state::State::new(read_config().await let state = state::State::new(read_config().await
.wrap_err(eyre!("Failed to load config"))? .wrap_err(eyre!("Failed to load config"))?
.validate() .validate()

@ -76,7 +76,7 @@ pub async fn work_on_all(state: State) -> INodeInfoGraph
// If this inode is not in the map, this is a top-level path. // If this inode is not in the map, this is a top-level path.
//if let Some(_) = ino_map.get(&ino) { //if let Some(_) = ino_map.get(&ino) {
output.insert(path, ino); output.insert(path, ino); //XXX: Why does `output` always end up empty???
//} else { //} else {
// eprintln!("No ino entry for {:?} ({:?})", path, ino); // eprintln!("No ino entry for {:?} ({:?})", path, ino);
//} //}

Loading…
Cancel
Save