use crate::editor::Editor;
use console::{Style, Term};
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle};
use lazy_static::lazy_static;
use std::{borrow::Cow, fmt, fs::File, io, mem, time::Duration};
pub trait Out: Send + Sync + 'static {
fn write(&self, buf: &[u8]) -> io::Result<usize>;
fn write_fmt(&self, args: fmt::Arguments<'_>) {
struct AsWrite<'a, T: ?Sized>(&'a T);
impl<'a, T: ?Sized + Out> io::Write for AsWrite<'a, T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Out::write(self.0, buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
io::Write::write_fmt(&mut AsWrite(self), args).unwrap();
}
fn is_term(&self) -> bool {
false
}
fn clear_screen(&self) -> io::Result<()> {
Ok(())
}
fn read_line_with_prompt(&self, _prompt: &str) -> io::Result<String> {
Err(io::ErrorKind::Unsupported.into())
}
fn style(&self) -> Style {
Style::new().force_styling(false)
}
fn editor<'a>(&'a self, _name: &'a str) -> io::Result<Editor<'a>> {
Err(io::ErrorKind::Unsupported.into())
}
}
impl Out for Term {
fn write(&self, buf: &[u8]) -> io::Result<usize> {
io::Write::write(&mut &*self, buf)
}
fn is_term(&self) -> bool {
self.is_term()
}
fn clear_screen(&self) -> io::Result<()> {
self.clear_screen()
}
fn read_line_with_prompt(&self, prompt: &str) -> io::Result<String> {
self.write_str(prompt)?;
self.flush()?;
self.read_line()
}
fn style(&self) -> Style {
self.style()
}
fn editor(&self, name: &str) -> io::Result<Editor<'_>> {
Editor::new(name)
}
}
impl Out for File {
fn write(&self, buf: &[u8]) -> io::Result<usize> {
io::Write::write(&mut &*self, buf)
}
}
impl io::Write for &'_ dyn Out {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Out::write(*self, buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
lazy_static! {
pub static ref MULTIPROGRESS: MultiProgress =
MultiProgress::with_draw_target(ProgressDrawTarget::hidden());
}
pub fn indeterminate_spinner(
prefix: impl Into<Cow<'static, str>>,
message: impl Into<Cow<'static, str>>,
) -> ProgressBar {
let progress_bar = MULTIPROGRESS.add(
ProgressBar::new_spinner()
.with_style(
ProgressStyle::with_template("{prefix:>12.cyan.bold} {msg} {spinner}").unwrap(),
)
.with_prefix(prefix)
.with_message(message),
);
progress_bar.enable_steady_tick(Duration::from_millis(100));
progress_bar
}
pub fn progress_bar(
prefix: impl Into<Cow<'static, str>>,
message: impl Into<Cow<'static, str>>,
len: u64,
) -> ProgressBar {
let progress_bar = MULTIPROGRESS.add(
ProgressBar::new(len)
.with_style(
ProgressStyle::with_template("{prefix:>12.cyan.bold} {msg} [{bar:57}] {pos}/{len}")
.unwrap()
.progress_chars("=> "),
)
.with_prefix(prefix)
.with_message(message),
);
progress_bar.tick();
progress_bar
}
pub struct IncProgressOnDrop<'a>(pub &'a ProgressBar, pub u64);
impl Drop for IncProgressOnDrop<'_> {
fn drop(&mut self) {
self.0.inc(self.1);
}
}
pub struct StderrLogWriter {
buffer: Vec<u8>,
}
impl StderrLogWriter {
pub fn new() -> Self {
StderrLogWriter { buffer: Vec::new() }
}
}
impl io::Write for StderrLogWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
io::Write::write(&mut self.buffer, buf)
}
fn flush(&mut self) -> io::Result<()> {
let buffer = mem::take(&mut self.buffer);
MULTIPROGRESS.suspend(|| io::stderr().write_all(&buffer))
}
}
impl Drop for StderrLogWriter {
fn drop(&mut self) {
let _ = io::Write::flush(self);
}
}