pub struct Bar {
pub current_partial: usize,
pub total: usize,
width: usize,
empty_char: char,
full_char: char,
leading_char: char,
include_percent: bool,
include_numbers: bool,
previous_text_width: usize,
}
pub struct BarBuilder {
bar: Bar,
}
impl Default for BarBuilder {
fn default() -> Self {
BarBuilder::new()
}
}
impl BarBuilder {
pub fn new() -> Self {
BarBuilder {
bar: Bar::default(),
}
}
pub fn total(mut self, total: usize) -> BarBuilder {
self.bar.total = total;
self
}
pub fn width(mut self, width: usize) -> BarBuilder {
self.bar.width = width;
self
}
pub fn empty_char(mut self, character: char) -> BarBuilder {
self.bar.empty_char = character;
self
}
pub fn full_char(mut self, character: char) -> BarBuilder {
self.bar.full_char = character;
self
}
pub fn leading_char(mut self, character: impl Into<Option<char>>) -> BarBuilder {
if let Some(char) = character.into() {
self.bar.leading_char = char;
} else {
self.bar.leading_char = self.bar.full_char;
}
self
}
pub fn include_percent(mut self) -> BarBuilder {
self.bar.include_percent = true;
self
}
pub fn include_numbers(mut self) -> BarBuilder {
self.bar.include_numbers = true;
self
}
#[deprecated]
pub fn get_bar(self) -> Bar {
self.bar
}
pub fn build(self) -> Bar {
self.bar
}
}
impl Default for Bar {
fn default() -> Self {
Self {
current_partial: 0,
total: 100,
width: 50,
full_char: '█',
empty_char: ' ',
leading_char: '█',
include_percent: false,
include_numbers: false,
previous_text_width: 0,
}
}
}
impl Bar {
pub fn update(&mut self, to_add: usize) {
self.previous_text_width = self.get_width();
self.current_partial += to_add;
}
pub fn replace(&mut self, new_progress: usize) {
self.previous_text_width = self.get_width();
self.current_partial = new_progress;
}
pub fn get_width(&self) -> usize {
let mut width: usize = 52;
if self.include_numbers {
let total_string = format!("{}", self.total);
let partial_string = format!("{}", self.current_partial);
width += total_string.len() + partial_string.len() + 2;
}
if self.include_percent {
let current_percent = self.calculate_percent();
if current_percent >= 0.95 {
width += 8;
} else if current_percent > 0.095 {
width += 7;
} else {
width += 6;
}
}
width
}
pub fn get_last_width(&self) -> usize {
self.previous_text_width
}
fn calculate_percent(&self) -> f32 {
self.current_partial as f32 / self.total as f32
}
}
impl std::fmt::Display for Bar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let percent = self.calculate_percent();
f.write_str("[")?;
for i in 0..self.width {
if (i as f32) < ((self.width as f32 * percent) - 1.0) {
f.write_fmt(format_args!("{}", self.full_char))?;
} else if (i as f32) < (self.width as f32 * percent) {
f.write_fmt(format_args!("{}", self.leading_char))?;
} else {
f.write_fmt(format_args!("{}", self.empty_char))?;
}
}
f.write_str("]")?;
if self.include_percent {
f.write_fmt(format_args!(" {:.2}%", percent * 100.0))?;
}
if self.include_numbers {
f.write_fmt(format_args!(" {:?}/{:?}", self.current_partial, self.total))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn include_percent_test() {
let mut bar = BarBuilder::new().include_percent().build();
assert_eq!(bar.get_width(), 58);
assert_eq!(
format!("{}", bar),
"[ ] 0.00%"
);
bar.update(50);
assert_eq!(bar.get_width(), 59);
assert_eq!(
format!("{}", bar),
"[█████████████████████████ ] 50.00%"
);
bar.update(50);
assert_eq!(bar.get_width(), 60);
assert_eq!(
format!("{}", bar),
"[██████████████████████████████████████████████████] 100.00%"
);
}
#[test]
fn include_numbers_test() {
let mut bar = BarBuilder::new().include_numbers().build();
assert_eq!(bar.get_width(), 58);
assert_eq!(
format!("{}", bar),
"[ ] 0/100"
);
bar.update(50);
assert_eq!(bar.get_width(), 59);
assert_eq!(
format!("{}", bar),
"[█████████████████████████ ] 50/100"
);
bar.update(50);
assert_eq!(bar.get_width(), 60);
assert_eq!(
format!("{}", bar),
"[██████████████████████████████████████████████████] 100/100"
);
}
#[test]
fn update_test() {
let mut bar = Bar::default();
bar.update(50);
assert_eq!(bar.current_partial, 50);
assert_eq!(
format!("{}", bar),
"[█████████████████████████ ]"
);
}
#[test]
fn replace_test() {
let mut bar = Bar::default();
bar.update(50);
assert_eq!(bar.current_partial, 50);
assert_eq!(
format!("{}", bar),
"[█████████████████████████ ]"
);
bar.replace(10);
assert_eq!(bar.current_partial, 10);
assert_eq!(
format!("{}", bar),
"[█████ ]"
);
}
#[test]
fn to_string_test() {
let mut bar = Bar::default();
assert_eq!(
bar.to_string(),
"[ ]"
);
bar.update(50);
assert_eq!(
bar.to_string(),
"[█████████████████████████ ]"
)
}
#[test]
fn leading_char() {
let mut bar = BarBuilder::new().leading_char('>').build();
assert_eq!(
bar.to_string(),
"[ ]"
);
bar.update(50);
assert_eq!(
bar.to_string(),
"[████████████████████████> ]"
)
}
#[test]
fn display() {
let mut bar = BarBuilder::new().build();
assert_eq!(
format!("{}", bar),
"[ ]"
);
bar.update(50);
assert_eq!(
format!("{}", bar),
"[█████████████████████████ ]"
)
}
}