1use std::io::{self, Write};
4
5use anstream::AutoStream;
6use serde::Serialize;
7
8use crate::color::ColorMode;
9use crate::format::OutputFormat;
10use crate::formatdoc;
11use crate::model::{Envelope, ErrorBody};
12
13pub trait Render {
15 fn render_pretty(&self) -> String;
17}
18
19pub trait Pretty: Serialize {
23 const TEMPLATE: &'static str;
25}
26
27pub fn render_template(source: &str, value: &impl Serialize) -> Result<String, minijinja::Error> {
29 let mut env = minijinja::Environment::new();
30 env.add_template("pretty", source)?;
31 env.get_template("pretty")?.render(value)
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub struct View {
37 pub format: OutputFormat,
39 pub color: ColorMode,
41 pub quiet: bool,
43}
44
45impl View {
46 #[must_use]
47 pub fn new(format: OutputFormat, color: ColorMode) -> Self {
49 Self {
50 format,
51 color,
52 quiet: false,
53 }
54 }
55
56 #[must_use]
57 pub fn quiet(mut self, quiet: bool) -> Self {
59 self.quiet = quiet;
60 self
61 }
62
63 pub fn show(self, value: &(impl Serialize + Render)) -> io::Result<()> {
65 self.emit(value, &value.render_pretty())
66 }
67
68 pub fn show_pretty<T: Pretty>(self, value: &T) -> io::Result<()> {
70 self.show_template(value, T::TEMPLATE)
71 }
72
73 pub fn show_template(self, value: &impl Serialize, template: &str) -> io::Result<()> {
75 if self.quiet {
76 return Ok(());
77 }
78 if self.format.is_json() {
79 return emit_json(value);
80 }
81 let pretty = render_template(template, value).map_err(io::Error::other)?;
82 write_stdout(pretty.as_bytes(), self.color)
83 }
84
85 pub fn emit(self, value: &impl Serialize, pretty: &str) -> io::Result<()> {
87 if self.quiet {
88 return Ok(());
89 }
90 if self.format.is_json() {
91 return emit_json(value);
92 }
93 write_stdout(pretty.as_bytes(), self.color)
94 }
95
96 pub fn emit_ok<T: Serialize>(self, data: &T, pretty: &str) -> io::Result<()> {
98 self.emit(&Envelope::ok(data), pretty)
99 }
100
101 pub fn emit_err(self, bin: &str, message: &str) -> io::Result<()> {
103 let error = ErrorBody::new(bin, message);
104 if self.format.is_json() {
105 return emit_json(&Envelope::<()>::err(error));
106 }
107 write_stderr(formatdoc!("{bin}: {message}\n").as_bytes(), self.color)
108 }
109}
110
111pub fn emit_json<T: Serialize>(value: &T) -> io::Result<()> {
113 let stdout = io::stdout();
114 let mut lock = stdout.lock();
115 serde_json::to_writer(&mut lock, value)?;
116 lock.write_all(b"\n")?;
117 lock.flush()
118}
119
120pub fn write_stdout(bytes: &[u8], color: ColorMode) -> io::Result<()> {
122 let mut stream = AutoStream::new(io::stdout().lock(), color.choice());
123 stream.write_all(bytes)?;
124 stream.flush()
125}
126
127pub fn write_stderr(bytes: &[u8], color: ColorMode) -> io::Result<()> {
129 let mut stream = AutoStream::new(io::stderr().lock(), color.choice());
130 stream.write_all(bytes)?;
131 stream.flush()
132}