use std::sync::{Arc, Mutex};
use std::thread;
use crate::{
manager_thread, LifecycleState, NestedBars, NestedMeta, ProgressBarSizedNester,
ProgressBarSummedNester, MANAGER,
};
use crate::{ProgressBarState, ProgressBarWeightedNester};
pub struct ProgressBar {
pub(crate) state: Option<Arc<Mutex<ProgressBarState>>>,
}
impl Drop for ProgressBar {
fn drop(&mut self) {
self.abandon();
}
}
impl Default for ProgressBar {
fn default() -> Self {
Self::new()
}
}
#[doc=include_str!("../images/simple.html")]
impl ProgressBar {
#[doc=include_str!("../images/message.html")]
pub fn new() -> Self {
let mut manager = MANAGER.lock().unwrap();
let state = Arc::new(Mutex::new(ProgressBarState::default()));
manager.bars.push(state.clone());
if manager.interactive_output && !manager.thread_started {
manager.thread_started = true;
thread::spawn(manager_thread);
}
Self { state: Some(state) }
}
pub fn hidden() -> Self {
let state = Arc::new(Mutex::new(ProgressBarState::default()));
Self { state: Some(state) }
}
#[doc=include_str!("../images/split_weighted.html")]
pub fn split_weighted(self) -> ProgressBarWeightedNester {
self.state
.as_ref()
.expect("You cannot split a finished/abandoned progress bar")
.lock()
.unwrap()
.nested = Some(NestedBars {
bars: vec![],
meta: NestedMeta::Weighted(vec![]),
});
ProgressBarWeightedNester {
bar: self,
taken_fraction: 0.0,
}
}
#[doc=include_str!("../images/split_sized.html")]
pub fn split_sized(self) -> ProgressBarSizedNester {
self.state
.as_ref()
.expect("You cannot split a finished/abandoned progress bar")
.lock()
.unwrap()
.nested = Some(NestedBars {
bars: vec![],
meta: NestedMeta::Sized(vec![]),
});
ProgressBarSizedNester {
bar: self,
taken_count: 0,
}
}
#[doc=include_str!("../images/split_summed.html")]
pub fn split_summed(self) -> ProgressBarSummedNester {
self.state
.as_ref()
.expect("You cannot split a finished/abandoned progress bar")
.lock()
.unwrap()
.nested = Some(NestedBars {
bars: vec![],
meta: NestedMeta::Summed,
});
ProgressBarSummedNester { bar: self }
}
#[doc=include_str!("../images/split_each.html")]
pub fn split_each<It: Iterator>(self, it: It) -> impl Iterator<Item = (ProgressBar, It::Item)> {
if let Some(upper_bound) = it.size_hint().1 {
self.set_length(upper_bound);
}
let mut splitter = self.split_sized();
it.map(move |v| (splitter.take(1), v))
}
pub fn length(&self) -> Option<usize> {
if let Some(state) = &self.state {
state.lock().unwrap().length
} else {
panic!(
"This progress bar is finished. You can no longer retrieve information about it."
);
}
}
pub fn set_length(&self, len: usize) {
if let Some(state) = &self.state {
let mut state = state.lock().unwrap();
state.length = Some(len);
}
}
pub fn set_position(&self, pos: usize) {
if let Some(state) = &self.state {
state.lock().unwrap().position = pos;
}
}
pub fn clear_message(&self) {
if let Some(state) = &self.state {
state.lock().unwrap().message = None;
}
}
pub fn with_message(self, message: impl Into<String>) -> Self {
self.set_message(message);
self
}
pub fn with_length(self, length: usize) -> Self {
self.set_length(length);
self
}
pub fn set_message(&self, message: impl Into<String>) {
let m = message.into();
if m.is_empty() {
self.clear_message();
} else if let Some(state) = &self.state {
state.lock().unwrap().message = Some(m);
}
}
pub fn inc(&self) {
if let Some(state) = &self.state {
state.lock().unwrap().position += 1;
}
}
pub fn finish_with_message(&mut self, message: impl Into<String>) {
self.set_message(message);
self.finish();
}
pub fn abandon(&mut self) {
if let Some(state) = &self.state {
let mut state = state.lock().unwrap();
state.lifecycle = LifecycleState::Abandoned;
}
self.state = None;
let mut manager = MANAGER.lock().unwrap();
manager.tick(&mut std::io::stdout().lock()).unwrap();
}
pub fn finish(&mut self) {
if let Some(state) = &self.state {
let mut state = state.lock().unwrap();
if let Some(length) = state.length {
state.position = length;
}
state.lifecycle = LifecycleState::Completed;
}
self.state = None;
let mut manager = MANAGER.lock().unwrap();
manager.tick(&mut std::io::stdout().lock()).unwrap();
}
#[doc=include_str!("../images/message.html")]
pub fn wrap<It: Iterator>(self, it: It) -> ProgressBarIterator<It> {
if let Some(upper_bound) = it.size_hint().1 {
self.set_length(upper_bound);
}
ProgressBarIterator {
progress: self,
inner: it,
}
}
}
pub struct ProgressBarIterator<It: Iterator> {
progress: ProgressBar,
inner: It,
}
impl<It: Iterator> Iterator for ProgressBarIterator<It> {
type Item = It::Item;
fn next(&mut self) -> Option<It::Item> {
let r = self.inner.next();
if r.is_none() {
self.progress.finish();
} else {
self.progress.inc();
}
r
}
}
impl<T, It: ExactSizeIterator<Item = T>> ExactSizeIterator for ProgressBarIterator<It> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl<It: Iterator> ProgressBarIterator<It> {
pub fn with_message(self, message: &str) -> Self {
self.progress.set_message(message);
self
}
}
pub trait ProgressBarIterable: Iterator + Sized {
#[doc=include_str!("../images/simple.html")]
fn progress(self) -> ProgressBarIterator<Self>;
fn progress_with(self, bar: ProgressBar) -> ProgressBarIterator<Self>;
}
impl<T, It: Iterator<Item = T>> ProgressBarIterable for It {
fn progress(self) -> ProgressBarIterator<It> {
self.progress_with(ProgressBar::new())
}
fn progress_with(self, bar: ProgressBar) -> ProgressBarIterator<It> {
bar.wrap(self)
}
}