// Copyright (C) 2017-2022 Γ‰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 implementation. Moved into a different file so it is possible // to make some dependencies (incidacitf) optional. use crate::book::{Book, Crowbar, CrowbarState}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use std::sync::Arc; use std::time::Duration; /// 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) pub mainbar: Option, /// Secondary bar pub secondbar: Option, // /// Guard for thread // pub guard: Option>, /// Spinners for each renderier pub spinners: Vec, } impl Bars { /// Create a new bars storage pub fn new() -> Bars { Bars { emoji: false, multibar: None, mainbar: None, secondbar: None, // guard: None, spinners: vec![], } } } impl Default for Bars { fn default() -> Self { Self::new() } } /// 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, 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()); b.enable_steady_tick(Duration::from_millis(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") // ); // } // })); } /// Sets a finished message to the progress bar, if it is set 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.to_owned()), }; } /// 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)); self.bar_set_style(Crowbar::Second, CrowbarState::Running); bar.set_message(msg.to_owned()); self.bars.secondbar = Some(bar); } } /// Increment second bar pub fn inc_second_bar(&self) { 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(&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()); bar.enable_steady_tick(Duration::from_millis(200)); bar.set_message(lformat!("waiting...")); bar.set_prefix(format!("{}:", key)); let i = self.bars.spinners.len(); self.bars.spinners.push(bar); self.bar_set_style(Crowbar::Spinner(i), CrowbarState::Running); i } else { 0 } } 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.to_owned()); } /// 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 => "yellow", 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}") .expect("Error in second progress bar style") .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 )) .expect("Error in spinner progress bar style"); } _ => { style = style .template(&format!( "{{spinner:.bold.{color}}} {{prefix}}{{wide_msg}}", color = color )) .expect("Error in progress bar style"); } }; } } pb.set_style(style); } } impl Drop for Book { fn drop(&mut self) { if let Some(ref bar) = self.bars.secondbar { bar.finish_and_clear(); } if let Some(ref bar) = self.bars.mainbar { bar.finish(); // let guard = mem::replace(&mut self.bars.guard, None); // guard.unwrap().join().unwrap(); } } }