pub trait Printable: std::io::Write {
fn is_terminal(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct StdoutPrinter {
buffer: String,
is_terminal: bool,
}
impl StdoutPrinter {
#[must_use]
pub fn new() -> Self {
Self {
buffer: String::new(),
is_terminal: std::io::stdout().is_terminal(),
}
}
#[must_use]
pub fn write_text(self, text: &str) -> Self {
Self {
buffer: format!("{}{}", self.buffer, text),
is_terminal: self.is_terminal,
}
}
#[must_use]
pub fn write_line(self, line: &str) -> Self {
self.write_text(&format!("{}\n", line))
}
#[must_use]
pub fn emit(self) -> (Self, String) {
let output = self.buffer.clone();
(
Self {
buffer: String::new(),
is_terminal: self.is_terminal,
},
output,
)
}
#[must_use]
pub fn get_buffer(&self) -> &str {
&self.buffer
}
pub fn flush(self) -> Self {
self
}
}
impl Default for StdoutPrinter {
fn default() -> Self {
Self::new()
}
}
impl Printable for StdoutPrinter {
fn is_terminal(&self) -> bool {
self.is_terminal
}
}
impl std::io::Write for StdoutPrinter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let s =
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.buffer.push_str(s);
std::io::stdout().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
std::io::stdout().flush()
}
}
#[derive(Debug, Clone)]
#[cfg(any(test, feature = "test-utils"))]
pub struct StderrPrinter {
buffer: String,
is_terminal: bool,
}
#[cfg(any(test, feature = "test-utils"))]
impl StderrPrinter {
#[must_use]
pub fn new() -> Self {
Self {
buffer: String::new(),
is_terminal: std::io::stderr().is_terminal(),
}
}
#[must_use]
pub fn write_text(self, text: &str) -> Self {
Self {
buffer: format!("{}{}", self.buffer, text),
is_terminal: self.is_terminal,
}
}
#[must_use]
pub fn write_line(self, line: &str) -> Self {
self.write_text(&format!("{}\n", line))
}
#[must_use]
pub fn emit(self) -> (Self, String) {
let output = self.buffer.clone();
(
Self {
buffer: String::new(),
is_terminal: self.is_terminal,
},
output,
)
}
#[must_use]
pub fn get_buffer(&self) -> &str {
&self.buffer
}
pub fn flush(self) -> Self {
self
}
}
#[cfg(any(test, feature = "test-utils"))]
impl Default for StderrPrinter {
fn default() -> Self {
Self::new()
}
}
#[cfg(any(test, feature = "test-utils"))]
impl std::io::Write for StderrPrinter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let s =
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.buffer.push_str(s);
std::io::stderr().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
std::io::stderr().flush()
}
}
#[cfg(any(test, feature = "test-utils"))]
impl Printable for StderrPrinter {
fn is_terminal(&self) -> bool {
self.is_terminal
}
}
pub type SharedPrinter = Rc<RefCell<dyn Printable>>;
#[must_use]
pub fn shared_stdout() -> SharedPrinter {
Rc::new(RefCell::new(StdoutPrinter::new()))
}