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 fn render_pretty_colored(&self, color: ColorMode) -> String {
20 let _ = color;
21 self.render_pretty()
22 }
23}
24
25pub trait Pretty: Serialize {
29 const TEMPLATE: &'static str;
31}
32
33pub fn render_template(source: &str, value: &impl Serialize) -> Result<String, minijinja::Error> {
35 let mut env = minijinja::Environment::new();
36 env.add_template("pretty", source)?;
37 env.get_template("pretty")?.render(value)
38}
39
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct View {
43 pub format: OutputFormat,
45 pub color: ColorMode,
47 pub quiet: bool,
49}
50
51impl View {
52 #[must_use]
53 pub fn new(format: OutputFormat, color: ColorMode) -> Self {
55 Self {
56 format,
57 color,
58 quiet: false,
59 }
60 }
61
62 #[must_use]
63 pub fn quiet(mut self, quiet: bool) -> Self {
65 self.quiet = quiet;
66 self
67 }
68
69 pub fn show(self, value: &(impl Serialize + Render)) -> io::Result<()> {
71 self.emit(value, &value.render_pretty_colored(self.color))
72 }
73
74 pub fn show_pretty<T: Pretty>(self, value: &T) -> io::Result<()> {
76 self.show_template(value, T::TEMPLATE)
77 }
78
79 pub fn show_template(self, value: &impl Serialize, template: &str) -> io::Result<()> {
81 if self.quiet {
82 return Ok(());
83 }
84 if self.format.is_json() {
85 return emit_json(value);
86 }
87 let pretty = render_template(template, value).map_err(io::Error::other)?;
88 write_stdout(pretty.as_bytes(), self.color)
89 }
90
91 pub fn emit(self, value: &impl Serialize, pretty: &str) -> io::Result<()> {
93 if self.quiet {
94 return Ok(());
95 }
96 if self.format.is_json() {
97 return emit_json(value);
98 }
99 write_stdout(pretty.as_bytes(), self.color)
100 }
101
102 pub fn emit_ok<T: Serialize>(self, data: &T, pretty: &str) -> io::Result<()> {
104 self.emit(&Envelope::ok(data), pretty)
105 }
106
107 pub fn emit_err(self, bin: &str, message: &str) -> io::Result<()> {
109 let error = ErrorBody::new(bin, message);
110 if self.format.is_json() {
111 return emit_json(&Envelope::<()>::err(error));
112 }
113 write_stderr(formatdoc!("{bin}: {message}\n").as_bytes(), self.color)
114 }
115}
116
117pub fn emit_json<T: Serialize>(value: &T) -> io::Result<()> {
119 let stdout = io::stdout();
120 let mut lock = stdout.lock();
121 serde_json::to_writer(&mut lock, value)?;
122 lock.write_all(b"\n")?;
123 lock.flush()
124}
125
126pub fn write_stdout(bytes: &[u8], color: ColorMode) -> io::Result<()> {
128 let mut stream = AutoStream::new(io::stdout().lock(), color.choice());
129 stream.write_all(bytes)?;
130 stream.flush()
131}
132
133pub fn write_stderr(bytes: &[u8], color: ColorMode) -> io::Result<()> {
135 let mut stream = AutoStream::new(io::stderr().lock(), color.choice());
136 stream.write_all(bytes)?;
137 stream.flush()
138}