1
0
Fork 0
mirror of https://github.com/lise-henry/crowbook synced 2024-06-27 13:31:37 +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

View File

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

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016, 2017 Élisabeth HENRY.
// Copyright (C) 2017 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
@ -19,11 +19,42 @@
// to make some dependencies (incidacitf) optional.
use book::Book;
use error::{Result, Error, Source};
use misc;
use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
use std::sync::Arc;
use std::thread;
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 {
/// 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.
pub fn private_add_progress_bar(&mut self) {
let multibar = Arc::new(MultiProgress::new());
self.multibar = Some(multibar.clone());
let b = self.multibar
self.bars.multibar = Some(multibar.clone());
let b = self.bars.multibar
.as_ref()
.unwrap()
.add(ProgressBar::new_spinner());
@ -42,8 +73,8 @@ impl Book {
.template("{spinner:.dim.bold.yellow} {prefix} {wide_msg}");
b.set_style(sty);
b.enable_steady_tick(200);
self.mainbar = Some(b);
self.guard = Some(thread::spawn(move || {
self.bars.mainbar = Some(b);
self.bars.guard = Some(thread::spawn(move || {
if let Err(_) = multibar.join() {
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
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()
.tick_chars("/|\\-X")
.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
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()
.tick_chars("/|\\-V")
.template("{spinner:.dim.bold.cyan} {wide_msg}");
@ -76,48 +107,101 @@ impl Book {
/// Adds a secondary progress bar to display progress of book parsing
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));
bar.set_style(ProgressStyle::default_bar()
.template("{bar:40.cyan/blue} {percent:>7}% {msg}")
.progress_chars("##-"));
bar.set_message(msg);
self.secondbar = Some(bar);
self.bars.secondbar = Some(bar);
}
}
/// Finish secondary prograss bar
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();
}
}
/// Increment second bar
pub fn inc_second_bar(&self) {
if let Some(ref bar) = self.secondbar {
if let Some(ref bar) = self.bars.secondbar {
bar.inc(1);
}
}
/// 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 {
if let Some(ref mainbar) = self.mainbar {
mainbar.set_message(&lformat!("Rendering..."));
pub fn add_spinner_to_multibar(&mut self, key: &str) -> usize {
if let Some(ref multibar) = self.bars.multibar {
if let Some(ref mainbar) = self.bars.mainbar {
mainbar.set_message(&lformat!("Rendering..."));
}
let bar = multibar.add(ProgressBar::new_spinner());
let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-X")
.template(&format!("{{spinner:.dim.bold.yellow}} {format}: {{wide_msg:.yellow}}",
format = key));
bar.set_style(sty);
bar.enable_steady_tick(200);
bar.set_message(&lformat!("waiting..."));
bar.tick();
self.bars.spinners.push(bar);
return self.bars.spinners.len() - 1;
} else {
0
}
let bar = multibar.add(ProgressBar::new_spinner());
let sty = ProgressStyle::default_spinner()
.tick_chars("/|\\-X")
.template(&format!("{{spinner:.dim.bold.yellow}} {format}: {{wide_msg:.yellow}}",
format = key));
bar.set_style(sty);
bar.enable_steady_tick(200);
bar.set_message(&lformat!("waiting..."));
bar.tick();
bar
}
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
pub fn finish_spinner_error(&self, bar: &ProgressBar, key: &str, msg: &str) {
bar.set_style(ProgressStyle::default_spinner()
@ -138,3 +222,15 @@ impl Book {
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();
}
}
}