diff --git a/Cargo.toml b/Cargo.toml index 92e00b6..039126b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ doc = false [features] default = ["binary", "proofread", "syntect"] proofread = ["caribon", "hyper", "url"] -binary = ["clap", "simplelog", "tempdir", "console"] +binary = ["clap", "simplelog", "tempdir", "console", "indicatif"] [build-dependencies] crowbook-intl = "0.2" @@ -57,7 +57,7 @@ crowbook-intl-runtime = "0.1" numerals = "0.1" epub-builder = "0.3" log = "0.3" -indicatif = "0.7" +indicatif = { version = "0.7", optional = true } console = { version = "0.5", optional = true } caribon = { version = "0.8", optional = true } clap = { version = "2.19", optional = true } diff --git a/src/bin/real_main.rs b/src/bin/real_main.rs index 7ad5138..6e7e847 100644 --- a/src/bin/real_main.rs +++ b/src/bin/real_main.rs @@ -172,7 +172,7 @@ pub fn try_main() -> Result<()> { { let mut book = Book::new(); if fancy_ui { - book.add_progress_bar(); + book.add_progress_bar(true); } book.set_options(&get_book_options(&matches)); diff --git a/src/lib/book.rs b/src/lib/book.rs index cab464a..98e924a 100644 --- a/src/lib/book.rs +++ b/src/lib/book.rs @@ -35,21 +35,18 @@ use book_renderer::BookRenderer; use chapter::Chapter; use token::Token; use text_view::view_as_text; + +#[cfg(feature = "indicatif")] use book_bars::Bars; - -use std::thread; -use std::sync::Arc; - -use indicatif::{ProgressBar, MultiProgress}; +#[cfg(not(feature = "indicatif"))] +use book_bars_stubs::Bars; #[cfg(feature = "proofread")] use repetition_check::RepetitionDetector; - #[cfg(feature = "proofread")] use grammar_check::GrammarChecker; #[cfg(feature = "proofread")] use grammalecte::GrammalecteChecker; - // Dummy grammarchecker thas does nothing to let the compiler compile #[cfg(not(feature = "proofread"))] struct GrammarChecker {} @@ -117,6 +114,23 @@ pub struct HeaderData { pub title: String, } +/// The types of bars +#[derive(Copy, Clone)] +pub enum Crowbar { + Main, + Second, + Spinner(usize), +} + +/// The state of bars +#[derive(Copy, Clone)] +pub enum CrowbarState { + Running, + Success, + Error, +} + + impl fmt::Display for HeaderData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.text) @@ -215,7 +229,7 @@ impl Book { /// Sets an error message to the progress bar, if it is set pub fn set_error(&self, msg: &str) { - self.private_set_error(msg) + self.bar_finish(Crowbar::Main, CrowbarState::Error, msg) } @@ -223,8 +237,8 @@ impl Book { /// Adds a progress bar where where info should be written. /// /// See [indicatif doc](https://docs.rs/indicatif) for more information. - pub fn add_progress_bar(&mut self) { - self.private_add_progress_bar(); + pub fn add_progress_bar(&mut self, emoji: bool) { + self.private_add_progress_bar(emoji); } /// Register a format that can be rendered. @@ -481,7 +495,7 @@ impl Book { Ok(words[0]) } - self.mainbar_set_message(&lformat!("setting options")); + self.bar_set_message(Crowbar::Main, &lformat!("setting options")); let mut s = String::new(); source.read_to_string(&mut s) @@ -566,7 +580,7 @@ impl Book { // Update grammar checker according to options (proofread.*) self.init_checker(); - self.mainbar_set_message(&lformat!("Parsing chapters")); + self.bar_set_message(Crowbar::Main, &lformat!("Parsing chapters")); // Parse chapters let lines: Vec<_> = lines.collect(); @@ -664,7 +678,7 @@ impl Book { } } - self.finish_second_bar(); + self.bar_finish(Crowbar::Second, CrowbarState::Success, ""); self.source.unset_line(); self.set_chapter_template()?; @@ -785,7 +799,7 @@ impl Book { self.render_format_with_bar(fmt, i); }); - self.set_finished(&lformat!("Finished")); + self.bar_finish(Crowbar::Main, CrowbarState::Success, &lformat!("Finished")); // if handles.is_empty() { // Logger::display_warning(lformat!("Crowbook generated no file because no output file was \ @@ -798,10 +812,12 @@ impl Book { 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...")); + self.bar_set_message(Crowbar::Spinner(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)); + self.bar_finish(Crowbar::Spinner(bar), + CrowbarState::Error, + &format!("{}", err)); error!("{}", lformat!("Error rendering {name}: {error}", name = format, error = err)); } } @@ -840,10 +856,10 @@ impl Book { format = description, path = &path); info!("{}", &msg); - self.finish_nth_spinner_success(bar, - format, - &lformat!("generated {path}", - path = path)); + self.bar_finish(Crowbar::Spinner(bar), + CrowbarState::Success, + &lformat!("generated {path}", + path = path)); Ok(()) }, None => { @@ -875,19 +891,19 @@ impl Book { 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")); + self.bar_finish(Crowbar::Spinner(bar), + CrowbarState::Success, + &lformat!("generated {format}", + format = format)); + self.bar_finish(Crowbar::Main, CrowbarState::Success, &lformat!("Finished")); info!("{}", lformat!("Succesfully generated {format}", format = description)); Ok(()) }, None => { - self.finish_nth_spinner_error(bar, - format, - &lformat!("unknown format")); + self.bar_finish(Crowbar::Spinner(bar), + CrowbarState::Error, + &lformat!("unknown format")); Err(Error::default(Source::empty(), lformat!("unknown format {format}", format = format))) @@ -919,11 +935,11 @@ impl Book { 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")); + self.bar_finish(Crowbar::Spinner(bar), + CrowbarState::Success, + &lformat!("generated {path}", + path = normalized)); + self.bar_finish(Crowbar::Main, CrowbarState::Success, &lformat!("Finished")); Ok(()) } @@ -937,7 +953,7 @@ impl Book { file: &str, mut source: R) -> Result<&mut Self> { - self.mainbar_set_message(&lformat!("Processing {file}...", file = file)); + self.bar_set_message(Crowbar::Main, &lformat!("Processing {file}...", file = file)); let mut content = String::new(); source.read_to_string(&mut content) .map_err(|_| { @@ -950,7 +966,7 @@ impl Book { self.parse_yaml(&mut content); // parse the file - self.secondbar_set_message(&lformat!("Parsing...")); + self.bar_set_message(Crowbar::Second, &lformat!("Parsing...")); let mut parser = Parser::from(self); parser.set_source_file(file); @@ -999,7 +1015,7 @@ impl Book { if cfg!(feature = "proofread") && self.is_proofread() { let normalized = misc::normalize(file); if let Some(ref checker) = self.checker { - self.secondbar_set_message(&lformat!("Running languagetool")); + self.bar_set_message(Crowbar::Second, &lformat!("Running languagetool")); info!("{}", lformat!("Trying to run languagetool on {file}, this might take a \ while...", @@ -1011,7 +1027,7 @@ impl Book { } } if let Some(ref checker) = self.grammalecte { - self.secondbar_set_message(&lformat!("Running grammalecte")); + self.bar_set_message(Crowbar::Second, &lformat!("Running grammalecte")); info!("{}", lformat!("Trying to run grammalecte on {file}, this might take a \ while...", @@ -1023,7 +1039,7 @@ impl Book { } } if let Some(ref detector) = self.detector { - self.secondbar_set_message(&lformat!("Detecting repetitions")); + self.bar_set_message(Crowbar::Second, &lformat!("Detecting repetitions")); info!("{}", lformat!("Trying to run repetition detector on {file}, this might take a \ while...", @@ -1035,7 +1051,7 @@ impl Book { } } } - self.secondbar_set_message(""); + self.bar_set_message(Crowbar::Second, ""); self.chapters.push(Chapter::new(number, file, tokens)); @@ -1089,8 +1105,9 @@ 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> { - self.mainbar_set_message(&lformat!("Parsing {file}", - file = misc::normalize(file))); + self.bar_set_message(Crowbar::Main, + &lformat!("Parsing {file}", + file = misc::normalize(file))); debug!("{}", lformat!("Parsing chapter: {file}...", file = misc::normalize(file))); diff --git a/src/lib/book_bars.rs b/src/lib/book_bars.rs index 10a21f3..5dda1d3 100644 --- a/src/lib/book_bars.rs +++ b/src/lib/book_bars.rs @@ -18,19 +18,18 @@ // Progress bars implementation. Moved into a different file so it is possible // to make some dependencies (incidacitf) optional. -use book::Book; -use error::{Result, Error, Source}; -use misc; +use book::{Book, Crowbar, CrowbarState}; 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 { + /// Whether or not to use emoji + pub emoji: bool, /// Container for the progress bars pub multibar: Option>, /// Main progress bar (actually a spinner) @@ -47,6 +46,7 @@ impl Bars { /// Create a new bars storage pub fn new() -> Bars { Bars { + emoji: false, multibar: None, mainbar: None, secondbar: None, @@ -56,24 +56,28 @@ impl Bars { } } + +/// Return the style of a bar + impl Book { /// Adds a progress bar where where info should be written. /// /// 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, emoji: bool) { + self.bars.emoji = emoji; let multibar = Arc::new(MultiProgress::new()); self.bars.multibar = Some(multibar.clone()); let b = self.bars.multibar .as_ref() .unwrap() .add(ProgressBar::new_spinner()); - let sty = ProgressStyle::default_spinner() - .tick_chars("πŸ•›πŸ•πŸ•‘πŸ•’πŸ•“πŸ•”πŸ•”πŸ••πŸ•–πŸ•—πŸ•˜πŸ•˜πŸ•™πŸ•šV") -// .tick_chars("/|\\-V") - .template("{spinner:.dim.bold.yellow} {prefix} {wide_msg}"); - b.set_style(sty); b.enable_steady_tick(200); self.bars.mainbar = Some(b); +// let sty = ProgressStyle::default_spinner() +// .tick_chars("πŸ•›πŸ•πŸ•‘πŸ•’πŸ•“πŸ•”πŸ•”πŸ••πŸ•–πŸ•—πŸ•˜πŸ•˜πŸ•™πŸ•šV") +// .tick_chars("/|\\-V") +// .template("{spinner:.dim.bold.yellow} {prefix} {wide_msg}"); + self.bar_set_style(Crowbar::Main, CrowbarState::Running); 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")); @@ -81,49 +85,31 @@ 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.bars.mainbar { - let sty = ProgressStyle::default_spinner() - .tick_chars("/|\\-X") - .template("{spinner:.dim.bold.red} {wide_msg}"); - mainbar.set_style(sty); - mainbar.set_message(msg); - } - } - - /// 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.bars.mainbar { - let sty = ProgressStyle::default_spinner() - .tick_chars("/|\\-V") - .template("{spinner:.dim.bold.cyan} {wide_msg}"); - bar.set_style(sty); - bar.set_message(msg); - } + pub fn bar_finish(&self, bar: Crowbar, state: CrowbarState, msg: &str) { + self.bar_set_style(bar, state); + let pb = match bar { + Crowbar::Main => if let Some(ref bar) = self.bars.mainbar { bar } else { return; }, + Crowbar::Second => if let Some(ref bar) = self.bars.secondbar { bar } else { return; }, + Crowbar::Spinner(i) => if i < self.bars.spinners.len() { &self.bars.spinners[i] } else { return; }, + }; + match bar { + Crowbar::Second => pb.finish_and_clear(), + _ => pb.finish_with_message(msg), + }; } /// 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.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("##-")); + self.bar_set_style(Crowbar::Second, CrowbarState::Running); bar.set_message(msg); self.bars.secondbar = Some(bar); } } - /// Finish secondary prograss bar - pub fn finish_second_bar(&self) { - 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.bars.secondbar { @@ -139,87 +125,82 @@ impl Book { } 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.set_prefix(&format!("{}:", key)); + let i = self.bars.spinners.len(); self.bars.spinners.push(bar); - return self.bars.spinners.len() - 1; + self.bar_set_style(Crowbar::Spinner(i), CrowbarState::Running); + + i } 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 bar_set_message(&self, bar: Crowbar, msg: &str) { + let bar = match bar { + Crowbar::Main => if let Some(ref bar) = self.bars.mainbar { bar } else { return; }, + Crowbar::Second => if let Some(ref bar) = self.bars.secondbar { bar } else { return; }, + Crowbar::Spinner(i) => if i < self.bars.spinners.len() { &self.bars.spinners[i] } else { return; }, + }; + bar.set_message(msg); } - 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(); + /// Sets the style of a bar + fn bar_set_style(&self, bar: Crowbar, state: CrowbarState) -> () { + let pb = match bar { + Crowbar::Main => if let Some(ref bar) = self.bars.mainbar { bar } else { return; }, + Crowbar::Second => if let Some(ref bar) = self.bars.secondbar { bar } else { return; }, + Crowbar::Spinner(i) => if i < self.bars.spinners.len() { &self.bars.spinners[i] } else { return; }, + + }; + let emoji = self.bars.emoji; + let mut style = match bar { + Crowbar::Second => ProgressStyle::default_bar(), + _ => ProgressStyle::default_spinner() + }; + + let color = match state { + CrowbarState::Running => "magenta", + CrowbarState::Success => "cyan", + CrowbarState::Error => "red", + }; + let tick_chars = match (bar, emoji) { + (Crowbar::Main, false) | (Crowbar::Spinner(_), false) => "-\\|/", + (Crowbar::Main, true) => "πŸ•›πŸ•πŸ•‘πŸ•’πŸ•“πŸ•”πŸ•”πŸ••πŸ•–πŸ•—πŸ•˜πŸ•˜πŸ•™πŸ•š", + (Crowbar::Spinner(_), true) => "-\\|/", + (_, _) => "" + }; + let end_tick = match (state, emoji) { + (CrowbarState::Running, _) => "V", + (CrowbarState::Success, true) => "βœ”", + (CrowbarState::Error, true) => "❌", + (CrowbarState::Success, false) => "V", + (CrowbarState::Error, false) => "X", + }; + match bar { + Crowbar::Second => { + style = style.template("{bar:40.cyan/blue} {percent:>7} {wide_msg}") + .progress_chars("##-"); + }, + bar => { + style = style.tick_chars(&format!("{}{}", tick_chars, end_tick)); + match bar { + Crowbar::Spinner(_) => { + style = style + .template(&format!("{{spinner:.bold.{color}}} {{prefix}} {{wide_msg}}", + color = color)); + }, + _ => { + style = style + .template(&format!("{{spinner:.bold.{color}}} {{prefix}}{{wide_msg}}", + color = color)); + }, + }; + }, } - } - - // 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() - .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_spinner_success(&self, bar: &ProgressBar, key: &str, msg: &str) { - 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); + pb.set_style(style); } } diff --git a/src/lib/book_bars_stubs.rs b/src/lib/book_bars_stubs.rs new file mode 100644 index 0000000..7d367d6 --- /dev/null +++ b/src/lib/book_bars_stubs.rs @@ -0,0 +1,57 @@ +// Copyright (C) 2017 Γ‰lisabeth HENRY. +// +// This file is part of Crowbook. +// +// Crowbook is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 2.1 of the License, or +// (at your option) any later version. +// +// Crowbook is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Crowbook. If not, see . + +// Progress bars non-implementation. Used when indicatif is not compiled. + +use book::{Book, Crowbar, CrowbarState}; + +/// Dummy bars implementation +pub struct Bars { +} + +impl Bars { + pub fn new() -> Bars { + Bars { + } + } +} + + +impl Book { + pub fn private_add_progress_bar(&mut self, _: bool) { + } + + /// Sets a finished message to the progress bar, if it is set + pub fn bar_finish(&self, _: Crowbar, _: CrowbarState, _: &str) { + } + + /// Adds a secondary progress bar to display progress of book parsing + pub fn add_second_bar(&mut self, _: &str, _: u64) { + } + + /// Increment second bar + pub fn inc_second_bar(&self) { + } + + /// Adds a spinner labeled key to the multibar, and set mainbar to "rendering" + pub fn add_spinner_to_multibar(&mut self, _: &str) -> usize { + 0 + } + + pub fn bar_set_message(&self, _: Crowbar, _: &str) { + } +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 4c618a5..fa544bb 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -116,12 +116,13 @@ extern crate crowbook_intl_runtime; extern crate numerals; extern crate epub_builder; extern crate uuid; -extern crate indicatif; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; +#[cfg(feature = "indicatif")] +extern crate indicatif; #[cfg(feature = "proofread")] extern crate hyper; #[cfg(feature = "proofread")] @@ -171,7 +172,11 @@ mod html_single; mod html_if; mod syntax; mod stats; + +#[cfg(feature = "indicatif")] mod book_bars; +#[cfg(not(feature = "indicatif"))] +mod book_bars_stubs; mod zipper; mod templates;