use std::cell::{Cell, RefCell};
#[derive(Clone, Copy, PartialEq)]
pub enum LinePrinterLineType {
Full,
Elide,
}
#[cfg(not(windows))]
struct LinePrinterOs {}
#[cfg(not(windows))]
impl LinePrinterOs {
fn new() -> Self {
LinePrinterOs {}
}
fn should_be_smart(&self) -> bool {
use libc;
use std::env;
use std::ffi;
if unsafe { libc::isatty(1usize as _) } != 0 {
return false;
}
if let Some(term) = env::var_os("TERM") {
if term != ffi::OsString::from("dumb") {
return true;
}
}
return false;
}
}
#[cfg(windows)]
struct LinePrinterOs {
console: ::winapi::HANDLE,
}
#[cfg(windows)]
impl LinePrinterOs {
fn new() -> Self {
use kernel32;
use winapi;
LinePrinterOs { console: unsafe { kernel32::GetStdHandle(winapi::STD_OUTPUT_HANDLE) } }
}
fn should_be_smart(&self) -> bool {
use kernel32;
use winapi;
use std::mem;
let mut csbi = unsafe { mem::zeroed::<winapi::CONSOLE_SCREEN_BUFFER_INFO>() };
unsafe {
kernel32::GetConsoleScreenBufferInfo(self.console, &mut csbi as *mut _) != winapi::FALSE
}
}
}
pub struct LinePrinter {
smart_terminal: bool,
have_blank_line: Cell<bool>,
console_locked: bool,
line_buffer: RefCell<Vec<u8>>,
line_type: Cell<LinePrinterLineType>,
output_buffer: RefCell<Vec<u8>>,
extra: LinePrinterOs,
}
impl LinePrinter {
pub fn new() -> Self {
let lp_os = LinePrinterOs::new();
let smart = lp_os.should_be_smart();
LinePrinter {
smart_terminal: smart,
have_blank_line: Cell::new(true),
console_locked: false,
line_buffer: RefCell::new(Vec::new()),
line_type: Cell::new(LinePrinterLineType::Full),
output_buffer: RefCell::new(Vec::new()),
extra: lp_os,
}
}
pub fn is_smart_terminal(&self) -> bool {
self.smart_terminal
}
pub fn set_smart_terminal(&mut self, smart: bool) {
self.smart_terminal = smart;
}
pub fn print(&self, to_print: &[u8], line_type: LinePrinterLineType) {
if self.console_locked {
*self.line_buffer.borrow_mut() = to_print.to_owned();
self.line_type.set(line_type);
return;
}
if self.smart_terminal {
print!("\r"); }
if self.smart_terminal && line_type == LinePrinterLineType::Elide {
self.have_blank_line.set(false);
return;
unimplemented!();
} else {
use std::io::{self, Write};
let stdout = io::stdout();
let mut handle = stdout.lock();
let _ = handle.write(to_print);
let _ = handle.write(b"\n");
}
}
fn print_or_buffer(&self, to_print: &[u8]) {
if self.console_locked {
self.output_buffer.borrow_mut().extend_from_slice(to_print);
} else {
use std::io::{self, Write};
let stdout = io::stdout();
let mut handle = stdout.lock();
let _ = handle.write(to_print);
}
}
pub fn print_on_new_line(&self, to_print: &[u8]) {
if self.console_locked && !self.line_buffer.borrow().is_empty() {
let mut line_buffer = self.line_buffer.borrow_mut();
let mut output_buffer = self.output_buffer.borrow_mut();
output_buffer.extend(line_buffer.drain(..));
output_buffer.push(b'\n');
}
if !self.have_blank_line.get() {
self.print_or_buffer(b"\n");
}
if !to_print.is_empty() {
self.print_or_buffer(to_print);
}
self.have_blank_line.set(match to_print.last() {
None | Some(&b'\n') => true,
_ => false,
});
}
pub fn set_console_locked(&mut self, locked: bool) {
use std::mem;
if self.console_locked == locked {
return;
}
if locked {
self.print_on_new_line(b"");
}
self.console_locked = locked;
if !locked {
let mut output_buffer = Vec::new();
let mut line_buffer = Vec::new();
mem::swap(&mut output_buffer, &mut *self.output_buffer.borrow_mut());
mem::swap(&mut line_buffer, &mut *self.line_buffer.borrow_mut());
self.print_on_new_line(&output_buffer);
if !line_buffer.is_empty() {
self.print(&line_buffer, self.line_type.get());
}
}
return;
}
}