1use crate::{Format, Result, TestBuffers, ToolIO, Writer};
6use serde::Serialize;
7use std::fmt::Display;
8use std::io::Write;
9
10pub struct JsonWriter<T> {
12 format: Option<Format>,
13 simple_writer: Writer,
14 _p: std::marker::PhantomData<fn(T)>,
15}
16
17impl<T> JsonWriter<T> {
18 pub fn new_buffers<'a, O, E>(format: Option<Format>, stdout: O, stderr: E) -> Self
21 where
22 O: Write + Send + Sync + 'static,
23 E: Write + Send + Sync + 'static,
24 {
25 let simple_writer = Writer::new_buffers(stdout, stderr);
26 let _p = Default::default();
27 Self { format, simple_writer, _p }
28 }
29
30 pub fn new(format: Option<Format>) -> Self {
34 let simple_writer = Writer::new();
35 let _p = Default::default();
36 Self { format, simple_writer, _p }
37 }
38
39 pub fn new_test(format: Option<Format>, test_buffers: &TestBuffers) -> Self {
42 Self::new_buffers(format, test_buffers.stdout.clone(), test_buffers.stderr.clone())
43 }
44
45 pub fn simple_writer(self) -> Writer {
48 self.simple_writer
49 }
50
51 pub fn stderr(&mut self) -> &mut (dyn Write + Send + Sync) {
52 self.simple_writer.stderr()
53 }
54
55 fn get_machine_format(&self) -> Option<Format> {
57 match self.format {
58 None | Some(Format::Raw) => None,
59 j => j,
60 }
61 }
62}
63
64impl<T> JsonWriter<T>
65where
66 T: Serialize,
67{
68 pub fn machine_many<I: IntoIterator<Item = T>>(&mut self, output: I) -> Result<()> {
72 if self.is_machine() {
73 for output in output {
74 self.machine(&output)?;
75 }
76 }
77 Ok(())
78 }
79
80 pub fn machine(&mut self, output: &T) -> Result<()> {
84 if let Some(format) = self.get_machine_format() {
85 format_output(format, &mut self.simple_writer, output)
86 } else {
87 Ok(())
88 }
89 }
90
91 pub fn machine_or<D: Display>(&mut self, value: &T, or: D) -> Result<()> {
94 if let Some(format) = self.get_machine_format() {
95 format_output(format, &mut self.simple_writer, value)?
96 } else {
97 writeln!(self, "{or}")?
98 }
99 Ok(())
100 }
101
102 pub fn machine_or_write<D: Display>(&mut self, value: &T, or: D) -> Result<()> {
105 if let Some(format) = self.get_machine_format() {
106 format_output(format, &mut self.simple_writer, value)?
107 } else {
108 write!(self, "{or}")?
109 }
110 Ok(())
111 }
112
113 pub fn machine_or_else<F, R>(&mut self, value: &T, f: F) -> Result<()>
117 where
118 F: FnOnce() -> R,
119 R: Display,
120 {
121 match self.format {
122 Some(format) => format_output(format, &mut self.simple_writer, value)?,
123 None => writeln!(self, "{}", f())?,
124 }
125 Ok(())
126 }
127
128 pub fn format(&self) -> Option<Format> {
129 self.format
130 }
131
132 pub fn formatted<J: Serialize>(&mut self, output: &J) -> Result<()> {
133 if let Some(format) = self.get_machine_format() {
134 format_output(format, &mut self.simple_writer, output)
135 } else {
136 Ok(())
137 }
138 }
139}
140
141pub fn format_output<W: Write, T>(format: Format, mut out: W, output: &T) -> Result<()>
142where
143 T: Serialize,
144{
145 match format {
146 Format::Json => {
147 let mut buf = serde_json::to_vec(output)?;
148 buf.push(b'\n');
149 out.write_all(&buf)?;
150 out.flush().map_err(Into::into)
151 }
152 Format::JsonPretty => {
153 let buf = serde_json::to_vec_pretty(output)?;
154 out.write_all(&buf)?;
155 out.flush().map_err(Into::into)
156 }
157 Format::Raw => Ok(()),
158 }
159}
160
161impl<T> ToolIO for JsonWriter<T>
162where
163 T: Serialize,
164{
165 type OutputItem = T;
166
167 fn supports_structured_output() -> bool {
168 true
169 }
170
171 fn is_machine(&self) -> bool {
172 match self.format {
173 Some(Format::Json) | Some(Format::JsonPretty) => true,
174 _ => false,
175 }
176 }
177
178 fn stderr(&mut self) -> &mut (dyn Write + Send + Sync) {
179 self.stderr()
180 }
181
182 fn item(&mut self, value: &T) -> Result<()>
183 where
184 T: Display,
185 {
186 if self.is_machine() { self.machine(value) } else { self.line(value) }
187 }
188}
189
190impl<T> Write for JsonWriter<T>
191where
192 T: Serialize,
193{
194 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
195 if self.is_machine() { Ok(buf.len()) } else { self.simple_writer.write(buf) }
196 }
197
198 fn flush(&mut self) -> std::io::Result<()> {
199 if self.is_machine() { Ok(()) } else { self.simple_writer.flush() }
200 }
201}
202#[cfg(test)]
203mod test {
204 use super::*;
205
206 #[test]
207 fn test_not_machine_is_ok() {
208 let test_buffers = TestBuffers::default();
209 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
210 let res = writer.machine(&"ehllo");
211 assert!(res.is_ok());
212 }
213
214 #[test]
215 fn test_machine_valid_json_is_ok() {
216 let test_buffers = TestBuffers::default();
217 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
218 let res = writer.machine(&"ehllo");
219 assert!(res.is_ok());
220 }
221
222 #[test]
223 fn writer_implements_write() {
224 let test_buffers = TestBuffers::default();
225 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
226 writer.write_all(b"foobar").unwrap();
227
228 let (stdout, stderr) = test_buffers.into_strings();
229 assert_eq!(stdout, "foobar");
230 assert_eq!(stderr, "");
231 }
232
233 #[test]
234 fn writer_implements_write_ignored_on_machine() {
235 let test_buffers = TestBuffers::default();
236 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
237 writer.write_all(b"foobar").unwrap();
238
239 let (stdout, stderr) = test_buffers.into_strings();
240 assert_eq!(stdout, "");
241 assert_eq!(stderr, "");
242 }
243
244 #[test]
245 fn test_item_for_test_as_machine() {
246 let test_buffers = TestBuffers::default();
247 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
248 writer.item(&"hello").unwrap();
249 writer.machine_or(&"hello again", "but what if").unwrap();
250 writer.machine_or_else(&"hello forever", || "but what if else").unwrap();
251
252 assert_eq!(
253 test_buffers.into_stdout_str(),
254 "\"hello\"\n\"hello again\"\n\"hello forever\"\n"
255 );
256 }
257
258 #[test]
259 fn test_item_for_test_as_not_machine() {
260 let test_buffers = TestBuffers::default();
261 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
262 writer.item(&"hello").unwrap();
263 writer.machine_or(&"hello again", "but what if").unwrap();
264 writer.machine_or_else(&"hello forever", || "but what if else").unwrap();
265
266 assert_eq!(test_buffers.into_stdout_str(), "hello\nbut what if\nbut what if else\n");
267 }
268
269 #[test]
270 fn test_machine_for_test() {
271 let test_buffers = TestBuffers::default();
272 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
273 writer.machine(&"hello").unwrap();
274
275 assert_eq!(test_buffers.into_stdout_str(), "\"hello\"\n");
276 }
277
278 #[test]
279 fn test_not_machine_for_test_is_empty() {
280 let test_buffers = TestBuffers::default();
281 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
282 writer.machine(&"hello").unwrap();
283
284 assert!(test_buffers.into_stdout_str().is_empty());
285 }
286
287 #[test]
288 fn test_machine_makes_is_machine_true() {
289 let test_buffers = TestBuffers::default();
290 let writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
291 assert!(writer.is_machine());
292 }
293
294 #[test]
295 fn test_not_machine_makes_is_machine_false() {
296 let test_buffers = TestBuffers::default();
297 let writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
298 assert!(!writer.is_machine());
299 }
300
301 #[test]
302 fn line_writer_for_machine_is_ok() {
303 let test_buffers = TestBuffers::default();
304 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
305 writer.line("hello").unwrap();
306
307 let (stdout, stderr) = test_buffers.into_strings();
308 assert_eq!(stdout, "");
309 assert_eq!(stderr, "");
310 }
311
312 #[test]
313 fn writer_write_for_machine_is_ok() {
314 let test_buffers = TestBuffers::default();
315 let mut writer: JsonWriter<&str> = JsonWriter::new_test(Some(Format::Json), &test_buffers);
316 writer.print("foobar").unwrap();
317
318 let (stdout, stderr) = test_buffers.into_strings();
319 assert_eq!(stdout, "");
320 assert_eq!(stderr, "");
321 }
322
323 #[test]
324 fn writer_print_output_has_no_newline() {
325 let test_buffers = TestBuffers::default();
326 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
327 writer.print("foobar").unwrap();
328
329 let (stdout, stderr) = test_buffers.into_strings();
330 assert_eq!(stdout, "foobar");
331 assert_eq!(stderr, "");
332 }
333
334 #[test]
335 fn writing_errors_goes_to_the_right_stream() {
336 let test_buffers = TestBuffers::default();
337 let mut writer: JsonWriter<&str> = JsonWriter::new_test(None, &test_buffers);
338 writeln!(writer.stderr(), "hello").unwrap();
339
340 let (stdout, stderr) = test_buffers.into_strings();
341 assert_eq!(stdout, "");
342 assert_eq!(stderr, "hello\n");
343 }
344
345 #[test]
346 fn test_machine_writes_pretty_json() {
347 let test_buffers = TestBuffers::default();
348 let mut writer: JsonWriter<serde_json::Value> =
349 JsonWriter::new_test(Some(Format::JsonPretty), &test_buffers);
350 let test_input = serde_json::json!({
351 "object1": {
352 "line1": "hello",
353 "line2": "foobar"
354 }
355 });
356 writer.machine(&test_input).unwrap();
357
358 assert_eq!(
359 test_buffers.into_stdout_str(),
360 r#"{
361 "object1": {
362 "line1": "hello",
363 "line2": "foobar"
364 }
365}"#
366 );
367 }
368
369 #[test]
370 fn test_concurrent_format_output_does_not_interleave_json_lines() {
371 use std::sync::{Arc, Barrier, Mutex};
372
373 #[derive(Clone)]
374 struct YieldingBuffer(Arc<Mutex<Vec<u8>>>);
375
376 impl Write for YieldingBuffer {
377 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
378 self.0.lock().unwrap().extend_from_slice(buf);
379 std::thread::yield_now();
380 Ok(buf.len())
381 }
382
383 fn flush(&mut self) -> std::io::Result<()> {
384 Ok(())
385 }
386 }
387
388 let shared = YieldingBuffer(Arc::new(Mutex::new(Vec::new())));
389 let num_threads = 8;
390 let items_per_thread = 50;
391 let barrier = Arc::new(Barrier::new(num_threads));
392 let mut handles = Vec::with_capacity(num_threads);
393
394 for tid in 0..num_threads {
395 let mut out = shared.clone();
396 let barrier = Arc::clone(&barrier);
397 handles.push(std::thread::spawn(move || {
398 barrier.wait();
399 for seq in 0..items_per_thread {
400 let payload = serde_json::json!({
401 "thread": tid,
402 "seq": seq,
403 "nested": {
404 "alpha": "value_a",
405 "beta": "value_b",
406 "gamma": [1, 2, 3, 4, 5],
407 }
408 });
409 format_output(Format::Json, &mut out, &payload).unwrap();
410 }
411 }));
412 }
413
414 for h in handles {
415 h.join().unwrap();
416 }
417
418 let raw = String::from_utf8(shared.0.lock().unwrap().clone()).unwrap();
419 let lines: Vec<&str> = raw.lines().collect();
420 assert_eq!(lines.len(), num_threads * items_per_thread);
421 for line in lines {
422 let parsed: serde_json::Value =
423 serde_json::from_str(line).expect("each line must be a complete valid JSON object");
424 assert!(parsed.get("thread").is_some());
425 assert!(parsed.get("seq").is_some());
426 assert_eq!(parsed["nested"]["alpha"], "value_a");
427 }
428 }
429}