use super::*; use tokio::{ sync::{ mpsc::{ channel, unbounded_channel, Sender, Receiver, UnboundedReceiver, UnboundedSender }, }, task::{ self, JoinHandle, }, }; use termprogress::{ Display, }; enum CommandInternal { None, PrintLine(String), BumpMax(u64), Bump(u64), Kill(Option), PushTask(String), PopTask(String), ClearTask(Option), } // TODO: Change from `Spinner` to `Bar`. Have push max, push current, etc. struct Command { internal: CommandInternal, callback: UnboundedSender<()>, } impl std::ops::Drop for Command { fn drop(&mut self) { let _ = self.callback.send(()); } } pub struct AsyncProgressCounter where T: termprogress::ProgressBar + WithTitle + std::marker::Send + std::marker::Sync +'static, { writer: Sender, reader: Receiver, title: String, small: u64, large: u64, _phantom: std::marker::PhantomData, } #[derive(Clone)] pub struct CommandSender(Sender, Option); pub struct CommandCallback(UnboundedReceiver<()>); pub mod error; impl Command { pub fn new(internal: CommandInternal) -> (Self, CommandCallback) { let (tx, rx) = unbounded_channel(); (Self { internal, callback: tx, }, CommandCallback(rx)) } } macro_rules! prog_send { (try link unwind $expression:expr) => { { $expression.await.expect("mpsc fatal").wait().await.expect("mpsc fatal") } }; (try link $expression:expr) => { { $expression.await?.wait().await? } }; (try unwind $expression:expr) => { { let _ = $expression.await.expect("mpsc fatal"); } }; (try $expression:expr) => { { let _ =$expression.await?; } }; (link unwind $expression:expr) => { { $expression.await.expect("mpsc fatal").wait().await.expect("mpsc fatal"); } }; (link $expression:expr) => { { if let Ok(ok) = $expression.await { let _ = ok.wait().await; } } }; ($expression:expr) => { { let _ = $expression.await; } }; } impl CommandSender { /// Clone with a new opt string pub fn clone_with(&self, opt: impl Into) -> Self { Self ( self.0.clone(), Some(opt.into()) ) } fn opt_str(&self, string: impl AsRef) -> String { if let Some(opt) = &self.1 { format!("[{}] {}", opt, string.as_ref()) } else { string.as_ref().into() } } /// Get or set an opt string pub fn opt(&mut self) -> &mut Option { &mut self.1 } /// Print a line pub async fn println(&mut self, string: impl AsRef) -> Result { let (com, call) = Command::new(CommandInternal::PrintLine(self.opt_str(string))); self.0.send(com).await?; Ok(call) } /// Bump the progress counter's upper bound pub async fn bump_max(&mut self, by: u64) -> Result { let (com, call) = Command::new(CommandInternal::BumpMax(by)); self.0.send(com).await?; Ok(call) } /// Bump the progress counter's upper bound pub async fn bump(&mut self, by: u64) -> Result { let (com, call) = Command::new(CommandInternal::Bump(by)); self.0.send(com).await?; Ok(call) } /// Finalise the progress counter pub async fn kill(&mut self) -> Result { let (com, call) = Command::new(CommandInternal::Kill(None)); self.0.send(com).await?; Ok(call) } /// Finalise the progress counter and print a line pub async fn kill_with(&mut self, string: impl AsRef) -> Result { let (com, call) = Command::new(CommandInternal::Kill(Some(self.opt_str(string)))); self.0.send(com).await?; Ok(call) } /// Remove a task pub async fn pop_task(&mut self, string: impl Into) -> Result { let (com, call) = Command::new(CommandInternal::PopTask(string.into())); self.0.send(com).await?; Ok(call) } /// Add a task pub async fn push_task(&mut self, string: impl Into) -> Result { let (com, call) = Command::new(CommandInternal::PushTask(string.into())); self.0.send(com).await?; Ok(call) } /// Clear all tasks pub async fn clear_task(&mut self) -> Result { let (com, call) = Command::new(CommandInternal::ClearTask(None)); self.0.send(com).await?; Ok(call) } /// Clear all tasks and set new title pub async fn set_title(&mut self, title: impl Into) -> Result { let (com, call) = Command::new(CommandInternal::ClearTask(Some(title.into()))); self.0.send(com).await?; Ok(call) } } impl CommandCallback { /// Wait until the command has been processed. pub async fn wait(mut self) -> Result<(), error::Error> { self.0.recv().await.ok_or(error::Error::RecvError) } } impl AsyncProgressCounter where T: termprogress::ProgressBar + WithTitle + std::marker::Send + std::marker::Sync +'static, { /// Create a new `AsyncProgressCounter` pub fn new(title: impl Into, max: u64) -> Self { let (tx, rx) = channel(16); Self { reader: rx, writer: tx, title: title.into(), small: 0, large: max, _phantom: std::marker::PhantomData, } } /// Create a new writer with an opt string pub fn writer_with(&self, opt: impl Into) -> CommandSender { CommandSender(self.writer.clone(), Some(opt.into())) } /// Create a new writer pub fn writer(&self) -> CommandSender { CommandSender(self.writer.clone(), None) } /// Consume the instance and host its receiver pub fn host(mut self) -> JoinHandle<()> { //let mut spin = spinner::Spin::with_title(&self.title[..], Default::default()); let mut bar = T::with_title(50, &self.title[..]); bar.update(); let mut task = tasklist::TaskList::new(); task::spawn(async move { let mut small = 0;//self.small; let mut large = 0; fn prog_calc(small: u64, large: u64) -> f64 { (small as f64) / (large as f64) } while let Some(mut com) = self.reader.recv().await { match std::mem::replace(&mut com.internal, CommandInternal::None) { CommandInternal::PrintLine(line) => bar.println(&line[..]), CommandInternal::BumpMax(by) => { large += by; bar.set_progress(prog_calc(small,large)); }, CommandInternal::Bump(by) => { small += by; bar.set_progress(prog_calc(small,large)); }, CommandInternal::Kill(Some(line)) => { bar.println(&line[..]); bar.complete(); self.reader.close(); break; }, CommandInternal::Kill(_) => { bar.complete(); self.reader.close(); break; }, CommandInternal::PushTask(tstr) => { task.push(tstr); bar.set_title(task.as_str()); }, CommandInternal::PopTask(tstr) => { task.pop_value(tstr); bar.set_title(task.as_str()); }, CommandInternal::ClearTask(None) => { task.clear(); bar.set_title(&self.title[..]); }, CommandInternal::ClearTask(Some(title)) => { task.clear(); bar.set_title(&title[..]); self.title = title; }, _ => {}, } } }) } } pub trait WithTitle: Sized + ProgressBar + Display { fn with_title(len: usize, string: impl AsRef) -> Self; fn update(&mut self); fn complete(self); } impl WithTitle for termprogress::progress::Bar { fn with_title(len: usize, string: impl AsRef) -> Self { Self::with_title(len, string) } fn update(&mut self) { self.update(); } fn complete(self) { self.complete(); } } #[derive(Debug)] pub struct Silent; impl termprogress::Display for Silent { fn println(&self, _: &str){} fn refresh(&self){} fn blank(&self){} fn get_title(&self) -> &str{""} fn set_title(&mut self, _: &str){} fn update_dimensions(&mut self, _:usize){} } impl termprogress::ProgressBar for Silent { fn set_progress(&mut self, _:f64){} fn get_progress(&self) -> f64{0.0} } impl WithTitle for Silent { fn with_title(_: usize, _: impl AsRef) -> Self{Self} fn update(&mut self) {} fn complete(self) {} }