use std::fmt::Debug;
use std::io::{Stdout, Write};
pub const INDENT_STR: &str = " ";
#[derive(Debug)]
pub struct ReplPrinter {
stdout: Stdout,
chars_written: usize,
finished: bool,
options: PrintOptions,
}
impl ReplPrinter {
pub fn new(verbose: bool) -> Self {
Self::from_options(PrintOptions::new(verbose, 0))
}
pub fn from_options(options: PrintOptions) -> Self {
Self {
stdout: std::io::stdout(),
chars_written: 0,
finished: false,
options,
}
}
pub fn indent(&mut self, indent: usize) {
self.options.indent = indent;
self.options.indent_str = make_indent(self.options.indent);
}
pub fn clearline(&mut self) {
if self.chars_written == 0 {
return;
}
let _ = write!(self.stdout, "\r");
for _ in 0..self.chars_written {
let _ = write!(self.stdout, " ");
}
self.chars_written = 0;
let _ = self.stdout.flush();
}
pub fn print(&mut self, text: &str) {
if !self.options.verbose {
return;
}
let _ = write!(self.stdout, "\r{}{text}", self.options.indent_str);
let written = get_terminal_width(text) + self.options.indent_str.chars().count();
let clear_count = self.chars_written.checked_sub(written).unwrap_or_default();
let _ = write!(self.stdout, "{}", " ".repeat(clear_count));
self.chars_written = written;
let _ = self.stdout.flush();
}
pub fn println(&mut self, text: &str) {
self.chars_written = 0;
let _ = writeln!(self.stdout);
self.print(text);
}
pub fn finish(&mut self) {
if self.finished {
return;
}
if self.chars_written != 0 {
self.newline();
}
self.finished = true;
}
pub fn newline(&mut self) {
self.println("");
}
}
impl Drop for ReplPrinter {
fn drop(&mut self) {
self.finish();
}
}
pub fn make_indent(indent: usize) -> String {
INDENT_STR.repeat(indent)
}
#[derive(Debug, Clone)]
pub struct PrintOptions {
pub verbose: bool,
pub indent: usize,
pub indent_str: String,
}
impl PrintOptions {
pub fn new(verbose: bool, indent: usize) -> Self {
Self {
verbose,
indent,
indent_str: make_indent(indent),
}
}
pub fn increase_indent(opt: &Self) -> Self {
let mut out = opt.clone();
out.indent += 1;
out.indent_str = make_indent(out.indent);
out
}
}
pub fn get_terminal_width(text: &str) -> usize {
let esc = 0o33 as char;
let mut out = 0;
let mut in_escape = false;
for c in text.chars() {
if c == esc {
in_escape = true;
}
if !in_escape {
out += 1;
}
if c == 'm' {
in_escape = false;
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_terminal_width() {
assert_eq!(get_terminal_width("\u{001b}[16mHello"), 5);
}
}