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.
302 lines
6.4 KiB
302 lines
6.4 KiB
use super::*;
|
|
use std::{
|
|
num::{
|
|
NonZeroUsize,
|
|
ParseIntError,
|
|
},
|
|
path::{
|
|
PathBuf,
|
|
Path,
|
|
},
|
|
};
|
|
use lazy_static::lazy_static;
|
|
|
|
#[inline] pub fn program_name() -> &'static str
|
|
{
|
|
lazy_static!{
|
|
static ref PROG: &'static str = Box::leak(std::env::args().next().unwrap().into_boxed_str());
|
|
}
|
|
&PROG[..]
|
|
}
|
|
|
|
fn extra_args() -> &'static str
|
|
{
|
|
lazy_static! {
|
|
static ref EXTRA: &'static str = {
|
|
let work = || -> Result<String, std::fmt::Error> {
|
|
use std::fmt::Write as _;
|
|
let mut output = String::new();
|
|
#[cfg(feature="progress")] writeln!(output, " --no-progress Do not display progress bar")?;
|
|
Ok(output)
|
|
};
|
|
match work() {
|
|
Ok(output) => Box::leak(output.into_boxed_str()),
|
|
Err(err) => {
|
|
eprintln!("Failed to write usage message: {}", err);
|
|
""
|
|
},
|
|
}
|
|
};
|
|
}
|
|
&EXTRA[..]
|
|
}
|
|
|
|
mod feature
|
|
{
|
|
use cfg_if::cfg_if;
|
|
#[macro_export] macro_rules! check {
|
|
(on $name:literal) => {
|
|
if cfg!(feature = $name) {
|
|
feature::on($name, true);
|
|
} else {
|
|
feature::off($name, false);
|
|
}
|
|
};
|
|
(off $name:literal) => {
|
|
if cfg!(feature = $name) {
|
|
feature::on($name, false);
|
|
} else {
|
|
feature::off($name, true);
|
|
}
|
|
}
|
|
}
|
|
pub fn on(name: impl AsRef<str>, default: bool)
|
|
{
|
|
cfg_if! {
|
|
if #[cfg(feature="colour")] {
|
|
use recolored::Colorize;
|
|
if default {
|
|
println!(" {}", format!("+{}", name.as_ref()).red());
|
|
} else {
|
|
println!(" {}", format!("+{}", name.as_ref()).bright_red());
|
|
}
|
|
} else {
|
|
println!(" +{}", name.as_ref())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn off(name: impl AsRef<str>, default: bool)
|
|
{
|
|
cfg_if! {
|
|
if #[cfg(feature="colour")] {
|
|
use recolored::Colorize;
|
|
if default {
|
|
println!(" {}", format!("-{}", name.as_ref()).blue());
|
|
} else {
|
|
println!(" {}", format!("-{}", name.as_ref()).bright_blue());
|
|
|
|
}
|
|
} else {
|
|
println!(" -{}", name.as_ref())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn comp_flags()
|
|
{
|
|
check!(on "splash");
|
|
check!(on "colour");
|
|
check!(on "progress");
|
|
check!(off "threads");
|
|
}
|
|
|
|
pub fn usage() -> !
|
|
{
|
|
#[cfg(feature="splash")]
|
|
splash::print();
|
|
|
|
println!(r"Usage: {prog} [OPTIONS] [-] <files...>
|
|
Usage: {prog} --help
|
|
|
|
OPTIONS:
|
|
--max-children <number> Max subprocesses allowed to live at once. Infinite by default.
|
|
--recursive <number> Recurse up to `<number>` times. Must be at least `1`. Default is off.
|
|
-r Resurse infinitely.
|
|
{extra}
|
|
- Stop parsing args here.
|
|
|
|
ENVIRONMENT VARS:
|
|
LEANIFY=<process name> Path to leanify executable, defaults to looking in `PATH' if set.
|
|
", prog = program_name(), extra = &extra_args()[..extra_args().len()-1]);
|
|
println!("Compiled with:");
|
|
comp_flags();
|
|
|
|
std::process::exit(1)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
#[cfg(nightly)] BadNumber(std::num::IntErrorKind),
|
|
#[cfg(not(nightly))] BadNumber(()),
|
|
NoExist(PathBuf),
|
|
Walking(dir::Error),
|
|
NoFiles,
|
|
}
|
|
impl std::error::Error for Error{}
|
|
impl std::fmt::Display for Error
|
|
{
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
|
{
|
|
//write!(f, "arg parsing failed: ")?;
|
|
match self {
|
|
Self::BadNumber(_v) => {
|
|
write!(f, "expected non-zero integer")?;
|
|
|
|
#[cfg(nightly)] use std::num::IntErrorKind;
|
|
#[cfg(nightly)] return match _v {
|
|
IntErrorKind::Empty => write!(f, ": argument empty"),
|
|
IntErrorKind::InvalidDigit => write!(f, ": invalid digit"),
|
|
IntErrorKind::Overflow => write!(f, ": conversion would result in overflow"),
|
|
IntErrorKind::Underflow => write!(f, ": conversion would result in underflow"),
|
|
IntErrorKind::Zero => write!(f, ": found zero"),
|
|
_=> Ok(()),
|
|
};
|
|
#[cfg(not(nightly))] Ok(())
|
|
},
|
|
Self::NoExist(path) => write!(f, "path {:?} does not exist", path),
|
|
Self::NoFiles => write!(f, "need at least one argument"),
|
|
Self::Walking(dir) => write!(f, "error walking directory structure(s): {}", dir),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ParseIntError> for Error
|
|
{
|
|
fn from(_er: ParseIntError) -> Self
|
|
{
|
|
#[cfg(nightly)] return Self::BadNumber(_er.kind().clone());
|
|
#[cfg(not(nightly))] Self::BadNumber(())
|
|
}
|
|
}
|
|
|
|
|
|
/// Any extra options
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct Flags
|
|
{
|
|
/// Display the progress bar
|
|
#[cfg(feature="progress")] pub progress: bool,
|
|
}
|
|
|
|
impl Default for Flags
|
|
{
|
|
#[inline]
|
|
fn default() -> Self
|
|
{
|
|
Self {
|
|
#[cfg(feature="progress")] progress: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct Config
|
|
{
|
|
pub max_children: Option<NonZeroUsize>,
|
|
pub files: Vec<PathBuf>,
|
|
pub recursive: Option<NonZeroUsize>,
|
|
pub flags: Flags
|
|
}
|
|
|
|
impl Default for Config
|
|
{
|
|
#[inline]
|
|
fn default() -> Self
|
|
{
|
|
Self {
|
|
max_children: None,
|
|
files: Vec::new(),
|
|
recursive: Some(unsafe{NonZeroUsize::new_unchecked(1)}),
|
|
flags: Flags::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Parse the `env::args()`
|
|
#[inline] pub async fn parse_args() -> Result<Config, Error>
|
|
{
|
|
let args = std::env::args();
|
|
if args.len() <= 1 {
|
|
println!("Warning: No arguments specified, try passing `--help`.");
|
|
}
|
|
parse(args.skip(1)).await
|
|
}
|
|
|
|
async fn parse<I,T>(args: I) -> Result<Config, Error>
|
|
where I: IntoIterator<Item=T>,
|
|
T: Into<String>
|
|
{
|
|
let mut args = args.into_iter().map(|x| x.into());
|
|
let mut cfg = Config::default();
|
|
let mut reading=true;
|
|
let mut first=true;
|
|
|
|
while let Some(arg) = args.next() {
|
|
if reading {
|
|
let lw_arg = arg.trim().to_lowercase();
|
|
if first {
|
|
match lw_arg.as_str() {
|
|
"--help" => {
|
|
usage()
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
first=false;
|
|
|
|
match lw_arg.as_str() {
|
|
"--max-children" => {
|
|
if let Some(nzi) = args.next() {
|
|
cfg.max_children = Some(nzi.parse()?);
|
|
continue;
|
|
}
|
|
},
|
|
"--recursive" => {
|
|
if let Some(nzi) = args.next() {
|
|
cfg.recursive = Some(nzi.parse()?);
|
|
continue;
|
|
}
|
|
},
|
|
#[cfg(feature="progress")]
|
|
"--no-progress" => {
|
|
cfg.flags.progress = false;
|
|
continue;
|
|
},
|
|
"-r" => {
|
|
cfg.recursive = None;
|
|
},
|
|
"-" => {
|
|
reading= false;
|
|
continue;
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
reading = false;
|
|
|
|
let path = Path::new(&arg);
|
|
if path.is_dir() {
|
|
cfg.files.extend(dir::walk(path, cfg.recursive, dir::recommended_max_walkers()).await?);
|
|
} else if path.is_file() {
|
|
cfg.files.push(path.to_owned());
|
|
} else {
|
|
return Err(Error::NoExist(path.to_owned()));
|
|
}
|
|
}
|
|
if cfg.files.len() == 0 {
|
|
return Err(Error::NoFiles);
|
|
}
|
|
Ok(cfg)
|
|
}
|
|
|
|
impl From<dir::Error> for Error
|
|
{
|
|
fn from(from: dir::Error) -> Self
|
|
{
|
|
Self::Walking(from)
|
|
}
|
|
}
|