use std::{
fmt,
io::{IsTerminal, Stdout, Write, stdout},
};
use anyhow::{Context, Result};
use serde::Serialize;
use crate::{clap::args::JsonFlag, output::Bytes};
pub trait PrintTable {
fn print(&self, writer: &mut dyn Write, table_max_width: Option<u16>) -> Result<()>;
}
pub trait Printer {
fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()>;
fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()>;
fn out_bytes(&mut self, bytes: Bytes) -> Result<()>;
fn is_json(&self) -> bool {
false
}
}
pub struct StdoutPrinter {
stdout: Stdout,
json: bool,
}
impl StdoutPrinter {
pub fn new(json: &JsonFlag) -> Self {
Self {
stdout: stdout(),
json: json.enabled,
}
}
fn write_json_line<T: Serialize>(&mut self, data: &T) -> Result<()> {
serde_json::to_writer(&mut self.stdout, data).context("Print JSON to stdout error")?;
writeln!(self.stdout)?;
self.stdout.flush()?;
Ok(())
}
}
impl Printer for StdoutPrinter {
fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()> {
if !self.json {
write!(self.stdout, "{data}")?;
return Ok(());
}
if self.stdout.is_terminal() {
serde_json::to_writer_pretty(&mut self.stdout, &data)
.context("Print pretty JSON to stdout error")?;
writeln!(self.stdout)?;
return Ok(());
}
self.write_json_line(&data)
}
fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()> {
if self.json {
return self.write_json_line(&event);
}
if self.stdout.is_terminal() {
write!(self.stdout, "{event}")?;
self.stdout.flush()?;
}
Ok(())
}
fn out_bytes(&mut self, bytes: Bytes) -> Result<()> {
if self.json {
return self.out(bytes);
}
self.stdout
.write_all(&bytes.data)
.context("Write bytes to stdout error")?;
self.stdout.flush().context("Flush stdout error")?;
Ok(())
}
fn is_json(&self) -> bool {
self.json
}
}
#[cfg(test)]
#[derive(Default)]
pub struct TestPrinter {
pub output: String,
pub events: Vec<String>,
}
#[cfg(test)]
impl Printer for TestPrinter {
fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()> {
self.output = data.to_string();
Ok(())
}
fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()> {
self.events.push(event.to_string());
Ok(())
}
fn out_bytes(&mut self, bytes: Bytes) -> Result<()> {
self.output = String::from_utf8_lossy(&bytes.data).into_owned();
Ok(())
}
}