use std::fmt::{self, Write, Display};
pub mod prefix;
pub trait Progress: Display {
fn set(&mut self, value: f32);
}
#[derive(Debug, Copy, Clone)]
pub struct Bar {
progress: f32,
}
impl Bar {
pub fn new() -> Self { Bar {
progress: 0.0,
}}
pub fn get(&self) -> f32 { self.progress }
}
impl Progress for Bar {
fn set(&mut self, value: f32) { self.progress = value; }
}
impl Display for Bar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let width = f.width().unwrap_or(80) as u32;
let count = width as f32 * self.progress.max(0.0).min(1.0);
let whole = count.trunc() as u32;
for _ in 0..whole {
f.write_char('█')?;
}
let fraction = (count.fract() * 8.0).trunc() as u32;
let fill = f.fill();
if whole < width {
f.write_char(match fraction {
0 => fill,
1 => '▏',
2 => '▎',
3 => '▍',
4 => '▌',
5 => '▋',
6 => '▊',
7 => '▉',
_ => unreachable!(),
})?;
for _ in whole..(width - 1) {
f.write_char(fill)?;
}
}
Ok(())
}
}
pub trait Spinner: Display {
fn set(&mut self, value: u32);
fn step(&mut self, count: u32);
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub struct Counter256 {
state: u8
}
impl Counter256 {
pub fn new() -> Self { Self { state: 0 } }
}
impl Spinner for Counter256 {
fn set(&mut self, state: u32) { self.state = state as u8; }
fn step(&mut self, count: u32) { self.state = self.state.wrapping_add(count as u8); }
}
fn braille_binary(value: u8) -> char {
let value = (value & 0b10000111)
| ((value & 0b00001000) << 3)
| ((value & 0b01110000) >> 1);
unsafe { ::std::char::from_u32_unchecked(0x2800 + value as u32) }
}
impl Display for Counter256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char(braille_binary(self.state))
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub struct Spinner8 {
state: u8
}
const SPINNER8_STATES: [char; 8] = ['⡀', '⠄', '⠂', '⠁', '⠈', '⠐', '⠠', '⢀'];
impl Spinner8 {
pub fn new() -> Self { Self { state: 0 } }
}
impl Spinner for Spinner8 {
fn set(&mut self, state: u32) { self.state = state as u8 % SPINNER8_STATES.len() as u8; }
fn step(&mut self, count: u32) { self.state = self.state.wrapping_add(count as u8) % SPINNER8_STATES.len() as u8; }
}
impl Display for Spinner8 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char(*unsafe { SPINNER8_STATES.get_unchecked(self.state as usize) })
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub struct Counter16 {
state: u8
}
const COUNTER16_STATES: [char; 16] = [' ', '▘', '▖', '▌', '▝', '▀', '▞', '▛', '▗', '▚', '▄', '▙', '▐', '▜', '▟', '█'];
impl Counter16 {
pub fn new() -> Self { Self { state: 0 } }
}
impl Spinner for Counter16 {
fn set(&mut self, state: u32) { self.state = state as u8 % COUNTER16_STATES.len() as u8; }
fn step(&mut self, count: u32) { self.state = self.state.wrapping_add(count as u8) % COUNTER16_STATES.len() as u8; }
}
impl Display for Counter16 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char(*unsafe { COUNTER16_STATES.get_unchecked(self.state as usize) })
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub struct Spinner4 {
state: u8
}
const SPINNER4_STATES: [char; 4] = ['▖', '▘', '▝', '▗'];
impl Spinner4 {
pub fn new() -> Self { Self { state: 0 } }
}
impl Spinner for Spinner4 {
fn set(&mut self, state: u32) { self.state = state as u8 % SPINNER4_STATES.len() as u8; }
fn step(&mut self, count: u32) { self.state = self.state.wrapping_add(count as u8) % SPINNER4_STATES.len() as u8; }
}
impl Display for Spinner4 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char(*unsafe { SPINNER4_STATES.get_unchecked(self.state as usize) })
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub struct Snake {
state: u32
}
impl Snake {
pub fn new() -> Self { Self { state: 0 } }
}
impl Spinner for Snake {
fn set(&mut self, state: u32) { self.state = state; }
fn step(&mut self, count: u32) { self.state = self.state.wrapping_add(count); }
}
impl Display for Snake {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const WOBBLE: u32 = 5;
let length = (((self.state % (2*WOBBLE)) as i32 - (WOBBLE as i32)).abs() + 1) as u32;
let bits = !(0xFFu8 << length);
let position = (WOBBLE * (self.state / (2*WOBBLE)) + (self.state % (2*WOBBLE)).saturating_sub(WOBBLE)) as u8;
let snake = bits.rotate_right(position as u32);
let value = snake & 0xF
| ((snake & 0b10000000) >> 3)
| ((snake & 0b01000000) >> 1)
| ((snake & 0b00100000) << 1)
| ((snake & 0b00010000) << 3);
f.write_char(braille_binary(value))
}
}
#[derive(Debug, Copy, Clone)]
pub struct MovingAverage {
alpha: f32,
value: f32,
}
impl MovingAverage {
pub fn new(alpha: f32, initial: f32) -> Self { Self { alpha, value: initial } }
pub fn update(&mut self, value: f32) {
self.value = self.alpha * value + (1.0 - self.alpha) * self.value;
}
pub fn get(&self) -> f32 { self.value }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bar_sanity() {
let mut bar = Bar::new();
assert_eq!(format!("{:10}", bar), " ");
bar.set(1.0);
assert_eq!(format!("{:10}", bar), "██████████");
}
}