use std::io;
use std::io::Write;
const LEN: usize = 66;
const INNER_INDENT: usize = 1;
pub fn print_header(text: &str) {
fn left_aligned_string(s: &str, total_len: usize) -> String {
let text_len = s.chars().count();
if text_len >= total_len {
return s.to_string();
}
let padding = total_len - text_len;
format!("{}{}", s, " ".repeat(padding))
}
print_border();
let mut remaining = text.to_string();
while !remaining.is_empty() {
if remaining.chars().count() <= LEN - 4 {
println!("| {} |", left_aligned_string(&remaining, LEN - 4));
break;
} else {
let mut line = String::from(&remaining[..LEN-4]);
if let Some(last_space) = line.rfind(' ') {
line.truncate(last_space);
remaining = remaining[last_space..].trim_start().to_string();
} else {
line = String::from(&remaining[..LEN-4]);
remaining = remaining[LEN-4..].trim_start().to_string();
}
println!("| {} |", left_aligned_string(&line, LEN - 4));
}
}
print_border();
}
pub struct LineBuilder {
current_line: String,
current_line_prompt: String,
pb_num_steps: usize,
pb_chars_available: usize,
pb_steps_per_char: f64,
}
impl Default for LineBuilder {
fn default() -> Self {
Self::new()
}
}
impl LineBuilder {
#[allow(clippy::repeat_once)]
pub fn new() -> Self {
Self {
current_line: " ".repeat(INNER_INDENT),
current_line_prompt: " ".repeat(INNER_INDENT),
pb_num_steps: 0,
pb_steps_per_char: 0.0,
pb_chars_available: 0,
}
}
#[allow(clippy::repeat_once)]
pub fn line_begin(&mut self, text: &str) {
self.current_line.clear();
self.current_line_prompt.clear();
let inner_indent = " ".repeat(INNER_INDENT);
self.current_line.push_str(&inner_indent);
self.current_line_prompt.push_str(&inner_indent);
self.current_line.push_str(text);
self.current_line_prompt.push_str(text);
self.print_and_clear(false);
io::stdout().flush().unwrap();
}
pub fn line_end(&mut self, text: &str) {
print!("\r"); self.current_line.push(' ');
self.current_line.push_str(text);
self.print_and_clear(true);
}
pub fn progress_bar_setup(&mut self, num_steps: usize) {
print!("\r");
self.pb_chars_available = LEN - 4 - 4 - self.current_line_prompt.chars().count();
self.pb_num_steps = num_steps;
if self.pb_chars_available >= self.pb_num_steps {
self.pb_steps_per_char = self.pb_chars_available as f64 / num_steps as f64;
} else {
self.pb_steps_per_char = num_steps as f64 / self.pb_chars_available as f64;
}
self.current_line.clear();
self.current_line.push_str(self.current_line_prompt.as_str());
self.current_line.push(' ');
self.current_line.push('>');
self.current_line.push_str(&".".repeat(self.pb_chars_available));
self.print_and_clear(false);
io::stdout().flush().unwrap();
}
pub fn progress_bar_update(&mut self, current_step: usize) {
let current_step = if current_step > self.pb_num_steps {
self.pb_num_steps
} else {
current_step
};
let num_filled = if self.pb_chars_available >= self.pb_num_steps {
self.pb_steps_per_char.floor() as usize * current_step
} else {
current_step / self.pb_steps_per_char as usize
};
let len_bar = if num_filled >= self.pb_chars_available {
self.pb_chars_available - 1
} else {
num_filled
};
print!("\r"); self.current_line.clear();
self.current_line.push_str(self.current_line_prompt.as_str());
self.current_line.push(' ');
self.current_line.push_str(&"#".repeat(len_bar));
self.current_line.push('>');
if len_bar < self.pb_chars_available {
self.current_line.push_str(&".".repeat(self.pb_chars_available - len_bar));
}
self.current_line.push(' ');
self.print_and_clear(false);
io::stdout().flush().unwrap();
}
pub fn progressbar_end(&mut self) {
print!("\r"); self.current_line.clear();
self.current_line.push_str(self.current_line_prompt.as_str());
self.print_and_clear(false);
io::stdout().flush().unwrap();
}
fn print_and_clear(&mut self, finalize: bool) {
let mut line_break_occurred = false;
while !self.current_line.is_empty() {
if self.current_line.chars().count() <= LEN - 4 {
if finalize {
println!("| {} |", format_line(&self.current_line, LEN - 4));
self.current_line.clear();
self.current_line_prompt.clear();
} else {
print!("| {} |", format_line(&self.current_line, LEN - 4));
}
break;
} else {
let mut line = String::from(&self.current_line[..LEN-4]);
let original_line = self.current_line.clone();
if let Some(last_space) = line.rfind(' ') {
line.truncate(last_space);
self.current_line = format!("{}{}", " ".repeat(INNER_INDENT), &self.current_line[last_space..].trim_start());
} else {
line = String::from(&self.current_line[..LEN-4]);
self.current_line = format!("{}{}", " ".repeat(INNER_INDENT), &self.current_line[LEN-4..].trim_start());
}
if self.current_line == original_line {
self.current_line.clear(); }
println!("| {} |", format_line(&line, LEN - 4));
line_break_occurred = true; }
}
if line_break_occurred {
println!("| {} |", " ".repeat(LEN - 4)); }
}
}
fn format_line(s: &str, total_len: usize) -> String {
let text_len = s.chars().count();
format!("{}{}", s, " ".repeat(total_len - text_len))
}
pub fn print_border() {
println!("+{}+", "-".repeat(LEN - 2));
}