1use crate::Val;
8use alloc::string::String;
9use core::fmt::{self, Formatter};
10#[cfg(feature = "std")]
11use std::io;
12
13#[macro_export]
20macro_rules! write_byte {
21 ($w:ident, $c:expr, $f:expr) => {{
22 match $c {
23 0x08 => write!($w, "\\b"),
25 0x0c => write!($w, "\\f"),
26 c @ (b'\t' | b'\n' | b'\r' | b'\\' | b'"') => {
27 write!($w, "{}", char::from(c).escape_default())
28 }
29 0x00..=0x1F | 0x7F..=0xFF => $f,
30 c => write!($w, "{}", char::from(c)),
31 }
32 }};
33}
34
35#[macro_export]
39macro_rules! write_utf8 {
40 ($w:ident, $s:ident, $f:expr) => {{
41 write!($w, "\"")?;
42 let is_special = |c| matches!(c, 0x00..=0x1F | b'\\' | b'"' | 0x7F);
43 for s in $s.split_inclusive(|c| is_special(*c)) {
44 match s.split_last() {
45 Some((last, init)) if is_special(*last) => {
46 $f(init)?;
47 $crate::write_byte!($w, *last, write!($w, "\\u{last:04x}"))?
48 }
49 _ => $f(s)?,
50 }
51 }
52 write!($w, "\"")
53 }};
54}
55
56#[macro_export]
60macro_rules! write_bytes {
61 ($w:ident, $s:ident) => {{
62 write!($w, "b\"")?;
63 $s.iter()
64 .try_for_each(|c| $crate::write_byte!($w, *c, write!($w, "\\x{c:02x}")))?;
65 write!($w, "\"")
66 }};
67}
68
69#[macro_export]
71macro_rules! write_seq {
72 ($w:ident, $pp:expr, $level:expr, $xs:expr, $f:expr) => {{
73 let indent = &$pp.indent;
74 if indent.is_some() {
75 writeln!($w)?;
76 }
77 let mut iter = $xs.into_iter().peekable();
78 while let Some(x) = iter.next() {
79 if let Some(indent) = indent {
80 write!($w, "{}", indent.repeat($level + 1))?;
81 }
82 $f(x)?;
83 if iter.peek().is_some() {
84 write!($w, ",")?;
85 if $pp.sep_space && indent.is_none() {
86 write!($w, " ")?
87 }
88 }
89 if indent.is_some() {
90 writeln!($w)?
91 }
92 }
93 if let Some(indent) = indent {
94 write!($w, "{}", indent.repeat($level))
95 } else {
96 Ok(())
97 }
98 }};
99}
100
101#[derive(Clone, Default)]
103pub struct Styles<S = String> {
104 pub null: S,
106 pub r#false: S,
108 pub r#true: S,
110 pub num: S,
112 pub str: S,
114 pub arr: S,
116 pub obj: S,
118 pub key: S,
120
121 pub bstr: S,
123
124 pub reset: S,
126}
127
128impl Styles {
129 pub fn ansi() -> Self {
131 let mut cols = Styles::default().parse("90:39:39:39:32:1;39:1;39:1;34");
132 cols.bstr = "\x1b[31m".into();
133 cols.reset = "\x1b[0m".into();
134 cols
135 }
136
137 pub fn parse(mut self, s: &str) -> Self {
139 let fields = [
140 &mut self.null,
141 &mut self.r#false,
142 &mut self.r#true,
143 &mut self.num,
144 &mut self.str,
145 &mut self.arr,
146 &mut self.obj,
147 &mut self.key,
148 ];
149 for (style, field) in s.split(':').zip(fields) {
150 *field = if style.is_empty() {
151 "".into()
152 } else {
153 alloc::format!("\x1b[{style}m")
154 }
155 }
156 self
157 }
158}
159
160#[derive(Clone, Default)]
162pub struct Pp<S = String> {
163 pub indent: Option<S>,
165 pub sort_keys: bool,
167 pub styles: Styles<S>,
169 pub sep_space: bool,
175}
176
177#[macro_export]
179macro_rules! style {
180 ($w:ident, $pp:ident, $style:ident, $f:expr) => {{
181 let style = &$pp.styles.$style;
182 let reset = if style.is_empty() {
183 ""
184 } else {
185 &*$pp.styles.reset
186 };
187 write!($w, "{style}")?;
188 $f?;
189 write!($w, "{reset}")
190 }};
191}
192
193#[macro_export]
202macro_rules! format_val {
203 ($w:ident, $pp:ident, $level:expr, $v:ident, $f:expr) => {{
204 macro_rules! color {
205 ($style:ident, $g:expr) => {{
206 $crate::style!($w, $pp, $style, $g)
207 }};
208 }
209 match $v {
210 Val::Null => color!(null, write!($w, "null")),
211 Val::Bool(true) => color!(r#true, write!($w, "true")),
212 Val::Bool(false) => color!(r#false, write!($w, "false")),
213 Val::Num(n) => color!(num, write!($w, "{n}")),
214 Val::BStr(b) => color!(bstr, $crate::write_bytes!($w, b)),
215 Val::TStr(s) => color!(
216 str,
217 $crate::write_utf8!($w, s, |part| write!($w, "{}", $crate::bstr(part)))
218 ),
219 Val::Arr(a) => {
220 color!(arr, write!($w, "["))?;
221 if !a.is_empty() {
222 $crate::write_seq!($w, $pp, $level, &**a, |x| $f($w, $pp, $level + 1, x))?;
223 }
224 color!(arr, write!($w, "]"))
225 }
226 Val::Obj(o) => {
227 color!(obj, write!($w, "{{"))?;
228 macro_rules! kv {
229 ($kv:expr) => {{
230 let (k, v) = $kv;
231 if $pp.styles.key.is_empty() {
232 $f($w, $pp, $level + 1, k)?
233 } else {
234 let unstyled = $crate::write::Pp {
235 styles: Default::default(),
236 ..$pp.clone()
237 };
238 color!(key, $f($w, &unstyled, $level + 1, k))?;
239 }
240 color!(obj, write!($w, ":"))?;
241 if $pp.sep_space {
242 write!($w, " ")?;
243 }
244 $f($w, $pp, $level + 1, v)
245 }};
246 }
247 if !o.is_empty() {
248 if $pp.sort_keys {
249 let mut o: alloc::vec::Vec<_> = o.iter().collect();
250 o.sort_by_key(|(k, _v)| *k);
251 $crate::write_seq!($w, $pp, $level, o, |x| kv!(x))
252 } else {
253 $crate::write_seq!($w, $pp, $level, &**o, |x| kv!(x))
254 }?
255 }
256 color!(obj, write!($w, "}}"))
257 }
258 }
259 }};
260}
261
262#[macro_export]
264macro_rules! write_val {
265 ($w:ident, $pp:ident, $level:expr, $v:ident, $f:expr) => {{
266 use $crate::Val::TStr;
267 match $v {
268 TStr(s) => style!($w, $pp, str, write_utf8!($w, s, |part| $w.write_all(part))),
269 _ => format_val!($w, $pp, $level, $v, $f),
270 }
271 }};
272}
273
274#[cfg(feature = "std")]
302pub fn write(w: &mut dyn io::Write, pp: &Pp, level: usize, v: &Val) -> io::Result<()> {
303 write_val!(w, pp, level, v, write)
304}
305
306pub(crate) struct Buf(pub(crate) alloc::vec::Vec<u8>);
307
308impl Buf {
309 fn write_all(&mut self, bytes: &[u8]) -> fmt::Result {
310 self.0.extend_from_slice(bytes);
311 Ok(())
312 }
313}
314
315impl fmt::Write for Buf {
316 fn write_str(&mut self, s: &str) -> fmt::Result {
317 self.write_all(s.as_bytes())
318 }
319}
320
321pub(crate) fn write_buf(w: &mut Buf, pp: &Pp, level: usize, v: &Val) -> fmt::Result {
322 use core::fmt::Write;
323 write_val!(w, pp, level, v, write_buf)
324}
325
326pub(crate) fn format(w: &mut Formatter, pp: &Pp, level: usize, v: &Val) -> fmt::Result {
327 format_val!(w, pp, level, v, format)
328}