use crate::{timing, Baring};
use log::warn;
use std::{
cmp::min,
fmt::{self, Display},
};
#[derive(Debug)]
struct PrintController {
last_printed_progress: Option<f64>,
interesting_progress_step: f64,
}
impl PrintController {
fn from(interesting_progress_step: f64) -> PrintController {
PrintController {
last_printed_progress: None,
interesting_progress_step,
}
}
fn map(&self, progress: f64) -> usize {
let scale = 1_000.0;
(progress * scale) as usize / ((scale * self.interesting_progress_step) as usize)
}
fn has_progressed_significantly(&self, progress: f64) -> bool {
self.map(progress) > self.map(self.last_printed_progress.unwrap_or(0.0))
}
fn update(&mut self, progress: f64) {
self.last_printed_progress = Some(progress);
}
}
pub struct Config {
pub bar_len: usize,
pub style: String,
pub interesting_progress_step: f64,
}
impl Config {
pub fn new() -> Config {
Config::default()
}
}
impl Default for Config {
fn default() -> Config {
Config {
bar_len: 42,
style: String::from("[=>.]"),
interesting_progress_step: 0.1,
}
}
}
#[derive(Debug)]
pub struct Bar {
bar_len: usize,
style: String,
progress: f64,
print_controller: PrintController,
}
impl Bar {
pub fn new() -> Bar {
Bar::default()
}
pub fn with(cfg: Config) -> Bar {
Bar {
bar_len: cfg.bar_len,
style: cfg.style,
progress: 0.0,
print_controller: PrintController::from(cfg.interesting_progress_step),
}
}
pub fn timed(self) -> timing::Bar<Bar> {
timing::Bar::with(self)
}
pub fn set_style<S>(&mut self, style: S)
where
S: Into<String>,
{
let style = style.into();
if style.len() != 5 {
warn!("The bar-style has to consist of 5 characters, e.g. [=>-]");
};
self.style = style;
}
fn inner_bar_len(&self) -> usize {
self.len() - self.brackets_len()
}
fn brackets_len(&self) -> usize {
self.left_bracket().len() + self.right_bracket().len()
}
fn left_bracket(&self) -> &str {
&self.style[0..1]
}
fn line(&self) -> &str {
&self.style[1..2]
}
fn hat(&self) -> &str {
&self.style[2..3]
}
fn empty_line(&self) -> &str {
&self.style[3..4]
}
fn right_bracket(&self) -> &str {
&self.style[4..5]
}
}
impl Default for Bar {
fn default() -> Bar {
Bar::with(Config::default())
}
}
impl Baring for Bar {
type Progress = f64;
fn len(&self) -> usize {
self.bar_len
}
fn set_len(&mut self, new_bar_len: usize) {
self.bar_len = new_bar_len;
}
fn progress(&self) -> f64 {
self.progress
}
fn set<P>(&mut self, new_progress: P)
where
P: Into<f64>,
{
let mut new_progress = new_progress.into();
if new_progress < 0.0 {
new_progress = 0.0;
}
if 1.0 < new_progress {
new_progress = 1.0;
}
self.progress = new_progress;
}
fn start(&self) -> f64 {
0.0
}
fn end(&self) -> f64 {
1.0
}
fn has_progressed_significantly(&self) -> bool {
self.print_controller
.has_progressed_significantly(self.progress())
}
fn remember_significant_progress(&mut self) {
self.print_controller.update(self.progress());
}
}
impl Display for Bar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let reached: usize = (self.progress * self.inner_bar_len() as f64) as usize;
let line = self.line().repeat(reached);
let hat = &self.hat()[0..min(self.hat().len(), self.inner_bar_len() - reached)];
let empty_line = self
.empty_line()
.repeat(self.inner_bar_len() - reached - hat.len());
write!(
f,
"{}{}{}{}{}",
self.left_bracket(),
line,
hat,
empty_line,
self.right_bracket()
)
}
}