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

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)
}
}