use std::io::{self, Write};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProgressState {
Clear,
Normal,
Error,
Indeterminate,
Warning,
}
impl ProgressState {
fn code(self) -> u8 {
match self {
ProgressState::Clear => 0,
ProgressState::Normal => 1,
ProgressState::Error => 2,
ProgressState::Indeterminate => 3,
ProgressState::Warning => 4,
}
}
}
pub fn encode(state: ProgressState, percent: u8) -> String {
format!("\x1b]9;4;{};{}\x07", state.code(), percent.min(100))
}
pub struct TerminalProgress {
active: bool,
}
impl TerminalProgress {
pub fn new() -> Self {
Self { active: false }
}
fn write(&mut self, state: ProgressState, percent: u8) {
let mut out = io::stdout();
if out.write_all(encode(state, percent).as_bytes()).is_ok() {
let _ = out.flush();
}
self.active = state != ProgressState::Clear;
}
pub fn indeterminate(&mut self) {
self.write(ProgressState::Indeterminate, 0);
}
pub fn percent(&mut self, percent: u8) {
self.write(ProgressState::Normal, percent);
}
pub fn error(&mut self, percent: u8) {
self.write(ProgressState::Error, percent);
}
pub fn clear(&mut self) {
if self.active {
self.write(ProgressState::Clear, 0);
}
}
pub fn is_active(&self) -> bool {
self.active
}
}
impl Default for TerminalProgress {
fn default() -> Self {
Self::new()
}
}
impl Drop for TerminalProgress {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn osc_progress_encoding() {
assert_eq!(encode(ProgressState::Indeterminate, 0), "\x1b]9;4;3;0\x07");
assert_eq!(encode(ProgressState::Normal, 50), "\x1b]9;4;1;50\x07");
assert_eq!(encode(ProgressState::Clear, 0), "\x1b]9;4;0;0\x07");
assert_eq!(encode(ProgressState::Error, 12), "\x1b]9;4;2;12\x07");
assert_eq!(encode(ProgressState::Normal, 200), "\x1b]9;4;1;100\x07");
}
}