finished~ actual release now

master
Avril 4 years ago
parent c3af720ebd
commit 2927ffd552
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1 @@
avril@flan-laptop.299665:1596751578

@ -14,6 +14,7 @@ progress = ["termprogress"]
threads = ["tokio/rt-threaded"]
splash = []
colour = ["recolored"]
checked_pass = []
[dependencies]
lazy_static = "1.4"

@ -37,6 +37,40 @@ You can set the number of children with `--max-children <number>`, and/or you ca
You can specify the max recursion depth with `--recursive <number>`, or set it to unlimited with `-r`.
### Passing arguments to the children
You can also pass arguments through to the children with `--passthrough` or `-p`, and also with the ones aliased here.
The full list of arguments can be found here <[https://github.com/JayXon/Leanify#usage]>.
| Option | Description |
|------------------------------|----------------------------------------------|
| `-i`, `--iteration <number>` | Number of iterations |
| `-d`, `--max_depth <number>` | Max depth of leanify's archive compressesion |
| `-f`, `--fastmode` | Fast mode, no recompression |
| `-q`, `--quiet` | No output to stdout |
| `-v`, `--verbose` | Verbose output |
| `--keep-exif` | Do not remove exif |
The following are the same:
``` shell
$ leanify-many --passthrough '--max_depth 10 -f -v' -m files/
$ leanify-many --max_depth 10 -f -v -m files/
```
But, the following is not:
``` shell
$ leanify-many --passthrough '--max_depth 0 -f -v' -m files/
$ leanify-many --max_depth 0 -f -v -m files/
```
Without the `checked_pass` feature enabled, the top command will cause all subprocesses to silently error, since `0` is not a valid argument for `--max_depth`. The same is true of any invalid arguments passed to `--passthrough`.
The other aliases, however, make these checks for validity before starting the subprocesses.
If you want to pass something 'as is' to the subprocesses, do not enable the `checked_pass` feature flag when building, and pass the args with `--passthrough` instead of using the aliases. Alternatively, if the `checked_pass` feature was enabled when the binary was compiled (check the output of `--help`, and see below for features legend), pass `-P` instead of `-p` or `--passthough`.
### Misc.
| Option | Description | Notes |
@ -49,12 +83,13 @@ You can specify the max recursion depth with `--recursive <number>`, or set it t
## Optional features
There are a few compile-time features that can be enabled/disabled for additional functionality.
| Name | Description | Default |
|------------|----------------------------------------------------|---------|
| `splash` | Show program information when printing help | On |
| `colour` | Enable colouring of certain outputs, like warnings | On |
| `progress` | Enable progress bar | On |
| `threads` | Enable threaded scheduler | Off |
| Name | Description | Default |
|----------------|--------------------------------------------------------|---------|
| `splash` | Show program information when printing help | On |
| `colour` | Enable colouring of certain outputs, like warnings | On |
| `progress` | Enable progress bar | On |
| `threads` | Enable threaded scheduler | Off |
| `checked_pass` | Check the arguments sent to leanify with `passthrough` | Off |
When building with Rust nightly, some other optimisations and features will be present.

@ -0,0 +1,3 @@
Add `collect_stderr` feature, to not immediately print stderr, but collect it like stdout.
Add `--install` mode with feature flag to enable using it

@ -8,7 +8,7 @@ use std::{
PathBuf,
Path,
},
marker::PhantomData,
borrow::Cow,
};
use lazy_static::lazy_static;
@ -162,7 +162,8 @@ fn comp_flags()
check!(on "splash", "Show splash-screen");
check!(on "colour", "Enable coloured output");
check!(on "progress", "Enable progress bar");
check!(off "threads", "Enable threaded scheduler (usually not needed).");
check!(off "threads", "Enable threaded scheduler (usually not needed)");
check!(off "checked_pass", "Check the arguments passed with `--passthrough` to leanify. By default they are passed as is");
}
pub fn usage() -> !
@ -179,6 +180,15 @@ OPTIONS:
-m Limit subprocesses to number of CPU cores.
--recursive <number> Recurse up to `<number>` times. Must be at least `1`. Default is off.
-r Resurse infinitely.
-p, --passthrough <args> Pass these args to the leanify subprocesses. See <https://github.com/JayXon/Leanify#usage> for details.
-P <args> Same as `--passthrough`, except do not check the passed args (does nothing without the `checked_pass` feature enabled.
Additionally, these leanify flags can be passed directly:
-i, --iteration <number> Number of iterations
-d, --max_depth <number> Max depth of leanify's archive compression
-f, --fastmode Fast mode, no recompression
-q, --quiet No output to stdout
-v, --verbose Verbose output
--keep-exif Do not remove Exif
{extra}
ENVIRONMENT VARS:
LEANIFY=<process name> Path to leanify executable, defaults to looking in `PATH' if set.
@ -196,7 +206,8 @@ pub enum Error {
NoExist(PathBuf),
Walking(dir::Error),
Other(&'static str),
Other(Cow<'static, str>),
UnknownArg(String),
NoFiles,
}
impl std::error::Error for Error{}
@ -223,6 +234,7 @@ impl std::fmt::Display for Error
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),
Self::UnknownArg(arg) => write!(f, "unknown argument `{}'", arg),
Self::Other(msg) => write!(f, "{}", msg),
}
}
@ -237,7 +249,6 @@ impl From<ParseIntError> for Error
}
}
/// Any extra options
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Flags
@ -248,9 +259,8 @@ pub struct Flags
#[cfg(feature="colour")] pub coloured: Option<bool>,
/// Limit max children to this number
pub hard_limit: Option<NonZeroUsize>,
/// To shut the linter up when we have no elements here
_data: PhantomData<()>,
/// Other flags to pass to the `leanify` subprocesses
pub leanify_flags: flags::LeanifyFlags,
}
impl Default for Flags
@ -262,8 +272,7 @@ impl Default for Flags
#[cfg(feature="progress")] progress: true,
#[cfg(feature="colour")] coloured: None,
hard_limit: None,
_data: PhantomData,
leanify_flags: Default::default(),
}
}
}
@ -334,7 +343,7 @@ where I: IntoIterator<Item=T>,
"-m" => {
cfg.flags.hard_limit = NonZeroUsize::new(num_cpus::get());
if cfg.flags.hard_limit.is_none() {
return Err(Error::Other("-m: Could not determine number of CPUs, try setting max children manually with `--max-children`"));
return Err(Error::Other(Cow::Borrowed("-m: Could not determine number of CPUs, try setting max children manually with `--max-children`")));
}
continue;
},
@ -366,7 +375,53 @@ where I: IntoIterator<Item=T>,
reading= false;
continue;
},
_ => (),
"-p" | "--passthrough" => {
if let Some(pass) = args.next() {
cfg_if! {
if #[cfg(feature="checked_pass")] {
if arg == "-P" {
cfg.flags.leanify_flags.add(flags::LeanifyFlag::Custom(pass.split(" ").filter_map(|x| {
match x.trim() {
y if y.len()< 1 => None,
x => Some(x.to_owned())
}
}).collect()));
} else {
let mut args = pass.split(" ").map(|x| x.to_owned());
while let Some((args, orig)) = args.next().map(|x| (flags::LeanifyFlag::try_parse(&x, &mut args), x)) {
if let Some(args) = args? {
cfg.flags.leanify_flags.add(args);
} else {
return Err(Error::UnknownArg(orig));
}
}
}
} else {
cfg.flags.leanify_flags.add(flags::LeanifyFlag::Custom(pass.split(" ").filter_map(|x| {
match x.trim() {
y if y.len()< 1 => None,
x => Some(x.to_owned())
}
}).collect()));
}
}
continue;
}
},
// Passthrough
passthrough => {
match flags::LeanifyFlag::try_parse(passthrough, &mut args) {
Ok(Some(flag)) => {
cfg.flags.leanify_flags.add(flag);
continue;
},
Err(err) => {
return Err(err);
},
_ => ()
}
},
}
}
reading = false;

@ -0,0 +1,163 @@
use super::*;
use std::{
collections::HashSet,
num::NonZeroUsize,
hash::{Hash, Hasher},
borrow::Cow,
};
/// Flags for `leanify`
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum LeanifyFlag
{
/// Corresponds to `-i`
Iteration(NonZeroUsize),
/// Corresponds to `-d`
Depth(NonZeroUsize),
/// Corresponds to `-f`
Fastmode,
/// Corresponds to `-q`
Quiet,
/// Corresponds to `-v`
Verbose,
/// Corresponds to `--keep-exif`
KeepExif,
/// Any other the user specified
Custom(Vec<String>),
}
impl Hash for LeanifyFlag {
fn hash<H: Hasher>(&self, state: &mut H) {
// Don't let us pass multiple of the same discriminant when their inner values are different
if let Self::Custom(custom) = self {
// Except custom
custom.hash(state)
}
std::mem::discriminant(self).hash(state);
}
}
impl LeanifyFlag
{
pub fn try_parse<P: AsRef<str>,T: Iterator<Item=S> + ?Sized, S: AsRef<str>>(path: P, iter: &mut T) -> Result<Option<Self>, arg::Error>
{
Ok(match path.as_ref() {
"-i" | "--iteration" => {
if let Some(iter) = iter.next() {
Some(Self::Iteration(iter.as_ref().parse()?))
} else {
None
}
},
"-d" | "--max_depth" => {
if let Some(iter) = iter.next() {
Some(Self::Depth(iter.as_ref().parse()?))
} else {
None
}
},
"-f" | "--fastmode" => {
Some(Self::Fastmode)
},
"-q" | "--quiet" => {
Some(Self::Quiet)
},
"-v" | "--verbose" => {
Some(Self::Verbose)
},
"--keep-exif" => {
Some(Self::KeepExif)
},
_ => None,
})
}
fn try_into_string(self) -> Option<String>
{
Some(match self {
Self::Iteration(iter) => format!("--iteration {}", iter),
Self::Depth(depth) => format!("--max_depth {}", depth),
Self::Fastmode => format!("-f"),
Self::Quiet => format!("-q"),
Self::Verbose => format!("-v"),
Self::KeepExif => format!("--keep-exif"),
Self::Custom(string) => string.into_iter().join(" "),
})
}
}
impl IntoIterator for LeanifyFlag
{
type Item= Cow<'static, str>;
type IntoIter = maybe_single::IntoIter<Cow<'static, str>>;
fn into_iter(self) -> Self::IntoIter
{
use maybe_single::MaybeSingle;
match self {
Self::Iteration(iter) => MaybeSingle::from(vec![Cow::Borrowed("--iteration"), Cow::Owned(iter.to_string())]),
Self::Depth(depth) => MaybeSingle::from(vec![Cow::Borrowed("--max_depth"), Cow::Owned(depth.to_string())]),
Self::Fastmode => MaybeSingle::single(Cow::Borrowed("-f")),
Self::Quiet => MaybeSingle::single(Cow::Borrowed("-q")),
Self::Verbose => MaybeSingle::single(Cow::Borrowed("-v")),
Self::KeepExif => MaybeSingle::single(Cow::Borrowed("--keep-exif")),
Self::Custom(string) => string.into_iter().map(|x| Cow::Owned(x)).collect(),
}.into_iter()
}
}
/// Extra flags to pass to `leanify`
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LeanifyFlags
{
flags: HashSet<LeanifyFlag>,
}
impl Default for LeanifyFlags
{
#[inline]
fn default() -> Self
{
Self{flags:HashSet::new()}
}
}
impl LeanifyFlags
{
/// Create a new empty flag list
#[inline] pub fn new() -> Self
{
Self::default()
}
/// Add one
pub fn add(&mut self, flag: LeanifyFlag)
{
self.flags.insert(flag);
}
/// Create the `leanify` arg string
pub fn into_arg_string(self) -> String
{
self.flags.into_iter().filter_map(|x| x.try_into_string()).join(" ")
}
pub fn iter_cloned<'a>(&'a self) -> impl Iterator<Item = Cow<'static, str>> + 'a
{
self.flags.iter().map(|x| x.clone().into_iter()).flatten()
}
}
pub type IntoIter = std::iter::FilterMap<std::collections::hash_set::IntoIter<LeanifyFlag>, fn (LeanifyFlag) -> Option<String>>; //fuck this is annoying
impl IntoIterator for LeanifyFlags
{
type Item= String;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter
{
self.flags.into_iter().filter_map(|x| x.try_into_string())
}
}

@ -14,6 +14,7 @@ pub use ext::JoinStrsExt as _;
#[cfg(feature="splash")]
mod splash;
mod flags;
mod arg;
mod error;
pub use error::{ErrorExt as _, ResultExt as _};

@ -1,4 +1,5 @@
//! Handles spawning the process
use super::*;
use tokio::{
process::{
Command,
@ -15,19 +16,52 @@ use tokio::{
};
use std::{
io,
path::Path,
path::{
Path,
PathBuf,
},
};
use cfg_if::cfg_if;
/// Process information
pub struct Process
{
name: PathBuf,
args: flags::LeanifyFlags,
}
impl Process
{
#[inline] pub fn new(name: impl Into<PathBuf>, args: flags::LeanifyFlags) -> Self
{
Self {
name: name.into(),
args: args
}
}
}
impl AsRef<Path> for Process
{
fn as_ref(&self) -> &Path
{
Path::new(&self.name)
}
}
/// Spawn the process, and contain its standard output.
///
/// # Notes
/// Standard error is printed immediately instead.
pub async fn contained_spawn<T,U,V>(process: T, args: U, mut output_to: mpsc::Sender<(bool, String)>) -> Result<(), Error>
where T: AsRef<Path>,
U: IntoIterator<Item=V>,
V: AsRef<std::ffi::OsStr>
where U: IntoIterator<Item=V>,
V: AsRef<std::ffi::OsStr>,
T: AsRef<Process>
{
let Process{
name: process,
args: process_args
} = process.as_ref();
cfg_if!{
if #[cfg(feature="progress")] {
let stderr = std::process::Stdio::piped();
@ -35,7 +69,9 @@ where T: AsRef<Path>,
let stderr = std::process::Stdio::inherit();
}
};
let mut child = match Command::new(process.as_ref())
let mut child = match Command::new(process)
.args(process_args.iter_cloned().map(|x| x.into_owned())) //Do we need `into_owned()` here?
.args(args)
.stdout(std::process::Stdio::piped())
.stderr(stderr)
@ -46,6 +82,7 @@ where T: AsRef<Path>,
return Err(Error::Spawning(sp));
}
};
let stdout = child.stdout.take().unwrap();
#[cfg(feature="progress")] let stderr_sender = {
let stderr = child.stderr.take().unwrap();

@ -6,7 +6,7 @@ use std::{
Send,
Sync,
},
path::{Path,PathBuf,},
path::PathBuf,
ffi::OsStr,
};
#[allow(unused_imports)]
@ -24,6 +24,7 @@ use futures::future::{
Future,
OptionFuture,
};
use process::Process;
async fn maybe_await<T>(from: Option<T>) -> Option<<T as Future>::Output>
where T: Future
@ -42,9 +43,8 @@ type ProgressSender = ();
#[allow(unused_mut)]
#[allow(unused_variables)]
async fn do_work(process: impl AsRef<Path>, file: impl AsRef<OsStr>, mut prog: ProgressSender) -> Result<fixed_stack::IntoIter<String>, process::Error>
async fn do_work(process: impl AsRef<Process>, file: impl AsRef<OsStr>, mut prog: ProgressSender) -> Result<fixed_stack::IntoIter<String>, process::Error>
{
let process = process.as_ref();
let file = file.as_ref();
let (tx, mut rx) = mpsc::channel::<(bool, String)>(16);
@ -82,7 +82,7 @@ async fn do_work(process: impl AsRef<Path>, file: impl AsRef<OsStr>, mut prog: P
}
}
pub async fn work<I,T,U>(#[allow(unused_variables)] flags: &arg::Flags, process: U, files: I, children: Option<NonZeroUsize>) -> Result<(), Box<dyn std::error::Error>>
pub async fn work<I,T,U>(flags: &arg::Flags, process: U, files: I, children: Option<NonZeroUsize>) -> Result<(), Box<dyn std::error::Error>>
where I: IntoIterator<Item=T>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
T: AsRef<OsStr> + Send + Sync + 'static + Clone,
@ -90,8 +90,7 @@ where I: IntoIterator<Item=T>,
{
let (tx,mut rx) = mpsc::channel::<(T, fixed_stack::IntoIter<String>, usize)>(children.as_ref().map(|&x| usize::from(x)).unwrap_or(16));
let semaphore = children.map(|children| Arc::new(Semaphore::new(children.into())));
let process = Arc::new(process.into());
let process = Arc::new(Process::new(process, flags.leanify_flags.clone()));
let files = files.into_iter();
#[cfg(feature="progress")] let (mut progress, prog_handle) = {
@ -147,7 +146,7 @@ where I: IntoIterator<Item=T>,
let worker = {
cfg_if!{
if #[cfg(feature="progress")] {
let worker = do_work(process.as_ref(), &filename, progress.clone());
let worker = do_work(&process, &filename, progress.clone());
let task = progress.add_task(format!("{:?}", filename.as_ref()));
future::join(task, worker).await
} else {

Loading…
Cancel
Save