Compare commits

..

No commits in common. 'master' and 'dedup' have entirely different histories.

173
Cargo.lock generated

@ -15,6 +15,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aho-corasick"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
@ -155,6 +164,19 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "eyre" name = "eyre"
version = "0.6.0" version = "0.6.0"
@ -292,17 +314,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.22.0" version = "0.22.0"
@ -318,6 +329,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]] [[package]]
name = "indenter" name = "indenter"
version = "0.3.0" version = "0.3.0"
@ -343,12 +363,6 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "lazy_format"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f389fd31a70d3b0f1d9d80e8f7a9366127fe5fb819f4d4dd21122a167352da8f"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -540,10 +554,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "ppv-lite86" name = "pretty_env_logger"
version = "0.2.10" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
@ -566,6 +584,12 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.7" version = "1.0.7"
@ -576,62 +600,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rand" name = "redox_syscall"
version = "0.7.3" version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "recolored" name = "regex"
version = "1.9.3" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1584c92dd8a87686229f766bb3a62d263a90c47c81e45a49f1a6d684a1b7968d" checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
dependencies = [ dependencies = [
"atty", "aho-corasick",
"lazy_static", "memchr",
"winapi 0.3.9", "regex-syntax",
"thread_local",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "regex-syntax"
version = "0.1.57" version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
@ -734,23 +724,12 @@ dependencies = [
] ]
[[package]] [[package]]
name = "terminal_size" name = "termcolor"
version = "0.1.13" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a14cd9f8c72704232f0bfc8455c0e861f0ad4eb60cc9ec8a170e231414c1e13" checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [ dependencies = [
"libc", "winapi-util",
"winapi 0.3.9",
]
[[package]]
name = "termprogress"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6611ecf7fedefdb0f74d6194da1918f15c00ef97ae4bbd1f60a9c7ca2ae0eb14"
dependencies = [
"rustc_version",
"terminal_size",
] ]
[[package]] [[package]]
@ -871,15 +850,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.2" version = "0.9.2"
@ -895,23 +865,15 @@ dependencies = [
"chrono", "chrono",
"color-eyre", "color-eyre",
"futures", "futures",
"lazy_format",
"lazy_static", "lazy_static",
"recolored", "log",
"pretty_env_logger",
"rustc_version", "rustc_version",
"sha2", "sha2",
"smallmap", "smallmap",
"termprogress",
"tokio", "tokio",
"uuid",
] ]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" version = "0.2.8"
@ -940,6 +902,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

@ -15,7 +15,9 @@ threads = ["tokio/rt-threaded"]
# Use base64 encoding of pathnames instead of SHA256. This can increase speed of database rebuilding, but can also cause files with large pathnames to fail. # Use base64 encoding of pathnames instead of SHA256. This can increase speed of database rebuilding, but can also cause files with large pathnames to fail.
fast-pathnames = ["base64"] fast-pathnames = ["base64"]
# Use low-precision argument dedup (64-bit hash) instead of high (256-bit hash) # Use low-precision argument dedup (64-bit) instead of high (unimplemented)
#
# Currently does nothing as this is the default and only implemented one.
low-prec-arg-dedup = [] low-prec-arg-dedup = []
[dependencies] [dependencies]
@ -28,10 +30,8 @@ color-eyre = "0.5.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
futures = "0.3.6" futures = "0.3.6"
smallmap = "^1.1.6" smallmap = "^1.1.6"
termprogress = "0.3.4" log = "0.4.11"
uuid = {version = "0.8.1", features=["v4"]} pretty_env_logger = "0.4.0"
lazy_format = "1.8.3"
recolored = "1.9.3"
[build-dependencies] [build-dependencies]
rustc_version = "0.2" rustc_version = "0.2"

@ -1 +1,2 @@
Rework logging macro calls to use new progress API Add lolistealer-like async progress bar handler to State (check state.rs)

@ -24,7 +24,7 @@ pub fn program_name() -> &'static str
/// Process program args in parallel spawning the `callback` closure of the argument in a new task for each. /// Process program args in parallel spawning the `callback` closure of the argument in a new task for each.
/// ///
/// The returned future can be awaited to wait for all tasks to complete. If one or more tasks are cancelled or panic, this future will immediately output `Err()`, if they all complete successfully, it will output an aggregate `Vec` of the output of each argument in order. /// The returned future can be awaited to wait for all tasks to complete. If one or more tasks are cancelled or panic, this future will immediately output `Err()`, if they all complete successfully, it will output an aggregate `Vec` of the output of each argument in order.
pub fn process<F, T>(mut callback: F) -> (usize, impl Future<Output= eyre::Result<Vec<T::Output>>>) pub fn process<F, T>(mut callback: F) -> impl Future<Output= eyre::Result<Vec<T::Output>>>
where F: FnMut(String) -> T, where F: FnMut(String) -> T,
T: Future + Send + 'static, T: Future + Send + 'static,
T::Output: Send, T::Output: Send,
@ -32,7 +32,7 @@ where F: FnMut(String) -> T,
let args = std::env::args(); let args = std::env::args();
let output: Vec<_> = args.skip(1).dedup().map(|arg| tokio::spawn(callback(arg))).collect(); let output: Vec<_> = args.skip(1).dedup().map(|arg| tokio::spawn(callback(arg))).collect();
let mut real_output = Vec::with_capacity(output.len()); let mut real_output = Vec::with_capacity(output.len());
(output.len(), async move { async move {
let mut j=0; let mut j=0;
for x in futures::future::try_join_all(output).await for x in futures::future::try_join_all(output).await
.wrap_err(eyre!("Child panic or cancel.")) .wrap_err(eyre!("Child panic or cancel."))
@ -43,5 +43,5 @@ where F: FnMut(String) -> T,
j+=1; j+=1;
} }
Ok(real_output) Ok(real_output)
}) }
} }

@ -25,55 +25,48 @@ use futures::{
/// Process a single file. /// Process a single file.
/// ///
/// `path` is known to be a file at this point. /// `path` is known to be a file at this point.
async fn process_single(state: Arc<state::State>, logger: &mut progress::logging::Logger, path: impl AsRef<Path>) -> eyre::Result<()> async fn process_single(state: Arc<state::State>, path: impl AsRef<Path>) -> eyre::Result<()>
{ {
let path = path.as_ref(); let path = path.as_ref();
debug_assert!(path.is_file(), "process_single() expected a file, but {:?} is not one.", path); #[cfg(debug_assertions)] {
if !path.is_file() {
panic!("process_single() expected a file, but {:?} is not one.", path);
}
}
let _g = state.lock().await; let _g = state.lock().await;
trace!(logger => "{:?} Processing", path); debug!("{:?} Processing", path);
//TODO: Actual processing
Ok(()) Ok(())
} }
/// Process this path /// Process this path
/// ///
/// This will not return until all its children finish too (if any) /// This will not return until all its children finish too (if any)
pub async fn process<'a, P, L>(state: Arc<state::State>, mut logger: L, path: P) -> eyre::Result<()> pub async fn process<'a, P>(state: Arc<state::State>, path: P) -> eyre::Result<()>
where P: 'a + Send + AsRef<Path>, where P: 'a + Send + AsRef<Path>
L: std::borrow::BorrowMut<progress::logging::Logger>
{ {
let path = path.as_ref(); let path = path.as_ref();
if path.is_dir() { if path.is_dir() {
let read = fs::read_dir(path).await?; let read = fs::read_dir(path).await?;
fn proc_dir(state: Arc<state::State>, logger: &mut progress::logging::Logger, mut read: fs::ReadDir) -> BoxFuture<'static, JoinHandle<Result<(), eyre::Report>>> fn proc_dir(state: Arc<state::State>, mut read: fs::ReadDir) -> BoxFuture<'static, JoinHandle<Result<(), eyre::Report>>>
{ {
let mut logger = logger.clone();
async move { async move {
tokio::spawn(async move { tokio::spawn(async move {
while let Some(entry) = read.next_entry().await? while let Some(entry) = read.next_entry().await?
{ {
let path = entry.path(); process(Arc::clone(&state), entry.path()).await?;
if let Err(error) = process(Arc::clone(&state), &mut logger, &path).await {
{
let err = &error;
let path = &path; //TODO: Get these macros to stop moving
error!(logger => "{:?} child failed: {}", path, err);
}
debug!(logger => "Error for {:?}: {:?}", path, error);
}
} }
Ok::<_, eyre::Report>(()) Ok::<_, eyre::Report>(())
}) })
}.boxed() }.boxed()
} }
let handle = proc_dir(state, logger.borrow_mut(), read).await; let handle = proc_dir(state, read).await;
let res = handle.await let res = handle.await
.wrap_err(eyre!("Child exited abnormally"))?; .wrap_err(eyre!("Child exited abnormally"))?;
res.wrap_err(eyre!("Failed to process children")) res.wrap_err(eyre!("Failed to process children"))
} else if path.is_file() { } else if path.is_file() {
process_single(state, logger.borrow_mut(), path).await process_single(state, path).await
} else { } else {
Err(eyre!("Invalid/unsupported FSO")) Err(eyre!("Invalid/unsupported FSO"))
}.with_section(|| format!("{:?}", path).header("Path was")) }.with_section(|| format!("{:?}", path).header("Path was"))

@ -11,33 +11,6 @@ use std::{
num::NonZeroU8, num::NonZeroU8,
}; };
pub trait JoinStrsExt: Sized
{
/// Join an iterator of `str` with a seperator
fn join(self, with: &str) -> String;
}
impl<T,I> JoinStrsExt for I
where I: Iterator<Item=T>,
T: AsRef<str>
{
fn join(self, with: &str) -> String
{
let mut output = String::new();
let mut first=true;
for string in self
{
if !first {
output.push_str(with);
}
let string = string.as_ref();
output.push_str(string);
first=false;
}
output
}
}
pub use dedup::DedupIterExt; pub use dedup::DedupIterExt;
/// Iterator that maps `T` -> `U` /// Iterator that maps `T` -> `U`
@ -202,24 +175,3 @@ where I: Iterator<Item = u8>
} }
} }
} }
pub trait IgnoreResultExt
{
fn ignore(self);
}
impl<T,E> IgnoreResultExt for Result<T,E>
{
#[inline(always)] fn ignore(self)
{
//Do nothing
}
}
impl<T> IgnoreResultExt for Option<T>
{
#[inline(always)] fn ignore(self)
{
//Do nothing
}
}

@ -1,429 +0,0 @@
//! CLI flags module
use super::*;
use std::{
borrow::Cow,
any::Any,
error,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Kind<'a>
{
/// Any number of these long-form arguments starting with `--`, and flag for case-sensitivity.
/// Can be empty to match any argument (not needing a prefix).
Long(&'a [&'a str], bool),
/// Any number of these short-form (single character) arguments together or apart (starting with `-`).
/// Can be empty for a single `-` character, and a flag for case-sensitivity.
Short(&'a [(char, )], bool),
/// A single `--`.
Terminator,
/// An exact match with no prefix.
Exact(&'a str),
}
impl Kind<'static>
{
pub fn as_ref<'a>(&'a self) -> Kind<'a>
{
match &self {
Self::Long(strs, bl) => Kind::Long(strs, *bl),
Self::Short(chrs, bl) => Kind::Short(chrs, *bl),
Self::Terminator => Kind::Terminator,
Self::Exact(stri) => Kind::Exact(&stri[..]),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueKind
{
None,
Equals,
Next(usize),
Rest,
}
pub struct ArgState<T=()>
{
user: T,
flags: Vec<(Kind<'static>, Cow<'static, str>, ValueKind, Box<(dyn FlagCallback<T, Box<dyn Any + 'static>, eyre::Report> + 'static)>, bool, bool)>,
}
pub trait FlagCallback<U, T,E>: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>{}
impl<U, F, T, E> FlagCallback<U, T,E> for F
where F: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>,
T: Any + 'static,
E: Into<eyre::Report> {}
impl<U> ArgState<U>
{
/// Push a handle to the state
pub fn push<T: Any + 'static,E: Into<eyre::Report>, F>(&mut self, kind: Kind<'static>, desc: impl Into<Cow<'static, str>>, value: ValueKind, mut handle: F, single: bool) -> usize
where F: FlagCallback<U, T,E> + 'static
{
let handle: Box<dyn FlagCallback<U, Box<(dyn Any + 'static)>, eyre::Report>> =
Box::new(move |state, index, value| handle(state, index, value)
.map(|x| -> Box<(dyn Any + 'static)> {Box::new(x)})
.map_err(Into::into));
self.flags.push((kind, desc.into(), value, handle, single, true));
self.flags.len()-1
}
}
#[derive(Debug, Clone)]
enum Either<T,U>
{
None,
One(T),
Many(U),
}
impl<T,U> Either<T,U>
{
pub fn take(&mut self) -> Self
{
std::mem::replace(self, Self::None)
}
}
#[derive(Debug, Clone)]
pub enum EitherIter<T,U>
{
None,
One(std::iter::Once<T>),
Many(std::iter::Fuse<U>),
}
impl<T,U> Iterator for EitherIter<T,U>
where U: Iterator<Item=T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item>
{
match self {
Self::None => None,
Self::One(one) => one.next(),
Self::Many(many) => many.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::None => (0, Some(0)),
Self::One(_) => (1, Some(1)),
Self::Many(many) => many.size_hint(),
}
}
}
impl<T,U: Iterator<Item=T>> std::iter::FusedIterator for EitherIter<T,U>{}
impl<T,U: Iterator<Item=T>> std::iter::ExactSizeIterator for EitherIter<T,U>
where U: ExactSizeIterator{}
impl<T, U: IntoIterator<Item=T>> IntoIterator for Either<T, U>
{
type Item= T;
type IntoIter = EitherIter<T, <U as IntoIterator>::IntoIter>;
fn into_iter(self) -> Self::IntoIter
{
match self {
Self::None => EitherIter::None,
Self::One(one) => EitherIter::One(std::iter::once(one)),
Self::Many(many) => EitherIter::Many(many.into_iter().fuse())
}
}
}
/// A handle to mutate the arg state.
pub struct StateHandle<'a, T>
{
held: Either<String, Vec<String>>,
state: &'a mut T,
args: &'a mut (dyn Iterator<Item=String> + 'a),
chk: &'a mut bool,
idx: usize,
}
impl<'a, T> StateHandle<'a,T>
{
/// The user defined state
pub fn state(&self) -> &T
{
self.state
}
/// The mutable user defined state
pub fn state_mut(&mut self) -> &mut T
{
self.state
}
/// The current args iterator
pub fn args(&self) -> &(dyn Iterator<Item=String> + 'a)
{
self.args
}
/// The mutable current args iterator
pub fn args_mut(&mut self) -> &mut (dyn Iterator<Item = String> + 'a)
{
self.args
}
/// Is this callback enabled?
///
/// It will always start as `true`. This can be mutated to `false` to disable further checks for this argument.
pub fn enabled_mut(&mut self) -> &mut bool
{
self.chk
}
/// Is this callback enabled?
///
/// This should always be `true`.
pub fn enabled(&mut self) -> bool
{
*self.chk
}
/// The index of the current arg
pub fn index(&self) -> usize
{
self.idx
}
/// The held values taken from the argument iterator as specified by the argument's definition.
///
/// # Note
/// This moves the values, if you want to call this more than once, use `clone_held`.
/// If called more than once will yield no values
pub fn extract_held(&mut self) -> EitherIter<String, std::vec::IntoIter<String>>
{
self.held.take().into_iter()
}
/// Clone the held values taken from the argument iterator for this invokation.
///
/// # Note
/// This clones all the values, instead of moving like `extract_held`, so multiple calls to this will yield the same clones.
/// However, if `extract_held` has been called, this will yield no values.
pub fn clone_held(&self) -> EitherIter<String, std::vec::IntoIter<String>>
{
self.held.clone().into_iter()
}
}
impl<U> ArgState<U>
{
/// Parse this argument iterator.
///
/// # Note
/// This mutates the argument state container indefinately, and multiple calls to it will keep the mutated state.
/// So this might not behave as expected (e.g. callbacks marked `single` that were fired in the first call will not be fired in the second, etc.)
pub fn parse<I,T: Into<String>>(&mut self, input: I) -> Result<Vec<Box<dyn Any+'static>>, ContextualError>
where I: Iterator<Item=T>
{
let mut output = Vec::with_capacity(self.flags.len());
let mut input = input.map(Into::into).fuse();
let mut i=0;
while let Some(arg) = input.next()
{
for (kind, desc, value, callback, single, chk) in self.flags.iter_mut().filter(|(_,_,_,_,_,chk)| *chk)
{
if *single {
*chk = false;
}
//TODO: Check Kind matches `arg`, check `value` matches `input` and get the values.
output.push(callback(&mut StateHandle{
state: &mut self.user,
args: &mut input,
chk,
idx: i,
held: Either::None, //TODO: This will be for either one `=<value>` or many `<values...>` from args as according to `value`.
}, i, &arg[..])
.map_err(|err| Error{flag: kind.clone(), from: err, desc: Some(desc.clone().into_owned()), arg: (i, arg.clone())})?);
}
i+=1;
}
Ok(output)
}
/// Consume into the user-provided state value
#[inline] pub fn into_inner(self) -> U
{
self.user
}
}
/// Error without context
#[derive(Debug)]
pub struct Error
{
flag: Kind<'static>,
arg: (usize, String),
desc: Option<String>,
from: eyre::Report,
}
/// An argument parsing error with context
#[repr(transparent)]
#[derive(Debug)]
pub struct ContextualError(Error);
#[derive(Debug, Clone, Copy)]
pub struct ErrorContext<'a>
{
/// The error-throwing flag
pub flag: &'a Kind<'static>,
/// The argument string
pub arg: &'a String,
/// The index of the argument string
pub arg_idx: &'a usize,
/// Message from the failing flag callback, if any
pub desc: Option<&'a String>,
/// The error report
pub from: &'a eyre::Report,
}
impl ContextualError
{
/// The inner error
#[inline] pub fn inner(&self) -> &Error
{
&self.0
}
/// Strips the context and returns the inner error
#[inline] fn into_inner(self) -> Error
{
self.0
}
/// The context of this error
pub fn context(&self) -> ErrorContext<'_>
{
ErrorContext {
flag: &self.0.flag,
arg: &self.0.arg.1,
arg_idx: &self.0.arg.0,
desc: self.0.desc.as_ref(),
from: &self.0.from
}
}
/// Which argument was the error thrown for
pub fn what(&self) -> (usize, &str)
{
(self.0.arg.0, &self.0.arg.1[..])
}
/// Where did the parsing fail?
pub fn which(&self) -> Kind<'_>
{
self.0.flag.as_ref()
}
/// The message reported by the failing callback, if any
pub fn message(&self) -> Option<&str>
{
self.0.desc.as_ref().map(|x| &x[..])
}
}
impl error::Error for Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.from.source()
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
writeln!(f, "Failed to parse args")
}
}
impl fmt::Display for ContextualError
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(f)
}
}
impl Error
{
/// Consume into an `eyre::Report`.
fn into_report(self) -> eyre::Report
{
let Error {flag, desc, from ,arg: (idx, arg)} = self;
let err = Err::<!, _>(from)
//.wrap_err(eyre!("Failed to parse args"))
.with_section(move || format!("{:?}", flag).header("While attempting to parse for"))
.with_note(move || idx.to_string().header("Argument index was"))
.with_note(move || format!("{:?}", arg).header("Argument was"));
if let Some(desc) = desc {
err.with_suggestion(move || desc)
} else {
err
}.unwrap_err()
}
/// Add context to this error
pub fn with_context(self) -> ContextualError
{
ContextualError(Self {
flag: self.flag.clone(),
arg: self.arg.clone(),
desc: self.desc.clone(),
from: self.into_report(),
})
}
}
impl From<Error> for ContextualError
{
#[inline] fn from(from: Error) -> Self
{
from.with_context()
}
}
impl From<ContextualError> for eyre::Report
{
#[inline] fn from(from: ContextualError) -> Self
{
from.0.from
}
}
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn errors() -> Result<(), eyre::Report>
{
//color_eyre::install()?;
let err= Error {
flag: Kind::Long(&["test"], false),
arg: (0, "owo".to_owned()),
desc: Some("expected between 1-10".to_string()),
from: eyre!("Invalid number").wrap_err("Parsing failure"),
};
println!("No ctx: `{} {:?}`\n", err, err);
let err = err.with_context();
println!("With ctx: `{} {:?}`\n", err, err);
//return Err(err)?;
Ok(())
}
}

@ -1,9 +1,8 @@
#![cfg_attr(nightly, feature(never_type))] #![cfg_attr(nightly, feature(never_type))]
#![cfg_attr(nightly, feature(drain_filter))]
#![allow(dead_code)] #![allow(dead_code)]
//#[macro_use] extern crate log; #[macro_use] extern crate log;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -34,11 +33,9 @@ mod dedup;
mod resolve; mod resolve;
mod database; mod database;
mod flags;
mod args; mod args;
mod config; mod config;
#[macro_use] mod progress;
mod state; mod state;
mod delete; mod delete;
mod restore; mod restore;
@ -53,22 +50,12 @@ cfg_if!{
type Pointer = *const Never; type Pointer = *const Never;
/// Install things and get other things fn install() -> eyre::Result<()>
fn install() -> eyre::Result<(progress::logging::Level,)>
{ {
let logv = std::env::var("RUST_LOG"); pretty_env_logger::init();
color_eyre::install()?; color_eyre::install()?;
let lv = match logv { Ok(())
Ok(log) if log.trim().len() !=0 => log.parse().unwrap_or_else(|e| {
let def = Default::default();
eprintln!("Failed to parse logging level, using default `{:?}`: {}", def, e);
def
}),
_ => Default::default(),
};
Ok((lv,))
} }
/// Currently a mock impl for process /// Currently a mock impl for process
@ -90,28 +77,15 @@ async fn process_mock(state: Arc<state::State>, file: String)
async fn process(state: Arc<state::State>, file: String) -> eyre::Result<()> async fn process(state: Arc<state::State>, file: String) -> eyre::Result<()>
{ {
let path = std::path::Path::new(&file); let path = std::path::Path::new(&file);
let mut progress = state.progress().clone(); if !path.exists() {
let task_id_fut = progress.send_command(progress::CommandKind::AddTask(file.clone())).await?; error!("{:?} does not exist, skipping", path);
let mut logger= state.logger_output();
let res = if !path.exists() {
error!(yield logger => "{:?} does not exist, skipping", path);
Ok(())
} else { } else {
info!(logger => "{:?} Processing", path); info!("{:?} Processing", path);
delete::process(state, &mut logger, path).await delete::process(state, path).await
.wrap_err(eyre!("Processing failed")) .wrap_err(eyre!("Processing failed"))
.with_section(move || file.header("Root path was")) .with_section(move || file.header("Root path was"))?;
}; }
progress.send_command_and_detach(progress::CommandKind::Bump(1)).await?; Ok(())
progress.send_command_and_wait(progress::CommandKind::
RemoveTask(task_id_fut.await?
.map(|x| x.downcast()
.ok()
.map(|x| *x))
.flatten()
.unwrap())).await?;
res
} }
async fn validate_config(config: config::Config) -> eyre::Result<config::Config> async fn validate_config(config: config::Config) -> eyre::Result<config::Config>
@ -126,58 +100,22 @@ async fn validate_config(config: config::Config) -> eyre::Result<config::Config>
async fn begin() -> eyre::Result<i32> async fn begin() -> eyre::Result<i32>
{ {
use futures::prelude::*; install()?;
let (log_level,) = install()?;
{
let (progress, progress_join) = {
let bar = termprogress::progress::Bar::new(50);
let (p, join) = progress::host(bar);
(p, join.map(|bar| match bar {
Ok(bar) => {
bar.complete();
Ok(())
},
Err(panic) => {
println!();
Err(panic)
},
}))
};
let state = {
use progress::logging;
let mut logger = logging::Logger::new(progress,
log_level);
let state = Arc::new(state::State::new(validate_config(config::Config::default()).await let state = Arc::new(state::State::new(validate_config(config::Config::default()).await
.wrap_err(eyre!("Failed to validate config"))?, .wrap_err(eyre!("Failed to validate config"))?));
logger.clone())); info!("Validated config OK");
info!(yield logger => "Validated config OK"); if args::process(|file| {
state
};
if {
let (sz, pro) = args::process(|file| {
let state = Arc::clone(&state); let state = Arc::clone(&state);
use futures::future::TryFutureExt; use futures::future::TryFutureExt;
process(state, file).inspect_err(|err| eprintln!("{:?}", err)) process(state, file).inspect_err(|err| eprintln!("{:?}", err))
}); }).await
state.progress().clone().send_command_and_wait(progress::CommandKind::BumpHigh(sz as isize)).await?;
pro
}.await
.wrap_err(eyre!("One or more child workers failed to complete successfully"))? .wrap_err(eyre!("One or more child workers failed to complete successfully"))?
.len() == 0 .len() == 0
{ {
args::usage(); args::usage();
} }
//Cleanup deferred in new `async` block to drop `state` before this block is ran. Ok(0)
async move {
progress_join.await?;
Ok::<_, eyre::Report>(0)
}.map(|res| res.wrap_err(eyre!("Cleanup failed")))
}.await
} }
#[tokio::main] #[tokio::main]

@ -1,275 +0,0 @@
//! Logging
use super::*;
use std::{
str,
fmt,
error,
};
use recolored::Colorize;
/// The logging level
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Level
{
Trace,
Debug,
Info,
Warning,
Error,
Fatal,
}
impl fmt::Display for Level
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", match self {
Level::Trace => "TRACE".purple(),
Level::Debug => "DEBUG".blue(),
Level::Info => "INFO".green(),
Level::Warning => "WARNING".yellow(),
Level::Error => "ERROR".red(),
Level::Fatal => "FATAL".bright_red(),
})
}
}
impl str::FromStr for Level
{
type Err = LevelParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim().to_lowercase();
Ok(match s.as_str() {
"trace" => Level::Trace,
"debug" => Level::Debug,
"info" => Level::Info,
"warning" => Level::Warning,
"error" => Level::Error,
"fatal" => Level::Fatal,
_ => return Err(LevelParseError(s)),
})
}
}
#[derive(Debug)]
pub struct LevelParseError(String);
impl error::Error for LevelParseError{}
impl fmt::Display for LevelParseError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{:?} is not a valid logging level", self.0)
}
}
impl Level
{
fn is_err(&self) -> bool
{
match self {
Self::Error |
Self::Fatal |
Self::Warning => true,
_ => false,
}
}
fn should_print(&self, other: &Self) -> bool
{
other >= self
}
}
impl Default for Level
{
#[inline]
fn default() -> Self
{
Self::Info
}
}
/// Contains logging state
#[derive(Debug)]
pub struct Logger<B: ProgressBar = Bar>
{
level: Level,
progress: Handle<B>,
}
impl<B: ProgressBar> Clone for Logger<B>
{
fn clone(&self) -> Self
{
Self {
level: self.level,
progress: self.progress.clone(),
}
}
}
impl<B: ProgressBar> Logger<B>
{
/// Create a new logging state
pub fn new(progress: Handle<B>, level: Level) -> Self
{
Self{progress, level}
}
/// Get a reference of the progress handle
pub fn progress(&self) -> &Handle<B>
{
&self.progress
}
/// Get a mutable reference of the progress handle
pub fn progress_mut(&mut self) -> &mut Handle<B>
{
&mut self.progress
}
/// Get the logging level
pub fn level(&self) -> &Level
{
&self.level
}
/// Set the logging level
pub fn level_mut(&mut self) -> &mut Level
{
&mut self.level
}
fn gen_string(&self, level: &Level, disp: impl fmt::Display) -> String
{
format!("[ {} ]: \t{}", level, disp)
}
pub async fn print_display(&mut self, level: Level, disp: impl fmt::Display) -> Result<impl Future<Output=Result<Response, Error>>, Error>
{
if self.level.should_print(&level) {
let string = self.gen_string(&level, disp);
self.progress.send_command(if level.is_err() {CommandKind::LineErr(string)} else {CommandKind::Line(string)}).await
.map_err(Error::Progress)
.map(|x| x
.map_err(Error::Progress))
} else {
Err(Error::Level)
}
}
pub async fn print_display_and_wait(&mut self, level: Level, disp: impl fmt::Display) -> Result<Response, Error>
{
Ok(self.print_display(level, disp).await?.await?)
}
pub async fn print_display_and_detach(&mut self, level: Level, disp: impl fmt::Display) -> Result<(), Error>
{
let _ = self.print_display(level, disp).await?;
Ok(())
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum Error
{
Progress(WorkerCommError),
Level,
}
impl error::Error for Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match &self {
Self::Progress(p) => p,
_ => return None,
})
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Progress(_) => write!(f, "sending to progress worker failed"),
Self::Level => write!(f, "invalid level for write"),
}
}
}
impl From<WorkerCommError> for Error
{
#[inline] fn from(from: WorkerCommError) -> Self
{
Self::Progress(from)
}
}
#[macro_export] macro_rules! trace {
(yield $logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Trace, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
($logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Trace, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
}
#[macro_export] macro_rules! debug {
(yield $logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Debug, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
($logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Debug, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
}
#[macro_export] macro_rules! info {
(yield $logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Info, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
($logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Info, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
}
#[macro_export] macro_rules! warn {
(yield $logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Warn, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
($logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Warn, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
}
#[macro_export] macro_rules! error {
(yield $logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Error, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
($logger:expr => $msg:literal $($rest:tt)*) => {
{
let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Error, ::lazy_format::lazy_format!($msg $($rest)*)).await;
}
};
}

@ -1,448 +0,0 @@
//! Async progression
use super::*;
use termprogress::{
prelude::*,
};
use futures::{
prelude::*,
};
use tokio::{
sync::{
mpsc,
RwLock,
watch,
oneshot,
RwLockReadGuard,
RwLockWriteGuard,
},
task::{self, JoinHandle},
};
use std::{
sync::{
Weak,
},
fmt,
error,
};
#[macro_use] pub mod logging;
mod tasklist;
pub use tasklist::{
TaskId,
IdNotFoundError,
};
/// Command to send to worker task.
#[derive(Debug)]
pub enum CommandKind
{
Line(String),
LineErr(String),
Bump(isize),
BumpHigh(isize),
Set{low: Option<usize>, high: Option<usize>},
Refresh,
/// Add a task to the tasklist
///
/// # Response
/// Will respond with the task's `TaskId`.
AddTask(String),
/// Remove a task from the tasklist.
///
/// # Response
/// Will respond with `Result<String, IdNotFoundError>` of the removal operation
RemoveTask(TaskId),
/// Set the title directly
SetTitle(String),
Shutdown,
Many(Vec<CommandKind>),
}
/// The type sent in response to a `Command`.
pub type Response = Option<Box<dyn std::any::Any + Send + 'static>>;
#[derive(Debug)]
enum CommandIter
{
One(std::iter::Once<CommandKind>),
Many(std::vec::IntoIter<CommandKind>),
}
impl ExactSizeIterator for CommandIter{}
impl Iterator for CommandIter
{
type Item = CommandKind;
fn next(&mut self) -> Option<Self::Item>
{
match self {
Self::One(one) => one.next(),
Self::Many(many) => many.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>)
{
let sz = match self {
Self::One(_) => 1,
Self::Many(m) => m.len(),
};
(sz, Some(sz))
}
}
impl std::iter::FusedIterator for CommandIter{}
impl CommandKind
{
/// Enumerate all possible commands if this is `Many`.
///
/// The outputs may still contain `Many`.
//TODO: Make this work recursively
fn enumerate(self) -> CommandIter
{
match self {
Self::Many(many) => CommandIter::Many(many.into_iter()),
other => CommandIter::One(std::iter::once(other)),
}
}
}
#[derive(Debug, Clone)]
pub struct BarRef<B>(Arc<RwLock<B>>);
#[derive(Debug)]
struct Command(CommandKind, oneshot::Sender<Response>);
/// The bar's state
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct State
{
max: usize,
cur: usize,
//TODO: Tasks
}
impl State
{
/// The current progress
pub fn prog(&self) -> f64
{
(self.cur as f64) / (self.max as f64)
}
}
/// A handle to a running async progress bar
#[derive(Debug)]
pub struct Handle<B = Bar>
where B: ProgressBar,
{
// Channel to send commands to the worker
chan: mpsc::Sender<Command>,
// A weak reference to the worker's bar itself
bar: Weak<RwLock<B>>,
// A strong reference to the bar's state
state: Arc<RwLock<State>>,
// Has the worker shut down?
dead: watch::Receiver<bool>,
}
impl<B: ProgressBar> Clone for Handle<B>
{
fn clone(&self)->Self
{
Self {
chan: self.chan.clone(),
bar: self.bar.clone(),
state: self.state.clone(),
dead: self.dead.clone(),
}
}
}
impl<B: ProgressBar> Handle<B>
{
/// Is the worker alive?
pub fn is_alive(&self) -> bool
{
self.bar.strong_count()>0 && !*self.dead.borrow()
}
/// Yields until the worker shutds down gracefully
pub async fn closed(&mut self) -> Result<(),WorkerCommError>
{
loop {
match self.dead.recv().await {
Some(true) => return Ok(()),
None => return Err(WorkerCommError),
_ => continue,
}
}
}
/// Send a command to the worker.
///
/// Returns a future that completes to `Ok` when the worker successfully processes the command, and `Err` if the worker exits before processing it
pub async fn send_command(&mut self, command: CommandKind) -> Result<impl Future<Output=Result<Response, WorkerCommError>>, WorkerCommError>
{
let (tx, rx) = oneshot::channel();
self.chan.send(Command(command, tx)).await.map_err(|_| WorkerCommError)?;
Ok(rx.map(|res| res.map_err(|_| WorkerCommError)))
}
/// Send a command to the worker and then wait for it to be processed
pub async fn send_command_and_wait(&mut self, command: CommandKind) -> Result<Response, WorkerCommError>
{
self.send_command(command).await?.await
}
/// Send a command to the worker and then wait for it to be processed, then attempt to downcast to type `T`.
pub async fn send_command_and_downcast<T: 'static>(&mut self, command: CommandKind) -> Result<Option<T>, WorkerCommError>
{
let resp = self.send_command(command).await?.await?;
Ok(resp.map(|x| x.downcast::<T>().ok().map(|x| *x)).flatten())
}
/// Send a command to the worker but do not wait for it to be processed
pub async fn send_command_and_detach(&mut self, command: CommandKind) -> Result<(), WorkerCommError>
{
let _ = self.send_command(command).await?;
Ok(())
}
/// Get a reference to the state
pub async fn state(&self) -> RwLockReadGuard<'_, State>
{
self.state.read().await
}
/// Act on a mutable reference to the bar within this closure
///
/// # Notes
/// Acquiring this will prevent the worker from exiting until the closure finishes.
pub async fn with_bar_mut<F,T>(&self, fun: F) -> Result<T, WorkerCommError>
where F: FnOnce(&'_ mut B) -> T,
{
let handle = self.bar.upgrade().ok_or(WorkerCommError)?;
let mut h = handle.write().await;
use std::ops::DerefMut;
Ok(fun(h.deref_mut()))
}
/// Act on a reference to the bar within this closure
///
/// # Notes
/// Acquiring this will prevent the worker from exiting until the closure finishes.
pub async fn with_bar<F,T>(&self, fun: F) -> Result<T, WorkerCommError>
where F: FnOnce(&'_ B) -> T,
{
let handle = self.bar.upgrade().ok_or(WorkerCommError)?;
let h = handle.read().await;
use std::ops::Deref;
Ok(fun(h.deref()))
}
}
/// Error communicating with worker
#[derive(Debug)]
pub struct WorkerCommError;
impl error::Error for WorkerCommError{}
impl fmt::Display for WorkerCommError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "failed to communicate with worker.")
}
}
/// Host a progress bar and detach it
pub fn host<B: ProgressBar + Send + Sync + 'static>(bar: B) -> (Handle<B>, JoinHandle<B>)
{
let state = Arc::new(RwLock::new(Default::default()));
let (mut rx, death, bar, handle) = {
let (tx, rx) = mpsc::channel(24);
let (death, dead) = watch::channel(false);
let bar = Arc::new(RwLock::new(bar));
let handle = Handle {
chan: tx,
dead,
bar: Arc::downgrade(&bar),
state: Arc::clone(&state),
};
(rx,death,bar,handle)
};
(handle, tokio::spawn(async move {
({
let mut tasks = tasklist::TaskList::new();
macro_rules! update_bar {
(refresh $($tt:tt)*) => {
{
let bar = bar.read().await;
bar.refresh();
update_bar!($($tt)*);
}
};
(to $state:ident $($tt:tt)*) => {
{
let mut bar = bar.write().await;
bar.set_progress($state.prog());
update_bar!($($tt)*);
}
};
(write error $line:ident $($tt:tt)*) => {
{
let bar = bar.read().await;
let string = &$line[..];
bar.eprintln(string);
update_bar!($($tt)*);
}
};
(write $(std)? $line:ident $($tt:tt)*) => {
{
let bar = bar.read().await;
let string = &$line[..];
bar.println(string);
update_bar!($($tt)*);
}
};
(title $(std)? $title:ident $($tt:tt)*) => {
{
let mut bar = bar.write().await;
bar.set_title(&$title[..]);
update_bar!($($tt)*);
}
};
(title $($tt:tt)*) => {
{
let mut bar = bar.write().await;
bar.set_title(tasks.as_str());
update_bar!($($tt)*);
}
};
(+task $task:ident $($tt:tt)*) => {
{
let id = tasks.add($task);
update_bar!($($tt)*);
id
}
};
(-task $id:ident $($tt:tt)*) => {
{
let res = tasks.remove(&$id);
update_bar!($($tt)*);
res
}
};
() => {};
}
update_bar!(refresh);
while let Some(Command(command, response)) = rx.recv().await {
let response = Arc::new(std::sync::Mutex::new(Some(response)));
/// Send a response if one has not already been sent.
///
/// # Returns
/// * `Some(Ok(())` - if response was sent okay
/// * `Some(Err(_))` - if response failed to send.
/// * `None` - if response has already been sent
///
/// # Panics
/// If mutex is poisoned (this should be impossible).
macro_rules! send_response {
($value:expr) => (send_response!(@ response => Some(Box::new($value))));
(@ $response:ident => $value:expr) => {
{
let value: Response = $value;
{
if let Some(response) = $response.lock().unwrap().take() {
Some(response.send(value))
} else {
None
}
}
}
};
}
// Guard that ensures a `None` response is sent after this command has been processed, if an explicit response has not yet been sent.
let _resp = {
let response = Arc::clone(&response);
util::defer(move || send_response!(@ response => None).ignore())
};
match command {
CommandKind::Shutdown => break,
CommandKind::BumpHigh(sz) if sz >= 0 => {
let mut state = state.write().await;
state.max = state.max.saturating_add(sz as usize);
update_bar!(to state);
},
CommandKind::BumpHigh(sz) => {
debug_assert!(sz <0);
let mut state = state.write().await;
state.max = state.max.saturating_sub(sz.abs() as usize);
update_bar!(to state);
},
CommandKind::Bump(sz) if sz >= 0 => {
let mut state = state.write().await;
state.cur = state.cur.saturating_add(sz as usize);
update_bar!(to state);
},
CommandKind::Bump(sz) => {
debug_assert!(sz <0);
let mut state = state.write().await;
state.cur = state.cur.saturating_sub(sz.abs() as usize);
update_bar!(to state);
},
CommandKind::Set{low: None, high: None} => (),
CommandKind::Set{low, high} => {
let mut state = state.write().await;
state.cur = low.unwrap_or(state.cur);
state.max = high.unwrap_or(state.max);
update_bar!(to state);
},
CommandKind::Line(line) => update_bar!(write line),
CommandKind::LineErr(line) => update_bar!(write error line),
CommandKind::AddTask(string) => {
send_response!(update_bar!(+task string title));
},
CommandKind::RemoveTask(id) => {
send_response!(update_bar!(-task id title));
},
CommandKind::SetTitle(string) => update_bar!(title string),
CommandKind::Refresh => update_bar!(refresh),
CommandKind::Many(_) => unimplemented!(),
}
}
// Consume the bar and return
{
let mut bar = bar;
loop {
bar = match Arc::try_unwrap(bar) {
Ok(bar) => break bar,
Err(bar) => bar,
};
task::yield_now().await;
}.into_inner()
}
}, death.broadcast(true)).0
}))
}

@ -1,141 +0,0 @@
//! Tasklist for progressbar
use super::*;
use std::{
collections::LinkedList,
fmt,
error,
};
use uuid::Uuid;
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct TaskId(Uuid);
impl fmt::Display for TaskId
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "<{}>", self.0)
}
}
/// A list of tasks
#[derive(Debug, Clone)]
pub struct TaskList
{
tasks: LinkedList<(Uuid, String)>,
strbuf: String,
}
impl fmt::Display for TaskList
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.strbuf)
}
}
impl TaskList
{
/// The current full string
pub fn as_str(&self) -> &str
{
self.strbuf.as_str()
}
/// Create a new, empty tasklist
pub fn new() -> Self
{
Self{tasks:LinkedList::new(), strbuf:String::new()}
}
fn recalc_buf(&mut self)
{
self.strbuf = self.tasks.iter().map(|(_, stri)| stri.as_str()).join(", ");
}
fn push_buf_one(&mut self, task: &str)
{
if self.strbuf.len() > 0 {
self.strbuf.push_str(", ");
}
self.strbuf.push_str(task)
}
/// Add a task to the end of the list
pub fn add(&mut self, task: impl Into<String>) -> TaskId
{
let id = Uuid::new_v4();
let task = task.into();
self.push_buf_one(&task[..]);
self.tasks.push_back((id.clone(), task));
TaskId(id)
}
/// Remove all tasks
pub fn clear(&mut self)
{
self.tasks.clear();
self.strbuf.clear();
}
/// An iterator over all tasks currently in
pub fn tasks(&self) -> impl Iterator<Item = &'_ str> + '_
{
self.tasks.iter().map(|(_, strs)| strs.as_str())
}
/// Remove this task from the list, returning its string if it exists
pub fn remove(&mut self, task_id: &TaskId) -> Result<String, IdNotFoundError>
{
let value = match self.tasks.drain_filter(|(id, _)| id==&task_id.0).next() {
Some((_, string)) => string,
None => return Err(IdNotFoundError(TaskId(task_id.0.clone()))),
};
self.recalc_buf();
Ok(value)
}
/// Get this task ID's string
pub fn task_get(&self, task_id: &TaskId)-> Option<&str>
{
self.tasks.iter().filter(|(id, _)| id == &task_id.0).next().map(|x| x.1.as_str())
}
/// Replace this task ID with this string, retuning the old one.
pub fn task_set(&mut self, task_id: &TaskId, value: impl Into<String>) -> Result<String, IdNotFoundError>
{
let old = match self.tasks.iter_mut().filter(|(id, _)| id == &task_id.0).next().map(|x| &mut x.1) {
Some(string) => std::mem::replace(string, value.into()),
None => return Err(IdNotFoundError(TaskId(task_id.0.clone()))),
};
self.recalc_buf();
Ok(old)
}
}
/// Error when trying to remove a non-existent ID.
#[derive(Debug)]
pub struct IdNotFoundError(TaskId);
impl IdNotFoundError
{
/// Get the ID that was not found
pub fn id(&self) -> &TaskId
{
&self.0
}
}
impl error::Error for IdNotFoundError{}
impl fmt::Display for IdNotFoundError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}: unknown ID to this TaskList", self.0)
}
}

@ -13,17 +13,14 @@ use futures::{
future::OptionFuture, future::OptionFuture,
}; };
use progress::logging::Logger;
/// Contains state for a set of operations /// Contains state for a set of operations
//TODO: Add progress bar handler in here, when we get around to adding it.
#[derive(Debug)] #[derive(Debug)]
pub struct State pub struct State
{ {
cfg: config::Config, cfg: config::Config,
mtx: Option<Semaphore>, mtx: Option<Semaphore>,
stop: RwLock<()>, stop: RwLock<()>,
logger: Logger,
} }
/// Guard for operations inside state /// Guard for operations inside state
@ -32,32 +29,13 @@ pub struct Permit<'a>(RwLockReadGuard<'a, ()>, Option<SemaphorePermit<'a>>);
impl State impl State
{ {
/// Get the logger object for this state
pub fn logger(&self) -> &Logger
{
&self.logger
}
/// Clone the logger into a new mutable one
pub fn logger_output(&self) -> Logger
{
self.logger.clone() //TODO: Eeeehhhhh this is really inefficient
}
/// A reference to the progress counter
pub fn progress(&self) -> &progress::Handle
{
self.logger.progress()
}
/// Create a new state /// Create a new state
pub fn new(cfg: config::Config, logger: progress::logging::Logger) -> Self pub fn new(cfg: config::Config) -> Self
{ {
Self { Self {
mtx: cfg.limit.map(|x| Semaphore::new(x.into())), mtx: cfg.limit.map(|x| Semaphore::new(x.into())),
cfg, cfg,
stop: RwLock::new(()), stop: RwLock::new(()),
logger,
} }
} }

@ -121,17 +121,3 @@ impl NewWithCap for String
(_, Some(x)) | (x, None) => T::with_capacity(x), (_, Some(x)) | (x, None) => T::with_capacity(x),
} }
} }
/// Defer an action until drop
pub fn defer<F: FnOnce()>(fun: F) -> impl std::ops::Drop
{
struct DropWrap<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> std::ops::Drop for DropWrap<F>
{
fn drop(&mut self)
{
self.0.take().map(|x| x());
}
}
DropWrap(Some(fun))
}

Loading…
Cancel
Save