1#![deny(missing_debug_implementations)]
2#![deny(missing_docs)]
3#![doc(html_root_url = "https://docs.rs/termcolor-json/1.0.0")]
4
5use std::{
27 cell::RefCell,
28 io::{self, Write},
29 mem,
30};
31
32use serde::Serialize;
33use serde_json::ser::{CharEscape, CompactFormatter, Formatter, PrettyFormatter, Serializer};
34use termcolor::{Color, ColorSpec, WriteColor};
35
36#[derive(Clone, Debug)]
40pub struct Theme {
41 reset: ColorSpec,
42 null: ColorSpec,
43 bool: ColorSpec,
44 number: ColorSpec,
45 string: ColorSpec,
46 object_key: ColorSpec,
47}
48
49pub fn to_writer<W, T>(writer: W, value: &T) -> serde_json::Result<()>
51where
52 W: WriteColor,
53 T: ?Sized + Serialize,
54{
55 to_writer_with_theme_and_formatter(writer, value, &Theme::default(), PrettyFormatter::new())
56}
57
58pub fn to_writer_compact<W, T>(writer: W, value: &T) -> serde_json::Result<()>
60where
61 W: WriteColor,
62 T: ?Sized + Serialize,
63{
64 to_writer_with_theme_and_formatter(writer, value, &Theme::default(), CompactFormatter)
65}
66
67pub fn to_writer_with_theme<W, T>(writer: W, value: &T, theme: &Theme) -> serde_json::Result<()>
69where
70 W: WriteColor,
71 T: ?Sized + Serialize,
72{
73 to_writer_with_theme_and_formatter(writer, value, theme, PrettyFormatter::new())
74}
75
76pub fn to_writer_with_theme_and_formatter<W, T, F>(
81 writer: W,
82 value: &T,
83 theme: &Theme,
84 formatter: F,
85) -> serde_json::Result<()>
86where
87 W: WriteColor,
88 T: ?Sized + Serialize,
89 F: Formatter,
90{
91 if !writer.supports_color() {
92 let mut ser = Serializer::with_formatter(writer, formatter);
93 value.serialize(&mut ser)
94 } else {
95 let writer = SharedWriter::new(writer);
96 let formatter = ColorFormatter::new(&writer, theme, formatter);
97 let mut ser = Serializer::with_formatter(&writer, formatter);
98 value.serialize(&mut ser)
99 }
100}
101
102struct ColorFormatter<'a, W, F> {
103 formatter: F,
104 writer: W,
105 theme: &'a Theme,
106 writing_key: bool,
107 need_reset: bool,
108}
109
110impl<'a, W, F> ColorFormatter<'a, W, F> {
111 fn new(writer: W, theme: &'a Theme, formatter: F) -> Self {
112 ColorFormatter {
113 formatter,
114 writer,
115 theme,
116 writing_key: false,
117 need_reset: false,
118 }
119 }
120}
121
122impl<'a, W, F> Formatter for ColorFormatter<'a, W, F>
123where
124 W: WriteColor,
125 F: Formatter,
126{
127 fn write_null<U>(&mut self, _: &mut U) -> io::Result<()>
128 where
129 U: ?Sized + Write,
130 {
131 let f = &mut self.formatter;
132 with_color(&mut self.writer, &self.theme.null, self.theme, |w| {
133 f.write_null(w)
134 })
135 }
136
137 fn write_bool<U>(&mut self, _: &mut U, value: bool) -> io::Result<()>
138 where
139 U: ?Sized + Write,
140 {
141 let f = &mut self.formatter;
142 with_color(&mut self.writer, &self.theme.bool, self.theme, |w| {
143 f.write_bool(w, value)
144 })
145 }
146
147 fn write_i8<U>(&mut self, _: &mut U, value: i8) -> io::Result<()>
148 where
149 U: ?Sized + Write,
150 {
151 let f = &mut self.formatter;
152 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
153 f.write_i8(w, value)
154 })
155 }
156
157 fn write_i16<U>(&mut self, _: &mut U, value: i16) -> io::Result<()>
158 where
159 U: ?Sized + Write,
160 {
161 let f = &mut self.formatter;
162 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
163 f.write_i16(w, value)
164 })
165 }
166
167 fn write_i32<U>(&mut self, _: &mut U, value: i32) -> io::Result<()>
168 where
169 U: ?Sized + Write,
170 {
171 let f = &mut self.formatter;
172 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
173 f.write_i32(w, value)
174 })
175 }
176
177 fn write_i64<U>(&mut self, _: &mut U, value: i64) -> io::Result<()>
178 where
179 U: ?Sized + Write,
180 {
181 let f = &mut self.formatter;
182 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
183 f.write_i64(w, value)
184 })
185 }
186
187 fn write_u8<U>(&mut self, _: &mut U, value: u8) -> io::Result<()>
188 where
189 U: ?Sized + Write,
190 {
191 let f = &mut self.formatter;
192 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
193 f.write_u8(w, value)
194 })
195 }
196
197 fn write_u16<U>(&mut self, _: &mut U, value: u16) -> io::Result<()>
198 where
199 U: ?Sized + Write,
200 {
201 let f = &mut self.formatter;
202 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
203 f.write_u16(w, value)
204 })
205 }
206
207 fn write_u32<U>(&mut self, _: &mut U, value: u32) -> io::Result<()>
208 where
209 U: ?Sized + Write,
210 {
211 let f = &mut self.formatter;
212 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
213 f.write_u32(w, value)
214 })
215 }
216
217 fn write_u64<U>(&mut self, _: &mut U, value: u64) -> io::Result<()>
218 where
219 U: ?Sized + Write,
220 {
221 let f = &mut self.formatter;
222 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
223 f.write_u64(w, value)
224 })
225 }
226
227 fn write_f32<U>(&mut self, _: &mut U, value: f32) -> io::Result<()>
228 where
229 U: ?Sized + Write,
230 {
231 let f = &mut self.formatter;
232 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
233 f.write_f32(w, value)
234 })
235 }
236
237 fn write_f64<U>(&mut self, _: &mut U, value: f64) -> io::Result<()>
238 where
239 U: ?Sized + Write,
240 {
241 let f = &mut self.formatter;
242 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
243 f.write_f64(w, value)
244 })
245 }
246
247 fn write_number_str<U>(&mut self, _: &mut U, value: &str) -> io::Result<()>
248 where
249 U: ?Sized + Write,
250 {
251 let f = &mut self.formatter;
252 with_color(&mut self.writer, &self.theme.number, self.theme, |w| {
253 f.write_number_str(w, value)
254 })
255 }
256
257 fn begin_string<U>(&mut self, _: &mut U) -> io::Result<()>
258 where
259 U: ?Sized + Write,
260 {
261 if !self.writing_key {
262 self.need_reset = set_color(&mut self.writer, &self.theme.string)?;
263 }
264 self.formatter.begin_string(&mut self.writer)
265 }
266
267 fn end_string<U>(&mut self, _: &mut U) -> io::Result<()>
268 where
269 U: ?Sized + Write,
270 {
271 self.formatter.end_string(&mut self.writer)?;
272 if !self.writing_key && mem::take(&mut self.need_reset) {
273 reset(&mut self.writer, self.theme)?;
274 }
275 Ok(())
276 }
277
278 fn write_string_fragment<U>(&mut self, _: &mut U, fragment: &str) -> io::Result<()>
279 where
280 U: ?Sized + Write,
281 {
282 self.formatter
283 .write_string_fragment(&mut self.writer, fragment)
284 }
285
286 fn write_char_escape<U>(&mut self, _: &mut U, char_escape: CharEscape) -> io::Result<()>
287 where
288 U: ?Sized + Write,
289 {
290 self.formatter
291 .write_char_escape(&mut self.writer, char_escape)
292 }
293
294 fn begin_array<U>(&mut self, _: &mut U) -> io::Result<()>
295 where
296 U: ?Sized + Write,
297 {
298 self.formatter.begin_array(&mut self.writer)
299 }
300
301 fn end_array<U>(&mut self, _: &mut U) -> io::Result<()>
302 where
303 U: ?Sized + Write,
304 {
305 self.formatter.end_array(&mut self.writer)
306 }
307
308 fn begin_array_value<U>(&mut self, _: &mut U, first: bool) -> io::Result<()>
309 where
310 U: ?Sized + Write,
311 {
312 self.formatter.begin_array_value(&mut self.writer, first)
313 }
314
315 fn end_array_value<U>(&mut self, _: &mut U) -> io::Result<()>
316 where
317 U: ?Sized + Write,
318 {
319 self.formatter.end_array_value(&mut self.writer)
320 }
321
322 fn begin_object<U>(&mut self, _: &mut U) -> io::Result<()>
323 where
324 U: ?Sized + Write,
325 {
326 self.formatter.begin_object(&mut self.writer)
327 }
328
329 fn end_object<U>(&mut self, _: &mut U) -> io::Result<()>
330 where
331 U: ?Sized + Write,
332 {
333 self.formatter.end_object(&mut self.writer)
334 }
335
336 fn begin_object_key<U>(&mut self, _: &mut U, first: bool) -> io::Result<()>
337 where
338 U: ?Sized + Write,
339 {
340 self.writing_key = true;
341 self.formatter.begin_object_key(&mut self.writer, first)?;
342 self.need_reset = set_color(&mut self.writer, &self.theme.object_key)?;
343 Ok(())
344 }
345
346 fn end_object_key<U>(&mut self, _: &mut U) -> io::Result<()>
347 where
348 U: ?Sized + Write,
349 {
350 if mem::take(&mut self.need_reset) {
351 reset(&mut self.writer, self.theme)?;
352 }
353 self.formatter.end_object_key(&mut self.writer)?;
354 self.writing_key = false;
355 Ok(())
356 }
357
358 fn begin_object_value<U>(&mut self, _: &mut U) -> io::Result<()>
359 where
360 U: ?Sized + Write,
361 {
362 self.formatter.begin_object_value(&mut self.writer)
363 }
364
365 fn end_object_value<U>(&mut self, _: &mut U) -> io::Result<()>
366 where
367 U: ?Sized + Write,
368 {
369 self.formatter.end_object_value(&mut self.writer)
370 }
371
372 fn write_raw_fragment<U>(&mut self, _: &mut U, fragment: &str) -> io::Result<()>
373 where
374 U: ?Sized + io::Write,
375 {
376 self.formatter
377 .write_raw_fragment(&mut self.writer, fragment)
378 }
379}
380
381fn set_color<W>(writer: &mut W, color: &ColorSpec) -> io::Result<bool>
382where
383 W: WriteColor,
384{
385 if color.is_none() {
386 Ok(false)
387 } else {
388 writer.set_color(color)?;
389 Ok(true)
390 }
391}
392
393fn reset<W>(writer: &mut W, theme: &Theme) -> io::Result<()>
394where
395 W: WriteColor,
396{
397 writer.set_color(&theme.reset)
398}
399
400fn with_color<W, F>(writer: &mut W, color: &ColorSpec, theme: &Theme, write: F) -> io::Result<()>
401where
402 W: WriteColor,
403 F: FnOnce(&mut W) -> io::Result<()>,
404{
405 if set_color(writer, color)? {
406 write(writer)?;
407 reset(writer, theme)?;
408 Ok(())
409 } else {
410 write(writer)
411 }
412}
413
414struct SharedWriter<W> {
423 inner: RefCell<W>,
424}
425
426impl<W> SharedWriter<W> {
427 fn new(writer: W) -> Self {
428 SharedWriter {
429 inner: RefCell::new(writer),
430 }
431 }
432}
433
434impl<W> Write for &'_ SharedWriter<W>
435where
436 W: Write,
437{
438 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
439 self.inner.borrow_mut().write(buf)
440 }
441
442 fn flush(&mut self) -> io::Result<()> {
443 self.inner.borrow_mut().flush()
444 }
445
446 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> io::Result<usize> {
447 self.inner.borrow_mut().write_vectored(bufs)
448 }
449
450 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
451 self.inner.borrow_mut().write_all(buf)
452 }
453}
454
455impl<W> WriteColor for &'_ SharedWriter<W>
456where
457 W: WriteColor,
458{
459 fn supports_color(&self) -> bool {
460 self.inner.borrow().supports_color()
461 }
462
463 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
464 self.inner.borrow_mut().set_color(spec)
465 }
466
467 fn reset(&mut self) -> io::Result<()> {
468 self.inner.borrow_mut().reset()
469 }
470
471 fn is_synchronous(&self) -> bool {
472 self.inner.borrow().is_synchronous()
473 }
474}
475
476impl Theme {
477 pub fn none() -> Self {
479 Theme::new(ColorSpec::new())
480 }
481
482 pub fn new(default: ColorSpec) -> Self {
484 Theme {
485 reset: default.clone(),
486 null: default.clone(),
487 bool: default.clone(),
488 number: default.clone(),
489 string: default.clone(),
490 object_key: default,
491 }
492 }
493
494 pub fn null(&self) -> &ColorSpec {
496 &self.null
497 }
498
499 pub fn null_mut(&mut self) -> &mut ColorSpec {
501 &mut self.null
502 }
503
504 pub fn bool(&self) -> &ColorSpec {
506 &self.bool
507 }
508
509 pub fn bool_mut(&mut self) -> &mut ColorSpec {
511 &mut self.bool
512 }
513
514 pub fn number(&self) -> &ColorSpec {
516 &self.number
517 }
518
519 pub fn number_mut(&mut self) -> &mut ColorSpec {
521 &mut self.number
522 }
523
524 pub fn string(&self) -> &ColorSpec {
528 &self.string
529 }
530
531 pub fn string_mut(&mut self) -> &mut ColorSpec {
535 &mut self.string
536 }
537
538 pub fn object_key(&self) -> &ColorSpec {
540 &self.object_key
541 }
542
543 pub fn object_key_mut(&mut self) -> &mut ColorSpec {
545 &mut self.object_key
546 }
547}
548
549impl Default for Theme {
550 fn default() -> Self {
552 let mut theme = Theme::none();
553
554 theme.null_mut().set_fg(Some(Color::Cyan)).set_bold(true);
555 theme.bool_mut().set_fg(Some(Color::Cyan)).set_bold(true);
556 theme.number_mut().set_fg(Some(Color::Cyan));
557 theme.string_mut().set_fg(Some(Color::Green));
558 theme
559 .object_key_mut()
560 .set_fg(Some(Color::Blue))
561 .set_intense(true);
562
563 theme
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570
571 fn data() -> impl Serialize {
572 serde_json::json!({
573 "b": true,
574 "n": null,
575 "m": 1,
576 "s": "v",
577 })
578 }
579
580 fn to_readable_string(vec: Vec<u8>) -> String {
581 String::from_utf8_lossy(&vec).replace('\x1B', "^[")
582 }
583
584 #[test]
585 fn no_color_passthrough() {
586 use termcolor::NoColor;
587
588 let mut buf = Vec::new();
589 let writer = NoColor::new(io::Cursor::new(&mut buf));
590
591 to_writer(writer, &data()).unwrap();
592
593 assert_eq!(
594 to_readable_string(buf).as_str(),
595 "{\n \"b\": true,\n \"m\": 1,\n \"n\": null,\n \"s\": \"v\"\n}"
596 );
597 }
598
599 #[test]
600 fn no_color_passthrough_compact() {
601 use termcolor::NoColor;
602
603 let mut buf = Vec::new();
604 let writer = NoColor::new(io::Cursor::new(&mut buf));
605
606 to_writer_compact(writer, &data()).unwrap();
607
608 assert_eq!(
609 to_readable_string(buf).as_str(),
610 "{\"b\":true,\"m\":1,\"n\":null,\"s\":\"v\"}"
611 );
612 }
613
614 #[test]
615 fn ansi_empty_theme_passthrough() {
616 use termcolor::Ansi;
617
618 let mut buf = Vec::new();
619 let writer = Ansi::new(io::Cursor::new(&mut buf));
620
621 to_writer_with_theme(writer, &data(), &Theme::none()).unwrap();
622
623 assert_eq!(
624 to_readable_string(buf).as_str(),
625 "{\n \"b\": true,\n \"m\": 1,\n \"n\": null,\n \"s\": \"v\"\n}"
626 );
627 }
628
629 #[test]
630 fn ansi_empty_theme_passthrough_compact() {
631 use termcolor::Ansi;
632
633 let mut buf = Vec::new();
634 let writer = Ansi::new(io::Cursor::new(&mut buf));
635
636 to_writer_with_theme_and_formatter(writer, &data(), &Theme::none(), CompactFormatter)
637 .unwrap();
638
639 assert_eq!(
640 to_readable_string(buf).as_str(),
641 "{\"b\":true,\"m\":1,\"n\":null,\"s\":\"v\"}"
642 );
643 }
644
645 #[test]
646 fn ansi_default_theme() {
647 use termcolor::Ansi;
648
649 let mut buf = Vec::new();
650 let writer = Ansi::new(io::Cursor::new(&mut buf));
651
652 to_writer(writer, &data()).unwrap();
653
654 assert_eq!(to_readable_string(buf).as_str(), "{\n ^[[0m^[[38;5;12m\"b\"^[[0m: ^[[0m^[[1m^[[36mtrue^[[0m,\n ^[[0m^[[38;5;12m\"m\"^[[0m: ^[[0m^[[36m1^[[0m,\n ^[[0m^[[38;5;12m\"n\"^[[0m: ^[[0m^[[1m^[[36mnull^[[0m,\n ^[[0m^[[38;5;12m\"s\"^[[0m: ^[[0m^[[32m\"v\"^[[0m\n}");
655 }
656
657 #[test]
658 fn ansi_default_theme_compact() {
659 use termcolor::Ansi;
660
661 let mut buf = Vec::new();
662 let writer = Ansi::new(io::Cursor::new(&mut buf));
663
664 to_writer_compact(writer, &data()).unwrap();
665
666 assert_eq!(to_readable_string(buf).as_str(), "{^[[0m^[[38;5;12m\"b\"^[[0m:^[[0m^[[1m^[[36mtrue^[[0m,^[[0m^[[38;5;12m\"m\"^[[0m:^[[0m^[[36m1^[[0m,^[[0m^[[38;5;12m\"n\"^[[0m:^[[0m^[[1m^[[36mnull^[[0m,^[[0m^[[38;5;12m\"s\"^[[0m:^[[0m^[[32m\"v\"^[[0m}");
667 }
668}