use std::fmt::{self, Display, Formatter, Write as _};
use owo_colors::OwoColorize;
pub mod styles {
use super::Bar;
pub const PARALLELOGRAM: Bar = Bar::new('▱', '▰');
pub const SHADED: Bar = Bar::new('░', '█');
pub const MEDIUM_SHADED: Bar = Bar::new('░', '▒');
pub const HEAVY_SHADED: Bar = Bar::new('▒', '▓');
pub const DOTTED: Bar = Bar::new('⣀', '⣿');
pub const THIN_LINE: Bar = Bar::new('─', '━');
pub const TRIPLE_DASH: Bar = Bar::new('┄', '┅');
pub const MID_DOTS: Bar = Bar::new('·', '•');
pub const EQUALS: Bar = Bar::new('╌', '═');
}
#[derive(Default, Clone)]
pub struct Bar<'a> {
empty: Option<char>,
complete: Option<char>,
in_between: Option<&'a str>,
left_border: Option<&'a str>,
right_border: Option<&'a str>,
filled_style: Option<owo_colors::Style>,
empty_style: Option<owo_colors::Style>,
}
impl Bar<'_> {
pub const fn new(empty: char, complete: char) -> Self {
Self {
empty: Some(empty),
complete: Some(complete),
in_between: None,
left_border: None,
right_border: None,
filled_style: None,
empty_style: None,
}
}
pub(crate) const fn empty() -> Self {
Self {
empty: None,
complete: None,
in_between: None,
left_border: None,
right_border: None,
filled_style: None,
empty_style: None,
}
}
pub fn render(&self, width: usize, completed: f64) -> String {
let mut buf = String::new();
self.render_into(&mut buf, width, completed);
buf
}
pub fn render_into(&self, buf: &mut String, width: usize, completed: f64) {
let completed = (completed * width as f64) as usize;
let remaining = width.saturating_sub(completed);
if let Some(left) = self.left_border {
buf.push_str(left);
}
if let Some(c) = self.complete {
let run = CharRun::new(c, completed);
match self.filled_style {
Some(style) => {
let _ = write!(buf, "{}", run.style(style));
}
None => {
let _ = write!(buf, "{run}");
}
}
}
if let Some(in_between) = self.in_between {
buf.push_str(in_between);
}
if let Some(c) = self.empty {
let run = CharRun::new(c, remaining);
match self.empty_style {
Some(style) => {
let _ = write!(buf, "{}", run.style(style));
}
None => {
let _ = write!(buf, "{run}");
}
}
}
if let Some(right) = self.right_border {
buf.push_str(right);
}
}
pub const fn with_in_between(mut self, chars: &'static str) -> Self {
self.in_between = Some(chars);
self
}
pub const fn with_border(mut self, left: &'static str, right: &'static str) -> Self {
self.left_border = Some(left);
self.right_border = Some(right);
self
}
pub const fn with_filled_style(mut self, style: owo_colors::Style) -> Self {
self.filled_style = Some(style);
self
}
pub const fn with_empty_style(mut self, style: owo_colors::Style) -> Self {
self.empty_style = Some(style);
self
}
}
struct CharRun {
ch: char,
count: usize,
}
impl CharRun {
const fn new(ch: char, count: usize) -> Self {
Self { ch, count }
}
}
impl Display for CharRun {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for _ in 0..self.count {
f.write_char(self.ch)?;
}
Ok(())
}
}