1use std::{
18 fmt,
19 io::{IsTerminal, Stdout, Write, stdout},
20};
21
22use anyhow::{Context, Result, bail};
23use serde::Serialize;
24use serde_json::Value;
25
26use crate::{clap::args::JsonFlag, output::Bytes};
27
28pub trait Printer {
30 fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()>;
32
33 fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()>;
39
40 fn out_bytes(&mut self, bytes: Bytes) -> Result<()>;
43
44 fn is_json(&self) -> bool {
46 false
47 }
48}
49
50pub struct StdoutPrinter {
52 stdout: Stdout,
53 json: bool,
54 streaming: bool,
55}
56
57impl StdoutPrinter {
58 pub fn new(json: &JsonFlag) -> Self {
60 Self {
61 stdout: stdout(),
62 json: json.enabled,
63 streaming: false,
64 }
65 }
66
67 pub fn streaming(mut self) -> Self {
71 self.streaming = true;
72 self
73 }
74
75 fn write_json_line<T: Serialize>(&mut self, data: &T) -> Result<()> {
76 serde_json::to_writer(&mut self.stdout, data).context("Print JSON to stdout error")?;
77 writeln!(self.stdout)?;
78 self.stdout.flush()?;
79 Ok(())
80 }
81}
82
83impl Printer for StdoutPrinter {
84 fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()> {
85 if !self.json {
86 write!(self.stdout, "{data}")?;
87 return Ok(());
88 }
89
90 if self.stdout.is_terminal() {
91 serde_json::to_writer_pretty(&mut self.stdout, &data)
92 .context("Print pretty JSON to stdout error")?;
93 writeln!(self.stdout)?;
94 return Ok(());
95 }
96
97 self.write_json_line(&data)
101 }
102
103 fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()> {
104 if self.json {
107 return self.write_json_line(&event);
108 }
109
110 if self.streaming || self.stdout.is_terminal() {
113 write!(self.stdout, "{event}")?;
114 self.stdout.flush()?;
115 }
116
117 Ok(())
118 }
119
120 fn out_bytes(&mut self, bytes: Bytes) -> Result<()> {
121 if self.json {
122 return self.out(bytes);
123 }
124
125 self.stdout
126 .write_all(&bytes.data)
127 .context("Write bytes to stdout error")?;
128 self.stdout.flush().context("Flush stdout error")?;
129
130 Ok(())
131 }
132
133 fn is_json(&self) -> bool {
134 self.json
135 }
136}
137
138#[derive(Debug, Default)]
141pub struct CapturePrinter {
142 pub values: Vec<Value>,
144 pub events: Vec<Value>,
146}
147
148impl CapturePrinter {
149 pub fn into_single(mut self) -> Result<Value> {
152 if self.values.len() != 1 {
153 let n = self.values.len();
154 bail!("The command printed {n} values, one expected");
155 }
156
157 Ok(self.values.remove(0))
158 }
159}
160
161impl Printer for CapturePrinter {
162 fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()> {
163 self.values.push(serde_json::to_value(&data)?);
164 Ok(())
165 }
166
167 fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()> {
168 self.events.push(serde_json::to_value(&event)?);
169 Ok(())
170 }
171
172 fn out_bytes(&mut self, bytes: Bytes) -> Result<()> {
173 self.out(bytes)
174 }
175
176 fn is_json(&self) -> bool {
177 true
178 }
179}
180
181#[cfg(test)]
183#[derive(Default)]
184pub struct TestPrinter {
185 pub output: String,
187 pub events: Vec<String>,
189}
190
191#[cfg(test)]
192impl Printer for TestPrinter {
193 fn out<T: fmt::Display + Serialize>(&mut self, data: T) -> Result<()> {
194 self.output = data.to_string();
195 Ok(())
196 }
197
198 fn event<T: fmt::Display + Serialize>(&mut self, event: T) -> Result<()> {
199 self.events.push(event.to_string());
200 Ok(())
201 }
202
203 fn out_bytes(&mut self, bytes: Bytes) -> Result<()> {
204 self.output = String::from_utf8_lossy(&bytes.data).into_owned();
205 Ok(())
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use serde_json::json;
212
213 use crate::printer::{CapturePrinter, Printer};
214
215 #[test]
216 fn a_single_value_is_taken_and_events_kept_apart() {
217 let mut printer = CapturePrinter::default();
218 printer.event(json!({ "event": "a" })).unwrap();
219 printer.out(json!({ "id": 1 })).unwrap();
220
221 assert_eq!(printer.events, [json!({ "event": "a" })]);
222 assert_eq!(printer.into_single().unwrap(), json!({ "id": 1 }));
223 }
224
225 #[test]
226 fn the_count_of_values_is_the_one_printed() {
227 let none = CapturePrinter::default().into_single().unwrap_err();
228 assert_eq!(
229 none.to_string(),
230 "The command printed 0 values, one expected"
231 );
232
233 let mut printer = CapturePrinter::default();
234 printer.out(json!(1)).unwrap();
235 printer.out(json!(2)).unwrap();
236
237 let two = printer.into_single().unwrap_err();
238 assert_eq!(
239 two.to_string(),
240 "The command printed 2 values, one expected"
241 );
242 }
243}