From 2becd03c6dbb30df3c45548b38ba50003f21df9d Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 8 Aug 2020 20:13:59 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + Cargo.lock | 512 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 22 +++ build.rs | 24 +++ src/arg.rs | 148 ++++++++++++++ src/error.rs | 68 +++++++ src/leanify.rs | 80 ++++++++ src/main.rs | 45 +++++ src/process.rs | 34 ++++ src/splash.rs | 16 ++ src/stage.rs | 131 +++++++++++++ src/work.rs | 70 +++++++ 12 files changed, 1152 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/arg.rs create mode 100644 src/error.rs create mode 100644 src/leanify.rs create mode 100644 src/main.rs create mode 100644 src/process.rs create mode 100644 src/splash.rs create mode 100644 src/stage.rs create mode 100644 src/work.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2a3069 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*~ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9b00544 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,512 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-executor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leanify-many" +version = "0.1.0" +dependencies = [ + "futures", + "lazy_static", + "rustc_version", + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + +[[package]] +name = "pin-project" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "signal-hook-registry" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "syn" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tokio" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..411762f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "leanify-many" +version = "0.1.0" +description = "spawn leanify subprocesses" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["splash"] + +threads = ["tokio/rt-threaded"] +splash = [] + +[dependencies] +lazy_static = "1.4" +tokio = {version = "0.2", features= ["rt-core", "rt-util", "macros", "io-driver", "io-util", "io-std", "process", "sync", "stream"]} +futures = "0.3" + +[build-dependencies] +rustc_version = "0.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/arg.rs b/src/arg.rs new file mode 100644 index 0000000..782faa4 --- /dev/null +++ b/src/arg.rs @@ -0,0 +1,148 @@ +use super::*; +use std::{ + num::{ + NonZeroUsize, + ParseIntError, + }, +}; +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[..] +} + +pub fn usage() -> ! +{ + #[cfg(feature="splash")] + splash::print(); + + println!(r"Usage: {prog} [--max-children ] [-] +Usage: {prog} --help + +OPTIONS: + --max-children Max subprocesses allowed to live at once. Infinite by default. + +ENVIRONMENT VARS: + LEANIFY= Path to leanify executable, defaults to looking in `PATH' if set.", prog = program_name()); + + std::process::exit(1) +} + +#[derive(Debug)] +pub enum Error { + #[cfg(nightly)] BadNumber(std::num::IntErrorKind), + #[cfg(not(nightly))] BadNumber(()), + 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::NoFiles => write!(f, "need at least one argument") + } + } +} + +impl From for Error +{ + fn from(_er: ParseIntError) -> Self + { + #[cfg(nightly)] return Self::BadNumber(_er.kind().clone()); + #[cfg(not(nightly))] Self::BadNumber + } +} + + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config +{ + pub max_children: Option, + pub files: Vec, +} + +impl Default for Config +{ + #[inline] + fn default() -> Self + { + Self { + max_children: None, + files: Vec::new(), + } + } +} + +/// Parse the `env::args()` +#[inline] pub fn parse_args() -> Result +{ + let args = std::env::args(); + if args.len() <= 1 { + println!("Warning: No arguments specified, try passing `--help`."); + } + parse(args.skip(1)) +} + +fn parse(args: I) -> Result +where I: IntoIterator, + T: Into +{ + 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; + } + } + "-" => { + reading= false; + continue; + }, + _ => (), + } + } + reading = false; + cfg.files.push(arg); + } + if cfg.files.len() == 0 { + return Err(Error::NoFiles); + } + Ok(cfg) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..d49032d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,68 @@ +use std::{ + error, + fmt, +}; + +#[derive(Debug)] +pub struct PrefixedError +where T: error::Error + 'static +{ + prefix: String, + internal: T, +} +impl std::error::Error for PrefixedError +where T: error::Error + 'static +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> + { + Some(&self.internal) + } +} +impl std::fmt::Display for PrefixedError +where T: error::Error + 'static +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}: {}", self.prefix, self.internal) + } +} + + +impl PrefixedError +where T: error::Error + 'static +{ + #[inline] pub fn into_inner(self) -> T + { + self.internal + } +} + +pub trait ErrorExt: Sized + error::Error +{ + fn with_prefix(self, msg: impl ToString) -> PrefixedError; +} +pub trait ResultExt: Sized + where E: error::Error + 'static +{ + fn with_prefix(self, msg: impl ToString) -> Result>; +} + +impl ErrorExt for T +where T: error::Error + 'static +{ + #[inline] fn with_prefix(self, msg: impl ToString) -> PrefixedError + { + PrefixedError { + prefix: msg.to_string(), + internal: self + } + } +} +impl ResultExt for Result +where E: error::Error + 'static +{ + #[inline] fn with_prefix(self, msg: impl ToString) -> Result> + { + self.map_err(|e| e.with_prefix(msg)) + } +} diff --git a/src/leanify.rs b/src/leanify.rs new file mode 100644 index 0000000..8c75763 --- /dev/null +++ b/src/leanify.rs @@ -0,0 +1,80 @@ +use super::*; +use std::{ + fmt, + borrow::Cow, + path::{ + Path, + PathBuf, + }, +}; + +const DEFAULT_BINARY_NAME: &'static str = "leanify"; + +#[derive(Debug)] +pub enum Error +{ + FileFailed(PathBuf), + PathFailed(PathBuf), +} + +impl std::error::Error for Error{} +impl std::fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::FileFailed(path) => write!(f, "file {:?} does not exist.", path), + Self::PathFailed(path) => write!(f, "no directory in `$PATH` contained file {:?}", path), + } + } +} + +fn binary_name() -> Cow<'static, str> +{ + if let Ok(bin_name) = std::env::var("LEANIFY") { + if bin_name.len() > 0 { + return Cow::Owned(bin_name); + } + } + return Cow::Borrowed(DEFAULT_BINARY_NAME); +} + +fn get_path() -> Vec +{ + if let Ok(path) = std::env::var("PATH") + { + path.split(":").filter_map(|x| { + let path = Path::new(x); + if path.is_dir() { + Some(path.to_owned()) + } else { + None + } + }).collect() + } else { + Vec::default() + } +} + +/// Find the path to leanify binary +pub fn find_binary() -> Result +{ + let path = binary_name(); + let path = Path::new(path.as_ref()); + + if path.is_file() { + Ok(path.to_owned()) + } + else if !path.is_absolute() { + //Lookup in $PATH + for spath in get_path().into_iter() { + let spath = spath.join(path); + if spath.is_file() { + return Ok(spath); + } + } + Err(Error::PathFailed(path.to_owned())) + } else { + Err(Error::FileFailed(path.to_owned())) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..394c115 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,45 @@ +#![cfg_attr(nightly, feature(int_error_matching))] + +#![allow(dead_code)] + + +#[cfg(feature="splash")] +mod splash; +mod arg; +mod error; +pub use error::{ErrorExt as _, ResultExt as _}; + +mod stage; +mod leanify; + +mod process; +mod work; + +async fn work() -> Result<(), Box> +{ + let args = arg::parse_args().with_prefix("failed to parse args")?; + let leanify = leanify::find_binary().with_prefix("Couldn't find leanify binary")?; + + + + Ok(()) +} + + +#[tokio::main] +async fn main() { + prettify_expect(work().await.map_err(|e| e.to_string()), "exited with error"); +} + +#[inline] fn prettify_expect(res: Result, msg: S) -> T +where S: AsRef, + E: std::fmt::Display +{ + match res { + Ok(v) => v, + Err(e) => { + eprintln!("\n{}: {}", msg.as_ref(), e); + std::process::exit(1) + }, + } +} diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..59396ad --- /dev/null +++ b/src/process.rs @@ -0,0 +1,34 @@ +//! Handles spawning the process + +/// Spawn the process, and contain its standard output. +/// +/// # Notes +/// Standard error is printed immediately instead. +pub async fn contained_spawn(process: T, args: U) -> Result +where T: AsRef, + U: IntoIterator, + T: AsRef +{ + +} + +#[derive(Debug)] +pub enum Error { + /// There was an error spawning the process + Spawning, + /// Process exited with non-zero error code. + Process, +} +impl std::error::Error for Error{} +impl std::fmt::Display for Error +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self { + Self::Spawning => write!(f, "there was an error spawning the process"), + Self::Process => write!(f, "process exited with non-zero code"), + } + } +} + + diff --git a/src/splash.rs b/src/splash.rs new file mode 100644 index 0000000..d820be6 --- /dev/null +++ b/src/splash.rs @@ -0,0 +1,16 @@ +use lazy_static::lazy_static; + +pub fn authors() -> &'static [&'static str] +{ + lazy_static! { + static ref AUTHORS: Vec<&'static str> = env!("CARGO_PKG_AUTHORS").split(":").collect(); + } + + &AUTHORS[..] +} + +#[inline] pub fn print() +{ + println!("{} version {} -- {}.\n Created with <3 by {}\n Licensed with GPL 3.0 or later\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_DESCRIPTION"), authors().join(", ")); + +} diff --git a/src/stage.rs b/src/stage.rs new file mode 100644 index 0000000..78c7a97 --- /dev/null +++ b/src/stage.rs @@ -0,0 +1,131 @@ +//! Supports sending many messages from many producers to many consumers, first come first served. + +use std::{ + collections::LinkedList, + sync::{Arc, Weak}, + iter::FromIterator, +}; +use tokio::{ + sync::{ + RwLock, + }, + task::yield_now, + stream::Stream, +}; + +#[derive(Debug, Clone)] +pub struct Stage +{ + internal: Arc>>, +} + +#[derive(Debug, Clone)] +pub struct StageSender { + internal: Weak>>, +} + +impl StageSender +{ + /// Send a message to the queue, returns `Ok(())` if possible, `Err(value)` if failed. + pub async fn send(&self, value: T) -> Result<(), T> + { + loop { + if let Some(internal) = self.internal.upgrade() { + let mut write = internal.write().await; + break Ok(write.push_back(value)); + } else { + break Err(value); + } + } + } +} + +impl Stage +{ + /// Create a new stage from an input + #[inline] fn from_iter(from: I) -> Self + where I: IntoIterator + { + Self { + internal: Arc::new(RwLock::new(from.into_iter().collect())) + } + } + + /// Create a new empty stage + pub fn new() -> Self + { + Self{ + internal: Arc::new(RwLock::new(LinkedList::new())), + } + } + + /// Create a new sender + pub fn sender(&self) -> StageSender + { + StageSender { + internal: Arc::downgrade(&self.internal), + } + } + + /// Receive one. + /// + /// If there are no senders, this will return `None` + pub async fn take(&self) -> Option + { + loop { + { + let mut write = self.internal.write().await; + if let Some(value) = write.pop_front() { + return Some(value); + } else if Arc::weak_count(&self.internal) < 1 { + return None; + } + } + yield_now().await + } + } + + /// Current backlog length + pub async fn len(&self) -> usize { + self.internal.read().await.len() + } + + /// Number of active senders + pub fn senders(&self) -> usize { + Arc::weak_count(&self.internal) + } + + /// Number of active receivers + pub fn receivers(&self) -> usize { + Arc::strong_count(&self.internal) + } +} + +impl FromIterator for Stage +{ + fn from_iter>(iter: I) -> Self { + Self::from_iter(iter) + } +} + +use std::{ + task::{Context, Poll}, + pin::Pin, + future::Future, +}; + +impl Stream for Stage +{ + type Item=T; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> + { + let future= async { + while let Some(value) = self.take().await { + return Some(value); + } + None + }; + tokio::pin!(future); + future.poll(cx) + } +} diff --git a/src/work.rs b/src/work.rs new file mode 100644 index 0000000..0a38253 --- /dev/null +++ b/src/work.rs @@ -0,0 +1,70 @@ +use super::*; +use std::{ + num::NonZeroUsize, + sync::Arc, + pin::Pin, + marker::{ + Send, + }, +}; +use tokio::{ + prelude::*, + stream::StreamExt, + sync::{ + Semaphore, + }, +}; +use futures::future::{ + join_all, + Future, +}; + +pub async fn maybe_await(from: Option) -> Option<::Output> +where T: Future +{ + if let Some(v) = from { + Some(v.await) + } else { + None + } +} + +pub async fn do_work(process: impl AsRef, file: impl AsRef) +{ + let process = process.as_ref(); + let file = file.as_ref(); + + match process::contained_spawn(process, std::iter::once(file)).await { + Ok(output) => { + + }, + Err(process::Error::Spawning) => { + + }, + Err(process::Error::Process) => { + + }, + } + +} + +pub async fn work(process: String, files: I, children: Option) -> Result<(), Box> +where I: IntoIterator, + T: AsRef + Send + 'static +{ + //let mut stage: stage::Stage = files.into_iter().map(|x| x.into()).collect(); + let semaphore = children.map(|children| Arc::new(Semaphore::new(children.into()))); + let process = Arc::new(process); + + join_all(files.into_iter() + .map(|filename| { + let semaphore = semaphore.clone(); + let process = Arc::clone(&process); + tokio::spawn(async move { + let _lock = maybe_await(semaphore.map(|x| x.acquire_owned())).await; + do_work(&process[..], filename).await; + }) + })).await; + + Ok(()) +}