1
0
mirror of https://github.com/lise-henry/crowbook synced 2024-09-29 08:11:25 +02:00

Refactor in progress (bar)

This commit is contained in:
Elisabeth Henry 2017-10-08 13:02:34 +02:00
parent 3aaffcbf72
commit 167650d774
2 changed files with 236 additions and 202 deletions

@ -35,10 +35,10 @@ use book_renderer::BookRenderer;
use chapter::Chapter; use chapter::Chapter;
use token::Token; use token::Token;
use text_view::view_as_text; use text_view::view_as_text;
use book_bars::Bars;
use std::thread; use std::thread;
use std::sync::Arc; use std::sync::Arc;
use std::mem;
use indicatif::{ProgressBar, MultiProgress}; use indicatif::{ProgressBar, MultiProgress};
@ -178,13 +178,7 @@ pub struct Book {
formats: HashMap<&'static str, (String, Box<BookRenderer>)>, formats: HashMap<&'static str, (String, Box<BookRenderer>)>,
#[doc(hidden)] #[doc(hidden)]
pub multibar: Option<Arc<MultiProgress>>, pub bars: Bars,
#[doc(hidden)]
pub mainbar: Option<ProgressBar>,
#[doc(hidden)]
pub secondbar: Option<ProgressBar>,
#[doc(hidden)]
pub guard: Option<thread::JoinHandle<()>>,
} }
impl Book { impl Book {
@ -203,10 +197,7 @@ impl Book {
detector: None, detector: None,
formats: HashMap::new(), formats: HashMap::new(),
features: Features::new(), features: Features::new(),
multibar: None, bars: Bars::new(),
mainbar: None,
secondbar: None,
guard: None,
}; };
book.add_format("html", lformat!("HTML (standalone page)"), Box::new(HtmlSingle{})) book.add_format("html", lformat!("HTML (standalone page)"), Box::new(HtmlSingle{}))
.add_format("proofread.html", lformat!("HTML (standalone page/proofreading)"), Box::new(ProofHtmlSingle{})) .add_format("proofread.html", lformat!("HTML (standalone page/proofreading)"), Box::new(ProofHtmlSingle{}))
@ -490,10 +481,7 @@ impl Book {
Ok(words[0]) Ok(words[0])
} }
if let Some(ref bar) = self.mainbar { self.mainbar_set_message(&lformat!("setting options"));
bar.set_message(&lformat!("setting options"));
bar.tick();
}
let mut s = String::new(); let mut s = String::new();
source.read_to_string(&mut s) source.read_to_string(&mut s)
@ -509,10 +497,6 @@ impl Book {
let mut line_number = 0; let mut line_number = 0;
let mut is_next_line_ok: bool; let mut is_next_line_ok: bool;
if let Some(ref bar) = self.mainbar {
bar.tick();
}
loop { loop {
if let Some(next_line) = lines.peek() { if let Some(next_line) = lines.peek() {
if next_line.starts_with(|c| match c { if next_line.starts_with(|c| match c {
@ -582,10 +566,7 @@ impl Book {
// Update grammar checker according to options (proofread.*) // Update grammar checker according to options (proofread.*)
self.init_checker(); self.init_checker();
if let Some(ref bar) = self.mainbar { self.mainbar_set_message(&lformat!("Parsing chapters"));
bar.set_message(&lformat!("parsing chapters"));
bar.tick();
}
// Parse chapters // Parse chapters
let lines: Vec<_> = lines.collect(); let lines: Vec<_> = lines.collect();
@ -733,24 +714,6 @@ impl Book {
#[cfg(not(feature = "proofread"))] #[cfg(not(feature = "proofread"))]
fn init_checker(&mut self) {} fn init_checker(&mut self) {}
/// Renders the book to the given format and reports to progress bar if set
pub fn render_format_with_bar(&self, format: &str, bar: Option<&ProgressBar>) -> () {
let mut key = String::from("output.");
key.push_str(format);
if let Ok(path) = self.options.get_path(&key) {
if let Some(bar) = bar {
bar.set_message(&lformat!("rendering..."));
}
let result = self.render_format_to_file_with_bar(format, path, bar);
if let Err(err) = result {
if let Some(bar) = bar {
self.finish_spinner_error(bar, format, &format!("{}", err));
}
error!("{}", lformat!("Error rendering {name}: {error}", name = format, error = err));
}
}
}
/// Renders the book to the given format if output.{format} is set; /// Renders the book to the given format if output.{format} is set;
/// do nothing otherwise. /// do nothing otherwise.
/// ///
@ -763,7 +726,8 @@ impl Book {
/// book.render_format("pdf"); /// book.render_format("pdf");
/// ``` /// ```
pub fn render_format(&self, format: &str) -> () { pub fn render_format(&self, format: &str) -> () {
self.render_format_with_bar(format, None) // TODO: check that it doesn't break everything, or use option?
self.render_format_with_bar(format, 0)
} }
/// Generates output files acccording to book options. /// Generates output files acccording to book options.
@ -787,7 +751,7 @@ impl Book {
/// .unwrap() /// .unwrap()
/// .render_all(); // renders foo.tex in /tmp /// .render_all(); // renders foo.tex in /tmp
/// ``` /// ```
pub fn render_all(&self) -> () { pub fn render_all(&mut self) -> () {
let mut keys: Vec<_> = self.formats let mut keys: Vec<_> = self.formats
.keys() .keys()
.filter(|fmt| { .filter(|fmt| {
@ -798,6 +762,7 @@ impl Book {
} }
self.options.get_path(&format!("output.{}", fmt)).is_ok() self.options.get_path(&format!("output.{}", fmt)).is_ok()
}) })
.map(|s| s.to_string())
.collect(); .collect();
// Make sure that PDF comes first since running latex takes lots of time // Make sure that PDF comes first since running latex takes lots of time
keys.sort_by(|fmt1, fmt2| { keys.sort_by(|fmt1, fmt2| {
@ -810,22 +775,14 @@ impl Book {
} }
}); });
let mut bars = vec![]; for key in &keys {
if let Some(ref multibar) = self.multibar { self.add_spinner_to_multibar(key);
for key in &keys {
let bar = self.add_spinner_to_multibar(multibar, key);
bars.push(bar);
}
} }
keys.par_iter() keys.par_iter()
.enumerate() .enumerate()
.for_each(|(i, fmt)| { .for_each(|(i, fmt)| {
if self.multibar.is_some() { self.render_format_with_bar(fmt, i);
self.render_format_with_bar(fmt, Some(&bars[i]));
} else {
self.render_format_with_bar(fmt, None);
}
}); });
self.set_finished(&lformat!("Finished")); self.set_finished(&lformat!("Finished"));
@ -836,87 +793,24 @@ impl Book {
// } // }
} }
/// Renders the book to the given format and reports to progress bar if set
/// Render book to specified format according to book options, and write the results pub fn render_format_with_bar(&self, format: &str, bar: usize) -> () {
/// in the `Write` object. let mut key = String::from("output.");
/// key.push_str(format);
/// This method will fail if the format is not handled by the book, or if there is a if let Ok(path) = self.options.get_path(&key) {
/// problem during rendering, or if the renderer can't render to a byte stream (e.g. self.nth_bar_set_message(bar, &lformat!("rendering..."));
/// multiple files HTML renderer can't, as it must create a directory.) let result = self.render_format_to_file_with_bar(format, path, bar);
/// if let Err(err) = result {
/// # See also self.finish_nth_spinner_error(bar, format, &format!("{}", err));
/// * `render_format_to_file`, which creates a new file (that *can* be a directory). error!("{}", lformat!("Error rendering {name}: {error}", name = format, error = err));
/// * `render_format`, which won't do anything if `output.{format}` isn't specified
/// in the book configuration file.
pub fn render_format_to<T: Write>(&self, format: &str, f: &mut T) -> Result<()> {
debug!("{}", lformat!("Attempting to generate {format}...",
format = format));
match self.formats.get(format) {
Some(&(ref description, ref renderer)) => {
if let Some(ref multibar) = self.multibar {
let bar = self.add_spinner_to_multibar(multibar, format);
renderer.render(self, f)?;
self.finish_spinner_success(&bar,
format,
&lformat!("generated {format}",
format = format));
self.set_finished(&lformat!("Finished"));
} else {
renderer.render(self, f)?;
}
info!("{}", lformat!("Succesfully generated {format}",
format = description));
Ok(())
},
None => {
Err(Error::default(Source::empty(),
lformat!("unknown format {format}",
format = format)))
} }
} }
} }
/// Render book to specified format according to book options. Creates a new file pub fn render_format_to_file_with_bar<P:Into<PathBuf>>(&self,
/// and write the result in it. format: &str,
/// path: P,
/// This method will fail if the format is not handled by the book, or if there is a bar: usize) -> Result<()> {
/// problem during rendering.
///
/// # Arguments
///
/// * `format`: the format to render;
/// * `path`: a path to the file that will be created;
/// * `bar`: a Progressbar, or `None`
///
/// # See also
/// * `render_format_to`, which writes in any `Write`able object.
/// * `render_format`, which won't do anything if `output.{format}` isn't specified
/// in the book configuration file.
pub fn render_format_to_file<P:Into<PathBuf>>(&self,
format: &str,
path: P) -> Result<()> {
if let Some(ref multibar) = self.multibar {
let bar = self.add_spinner_to_multibar(multibar, format);
let path = path.into();
let normalized = misc::normalize(&path);
self.render_format_to_file_with_bar(format, path, Some(&bar))?;
self.finish_spinner_success(&bar,
format,
&lformat!("generated {path}",
path = normalized));
self.set_finished(&lformat!("Finished"));
Ok(())
} else {
self.render_format_to_file_with_bar(format, path, None)
}
}
fn render_format_to_file_with_bar<P:Into<PathBuf>>(&self,
format: &str,
path: P,
bar: Option<&ProgressBar>) -> Result<()> {
debug!("{}", lformat!("Attempting to generate {format}...", debug!("{}", lformat!("Attempting to generate {format}...",
format = format)); format = format));
let path = path.into(); let path = path.into();
@ -946,12 +840,10 @@ impl Book {
format = description, format = description,
path = &path); path = &path);
info!("{}", &msg); info!("{}", &msg);
if let Some(bar) = bar { self.finish_nth_spinner_success(bar,
self.finish_spinner_success(bar, format,
format, &lformat!("generated {path}",
&lformat!("generated {path}", path = path));
path = path));
}
Ok(()) Ok(())
}, },
None => { None => {
@ -962,6 +854,81 @@ impl Book {
} }
} }
/// Render book to specified format according to book options, and write the results
/// in the `Write` object.
///
/// This method will fail if the format is not handled by the book, or if there is a
/// problem during rendering, or if the renderer can't render to a byte stream (e.g.
/// multiple files HTML renderer can't, as it must create a directory.)
///
/// # See also
/// * `render_format_to_file`, which creates a new file (that *can* be a directory).
/// * `render_format`, which won't do anything if `output.{format}` isn't specified
/// in the book configuration file.
pub fn render_format_to<T: Write>(&mut self, format: &str, f: &mut T) -> Result<()> {
debug!("{}", lformat!("Attempting to generate {format}...",
format = format));
let bar = self.add_spinner_to_multibar(format);
match self.formats.get(format) {
Some(&(ref description, ref renderer)) => {
renderer.render(self, f)?;
self.finish_nth_spinner_success(bar,
format,
&lformat!("generated {format}",
format = format));
self.set_finished(&lformat!("Finished"));
info!("{}", lformat!("Succesfully generated {format}",
format = description));
Ok(())
},
None => {
self.finish_nth_spinner_error(bar,
format,
&lformat!("unknown format"));
Err(Error::default(Source::empty(),
lformat!("unknown format {format}",
format = format)))
}
}
}
/// Render book to specified format according to book options. Creates a new file
/// and write the result in it.
///
/// This method will fail if the format is not handled by the book, or if there is a
/// problem during rendering.
///
/// # Arguments
///
/// * `format`: the format to render;
/// * `path`: a path to the file that will be created;
/// * `bar`: a Progressbar, or `None`
///
/// # See also
/// * `render_format_to`, which writes in any `Write`able object.
/// * `render_format`, which won't do anything if `output.{format}` isn't specified
/// in the book configuration file.
pub fn render_format_to_file<P:Into<PathBuf>>(&mut self,
format: &str,
path: P) -> Result<()> {
let bar = self.add_spinner_to_multibar(format);
let path = path.into();
let normalized = misc::normalize(&path);
self.render_format_to_file_with_bar(format, path, bar)?;
self.finish_nth_spinner_success(bar,
format,
&lformat!("generated {path}",
path = normalized));
self.set_finished(&lformat!("Finished"));
Ok(())
}
/// Adds a chapter to the book. /// Adds a chapter to the book.
/// ///
/// This method is the backend used both by `add_chapter` and `add_chapter_from_source`. /// This method is the backend used both by `add_chapter` and `add_chapter_from_source`.
@ -970,10 +937,7 @@ impl Book {
file: &str, file: &str,
mut source: R) mut source: R)
-> Result<&mut Self> { -> Result<&mut Self> {
if let Some(ref bar) = self.mainbar { self.mainbar_set_message(&lformat!("Processing {file}...", file = file));
bar.set_message(&lformat!("Processing {file}...", file = file));
bar.tick();
}
let mut content = String::new(); let mut content = String::new();
source.read_to_string(&mut content) source.read_to_string(&mut content)
.map_err(|_| { .map_err(|_| {
@ -986,10 +950,8 @@ impl Book {
self.parse_yaml(&mut content); self.parse_yaml(&mut content);
// parse the file // parse the file
if let Some(ref bar) = self.secondbar { self.secondbar_set_message(&lformat!("Parsing..."));
bar.set_message(&lformat!("Parsing..."));
bar.tick();
}
let mut parser = Parser::from(self); let mut parser = Parser::from(self);
parser.set_source_file(file); parser.set_source_file(file);
let mut tokens = parser.parse(&content)?; let mut tokens = parser.parse(&content)?;
@ -1037,9 +999,7 @@ impl Book {
if cfg!(feature = "proofread") && self.is_proofread() { if cfg!(feature = "proofread") && self.is_proofread() {
let normalized = misc::normalize(file); let normalized = misc::normalize(file);
if let Some(ref checker) = self.checker { if let Some(ref checker) = self.checker {
if let Some(ref bar) = self.secondbar { self.secondbar_set_message(&lformat!("Running languagetool"));
bar.set_message(&lformat!("Running languagetool"));
}
info!("{}", lformat!("Trying to run languagetool on {file}, this might take a \ info!("{}", lformat!("Trying to run languagetool on {file}, this might take a \
while...", while...",
@ -1051,9 +1011,7 @@ impl Book {
} }
} }
if let Some(ref checker) = self.grammalecte { if let Some(ref checker) = self.grammalecte {
if let Some(ref bar) = self.secondbar { self.secondbar_set_message(&lformat!("Running grammalecte"));
bar.set_message(&lformat!("Running grammalecte"));
}
info!("{}", lformat!("Trying to run grammalecte on {file}, this might take a \ info!("{}", lformat!("Trying to run grammalecte on {file}, this might take a \
while...", while...",
@ -1065,9 +1023,7 @@ impl Book {
} }
} }
if let Some(ref detector) = self.detector { if let Some(ref detector) = self.detector {
if let Some(ref bar) = self.secondbar { self.secondbar_set_message(&lformat!("Detecting repetitions"));
bar.set_message(&lformat!("Detecting repetitions"));
}
info!("{}", lformat!("Trying to run repetition detector on {file}, this might take a \ info!("{}", lformat!("Trying to run repetition detector on {file}, this might take a \
while...", while...",
@ -1079,9 +1035,8 @@ impl Book {
} }
} }
} }
if let Some(ref bar) = self.secondbar { self.secondbar_set_message("");
bar.set_message("");
}
self.chapters.push(Chapter::new(number, file, tokens)); self.chapters.push(Chapter::new(number, file, tokens));
Ok(self) Ok(self)
@ -1134,11 +1089,8 @@ impl Book {
/// **Returns** an error if `file` does not exist, could not be read, of if there was /// **Returns** an error if `file` does not exist, could not be read, of if there was
/// some error parsing it. /// some error parsing it.
pub fn add_chapter(&mut self, number: Number, file: &str) -> Result<&mut Self> { pub fn add_chapter(&mut self, number: Number, file: &str) -> Result<&mut Self> {
if let Some(ref bar) = self.mainbar { self.mainbar_set_message(&lformat!("Parsing {file}",
bar.set_message(&lformat!("Parsing {file}", file = misc::normalize(file)));
file = misc::normalize(file)));
bar.tick();
}
debug!("{}", lformat!("Parsing chapter: {file}...", debug!("{}", lformat!("Parsing chapter: {file}...",
file = misc::normalize(file))); file = misc::normalize(file)));
@ -1527,20 +1479,6 @@ impl Book {
} }
impl Drop for Book {
fn drop(&mut self) {
if let Some(ref bar) = self.mainbar {
bar.finish();
let guard = mem::replace(&mut self.guard, None);
guard.unwrap()
.join()
.unwrap();
}
}
}
/// Calls mustache::compile_str but catches panics and returns a result /// Calls mustache::compile_str but catches panics and returns a result
pub fn compile_str<O>(template: &str, source: O, template_name: &str) -> Result<mustache::Template> pub fn compile_str<O>(template: &str, source: O, template_name: &str) -> Result<mustache::Template>
where O: Into<Source> where O: Into<Source>

@ -1,4 +1,4 @@
// Copyright (C) 2016, 2017 Élisabeth HENRY. // Copyright (C) 2017 Élisabeth HENRY.
// //
// This file is part of Crowbook. // This file is part of Crowbook.
// //
@ -19,11 +19,42 @@
// to make some dependencies (incidacitf) optional. // to make some dependencies (incidacitf) optional.
use book::Book; use book::Book;
use error::{Result, Error, Source};
use misc;
use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
use std::mem; use std::mem;
use std::path::{Path, PathBuf};
/// Store the progress bars needed for the book
pub struct Bars {
/// Container for the progress bars
pub multibar: Option<Arc<MultiProgress>>,
/// Main progress bar (actually a spinner)
pub mainbar: Option<ProgressBar>,
/// Secondary bar
pub secondbar: Option<ProgressBar>,
/// Guard for thread
pub guard: Option<thread::JoinHandle<()>>,
/// Spinners for each renderier
pub spinners: Vec<ProgressBar>,
}
impl Bars {
/// Create a new bars storage
pub fn new() -> Bars {
Bars {
multibar: None,
mainbar: None,
secondbar: None,
guard: None,
spinners: vec![],
}
}
}
impl Book { impl Book {
/// Adds a progress bar where where info should be written. /// Adds a progress bar where where info should be written.
@ -31,8 +62,8 @@ impl Book {
/// See [indicatif doc](https://docs.rs/indicatif) for more information. /// See [indicatif doc](https://docs.rs/indicatif) for more information.
pub fn private_add_progress_bar(&mut self) { pub fn private_add_progress_bar(&mut self) {
let multibar = Arc::new(MultiProgress::new()); let multibar = Arc::new(MultiProgress::new());
self.multibar = Some(multibar.clone()); self.bars.multibar = Some(multibar.clone());
let b = self.multibar let b = self.bars.multibar
.as_ref() .as_ref()
.unwrap() .unwrap()
.add(ProgressBar::new_spinner()); .add(ProgressBar::new_spinner());
@ -42,8 +73,8 @@ impl Book {
.template("{spinner:.dim.bold.yellow} {prefix} {wide_msg}"); .template("{spinner:.dim.bold.yellow} {prefix} {wide_msg}");
b.set_style(sty); b.set_style(sty);
b.enable_steady_tick(200); b.enable_steady_tick(200);
self.mainbar = Some(b); self.bars.mainbar = Some(b);
self.guard = Some(thread::spawn(move || { self.bars.guard = Some(thread::spawn(move || {
if let Err(_) = multibar.join() { if let Err(_) = multibar.join() {
error!("{}", lformat!("could not display fancy UI, try running crowbook with --no-fancy")); error!("{}", lformat!("could not display fancy UI, try running crowbook with --no-fancy"));
} }
@ -52,7 +83,7 @@ impl Book {
/// Sets an error message to the progress bar, if it is set /// Sets an error message to the progress bar, if it is set
pub fn private_set_error(&self, msg: &str) { pub fn private_set_error(&self, msg: &str) {
if let Some(ref mainbar) = self.mainbar { if let Some(ref mainbar) = self.bars.mainbar {
let sty = ProgressStyle::default_spinner() let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-X") .tick_chars("/|\\-X")
.template("{spinner:.dim.bold.red} {wide_msg}"); .template("{spinner:.dim.bold.red} {wide_msg}");
@ -64,7 +95,7 @@ impl Book {
/// Sets a finished message to the progress bar, if it is set /// Sets a finished message to the progress bar, if it is set
pub fn set_finished(&self, msg: &str) { pub fn set_finished(&self, msg: &str) {
if let Some(ref bar) = self.mainbar { if let Some(ref bar) = self.bars.mainbar {
let sty = ProgressStyle::default_spinner() let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-V") .tick_chars("/|\\-V")
.template("{spinner:.dim.bold.cyan} {wide_msg}"); .template("{spinner:.dim.bold.cyan} {wide_msg}");
@ -76,48 +107,101 @@ impl Book {
/// Adds a secondary progress bar to display progress of book parsing /// Adds a secondary progress bar to display progress of book parsing
pub fn add_second_bar(&mut self, msg: &str, len: u64) { pub fn add_second_bar(&mut self, msg: &str, len: u64) {
if let Some(ref multibar) = self.multibar { if let Some(ref multibar) = self.bars.multibar {
let bar = multibar.add(ProgressBar::new(len)); let bar = multibar.add(ProgressBar::new(len));
bar.set_style(ProgressStyle::default_bar() bar.set_style(ProgressStyle::default_bar()
.template("{bar:40.cyan/blue} {percent:>7}% {msg}") .template("{bar:40.cyan/blue} {percent:>7}% {msg}")
.progress_chars("##-")); .progress_chars("##-"));
bar.set_message(msg); bar.set_message(msg);
self.secondbar = Some(bar); self.bars.secondbar = Some(bar);
} }
} }
/// Finish secondary prograss bar /// Finish secondary prograss bar
pub fn finish_second_bar(&self) { pub fn finish_second_bar(&self) {
if let Some(ref bar) = self.secondbar { if let Some(ref bar) = self.bars.secondbar {
bar.finish_and_clear(); bar.finish_and_clear();
} }
} }
/// Increment second bar /// Increment second bar
pub fn inc_second_bar(&self) { pub fn inc_second_bar(&self) {
if let Some(ref bar) = self.secondbar { if let Some(ref bar) = self.bars.secondbar {
bar.inc(1); bar.inc(1);
} }
} }
/// Adds a spinner labeled key to the multibar, and set mainbar to "rendering" /// Adds a spinner labeled key to the multibar, and set mainbar to "rendering"
pub fn add_spinner_to_multibar(&self, multibar: &MultiProgress, key: &str) -> ProgressBar { pub fn add_spinner_to_multibar(&mut self, key: &str) -> usize {
if let Some(ref mainbar) = self.mainbar { if let Some(ref multibar) = self.bars.multibar {
mainbar.set_message(&lformat!("Rendering...")); if let Some(ref mainbar) = self.bars.mainbar {
} mainbar.set_message(&lformat!("Rendering..."));
}
let bar = multibar.add(ProgressBar::new_spinner()); let bar = multibar.add(ProgressBar::new_spinner());
let sty = ProgressStyle::default_spinner() let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-X") .tick_chars("/|\\-X")
.template(&format!("{{spinner:.dim.bold.yellow}} {format}: {{wide_msg:.yellow}}", .template(&format!("{{spinner:.dim.bold.yellow}} {format}: {{wide_msg:.yellow}}",
format = key)); format = key));
bar.set_style(sty); bar.set_style(sty);
bar.enable_steady_tick(200); bar.enable_steady_tick(200);
bar.set_message(&lformat!("waiting...")); bar.set_message(&lformat!("waiting..."));
bar.tick(); bar.tick();
bar self.bars.spinners.push(bar);
return self.bars.spinners.len() - 1;
} else {
0
}
} }
pub fn mainbar_set_message(&self, msg: &str) {
if let Some(ref bar) = self.bars.mainbar {
bar.set_message(msg);
bar.tick();
}
}
pub fn secondbar_set_message(&self, msg: &str) {
if let Some(ref bar) = self.bars.secondbar {
bar.set_message(msg);
bar.tick();
}
}
pub fn nth_bar_set_message(&self, bar: usize, msg: &str) {
if bar < self.bars.spinners.len() {
let bar = &self.bars.spinners[bar];
bar.set_message(msg);
bar.tick();
}
}
// Finish a spinner with an error message
pub fn finish_nth_spinner_error(&self, bar: usize, key: &str, msg: &str) {
if bar < self.bars.spinners.len() {
let bar = &self.bars.spinners[bar];
bar.set_style(ProgressStyle::default_spinner()
.tick_chars("/|\\-X")
.template(&format!("{{spinner:.dim.bold.red}} {format}: {{wide_msg:.red}}",
format = key)));
bar.finish_with_message(msg);
}
}
// Finish a spinner with success message
pub fn finish_nth_spinner_success(&self, bar: usize, key: &str, msg: &str) {
if bar < self.bars.spinners.len() {
let bar = &self.bars.spinners[bar];
let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-V")
.template(&format!("{{spinner:.dim.bold.cyan}} {format}: {{wide_msg:.cyan}}",
format = key));
bar.set_style(sty);
bar.finish_with_message(msg);
}
}
// Finish a spinner with an error message // Finish a spinner with an error message
pub fn finish_spinner_error(&self, bar: &ProgressBar, key: &str, msg: &str) { pub fn finish_spinner_error(&self, bar: &ProgressBar, key: &str, msg: &str) {
bar.set_style(ProgressStyle::default_spinner() bar.set_style(ProgressStyle::default_spinner()
@ -138,3 +222,15 @@ impl Book {
bar.finish_with_message(msg); bar.finish_with_message(msg);
} }
} }
impl Drop for Book {
fn drop(&mut self) {
if let Some(ref bar) = self.bars.mainbar {
bar.finish();
let guard = mem::replace(&mut self.bars.guard, None);
guard.unwrap()
.join()
.unwrap();
}
}
}