use std::io::{self, Write};
use anstream::AutoStream;
use serde::Serialize;
use crate::color::ColorMode;
use crate::format::OutputFormat;
use crate::formatdoc;
use crate::model::{Envelope, ErrorBody};
pub trait Render {
fn render_pretty(&self) -> String;
}
pub trait Pretty: Serialize {
const TEMPLATE: &'static str;
}
pub fn render_template(source: &str, value: &impl Serialize) -> Result<String, minijinja::Error> {
let mut env = minijinja::Environment::new();
env.add_template("pretty", source)?;
env.get_template("pretty")?.render(value)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct View {
pub format: OutputFormat,
pub color: ColorMode,
pub quiet: bool,
}
impl View {
#[must_use]
pub fn new(format: OutputFormat, color: ColorMode) -> Self {
Self {
format,
color,
quiet: false,
}
}
#[must_use]
pub fn quiet(mut self, quiet: bool) -> Self {
self.quiet = quiet;
self
}
pub fn show(self, value: &(impl Serialize + Render)) -> io::Result<()> {
self.emit(value, &value.render_pretty())
}
pub fn show_pretty<T: Pretty>(self, value: &T) -> io::Result<()> {
self.show_template(value, T::TEMPLATE)
}
pub fn show_template(self, value: &impl Serialize, template: &str) -> io::Result<()> {
if self.quiet {
return Ok(());
}
if self.format.is_json() {
return emit_json(value);
}
let pretty = render_template(template, value).map_err(io::Error::other)?;
write_stdout(pretty.as_bytes(), self.color)
}
pub fn emit(self, value: &impl Serialize, pretty: &str) -> io::Result<()> {
if self.quiet {
return Ok(());
}
if self.format.is_json() {
return emit_json(value);
}
write_stdout(pretty.as_bytes(), self.color)
}
pub fn emit_ok<T: Serialize>(self, data: &T, pretty: &str) -> io::Result<()> {
self.emit(&Envelope::ok(data), pretty)
}
pub fn emit_err(self, bin: &str, message: &str) -> io::Result<()> {
let error = ErrorBody::new(bin, message);
if self.format.is_json() {
return emit_json(&Envelope::<()>::err(error));
}
write_stderr(formatdoc!("{bin}: {message}\n").as_bytes(), self.color)
}
}
pub fn emit_json<T: Serialize>(value: &T) -> io::Result<()> {
let stdout = io::stdout();
let mut lock = stdout.lock();
serde_json::to_writer(&mut lock, value)?;
lock.write_all(b"\n")?;
lock.flush()
}
pub fn write_stdout(bytes: &[u8], color: ColorMode) -> io::Result<()> {
let mut stream = AutoStream::new(io::stdout().lock(), color.choice());
stream.write_all(bytes)?;
stream.flush()
}
pub fn write_stderr(bytes: &[u8], color: ColorMode) -> io::Result<()> {
let mut stream = AutoStream::new(io::stderr().lock(), color.choice());
stream.write_all(bytes)?;
stream.flush()
}