1use facet_poke::{Peek, PeekValue};
2use log::trace;
3use std::collections::VecDeque;
4use std::io::{self, Write};
5use std::num::NonZero;
6
7macro_rules! prim {
8 ($pv:expr, $writer:expr, $type:ty) => {
9 if $pv.shape().is_type::<$type>() {
10 let value = unsafe { $pv.data().as_ref::<$type>() };
11 return write!($writer, "{}", value);
12 }
13 };
14}
15
16macro_rules! prims {
17 ($pv:expr, $writer:expr, $type:ty, $($types:ty),*) => {
18 prim!($pv, $writer, $type);
19 prims!($pv, $writer, $($types),*);
20 };
21 ($pv:expr, $writer:expr, $type:ty) => {
22 prim!($pv, $writer, $type);
23 };
24}
25
26macro_rules! int {
27 ($pv:expr, $writer:expr, $type:ty) => {
28 prim!($pv, $writer, $type);
29 prim!($pv, $writer, NonZero<$type>);
30 };
31}
32
33macro_rules! ints {
34 ($pv:expr, $writer:expr, $type:ty, $($types:ty),*) => {
35 int!($pv, $writer, $type);
36 ints!($pv, $writer, $($types),*);
37 };
38 ($pv:expr, $writer:expr, $type:ty) => {
39 int!($pv, $writer, $type);
40 };
41}
42
43fn peek_value_to_json<W: Write>(pv: PeekValue, writer: &mut W) -> io::Result<()> {
44 if pv.shape().is_type::<()>() {
45 return write!(writer, "null");
46 }
47 prims!(pv, writer, bool, f32, f64);
48 ints!(
49 pv, writer, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize
50 );
51 if pv.shape().is_type::<String>() {
52 let value = unsafe { pv.data().as_ref::<String>() };
53 return write!(writer, "\"{}\"", value.escape_debug());
54 }
55
56 write!(writer, "\"<unsupported type>\"")?;
57 Ok(())
58}
59
60pub fn to_json<W: Write>(peek: Peek<'_>, writer: &mut W, indent: bool) -> io::Result<()> {
62 #[derive(Debug)]
63 enum StackItem<'mem> {
64 Value {
65 peek: Peek<'mem>,
66 level: usize,
67 },
68 StructField {
69 field_name: String,
70 peek: Peek<'mem>,
71 level: usize,
72 is_first: bool,
73 },
74 StructEnd {
75 level: usize,
76 had_fields: bool,
77 },
78 ListItem {
79 peek: Peek<'mem>,
80 level: usize,
81 is_first: bool,
82 },
83 ListEnd {
84 level: usize,
85 had_items: bool,
86 },
87 MapEntry {
88 key: Peek<'mem>,
89 value: Peek<'mem>,
90 level: usize,
91 is_first: bool,
92 },
93 MapEnd {
94 level: usize,
95 had_entries: bool,
96 },
97 }
98
99 let mut stack: VecDeque<StackItem> = VecDeque::new();
100 stack.push_back(StackItem::Value { peek, level: 0 });
101
102 while let Some(item) = stack.pop_front() {
103 match item {
104 StackItem::Value { peek, level } => {
105 match peek {
106 Peek::Value(pv) => {
107 peek_value_to_json(pv, writer)?;
108 }
109 Peek::Struct(ps) => {
110 write!(writer, "{{")?;
111 if indent {
112 writeln!(writer)?;
113 }
114
115 let fields: Vec<_> = ps.fields().collect();
116 stack.push_front(StackItem::StructEnd {
117 level,
118 had_fields: !fields.is_empty(),
119 });
120
121 for (i, field) in fields.into_iter().enumerate().rev() {
123 stack.push_front(StackItem::StructField {
124 field_name: field.0.to_string(),
125 peek: field.1,
126 level,
127 is_first: i == 0,
128 });
129 }
130 }
131 Peek::List(pl) => {
132 write!(writer, "[")?;
133 if indent {
134 writeln!(writer)?;
135 }
136
137 let mut items = Vec::new();
138 let mut index = 0;
139 while let Some(item) = pl.item_at(index) {
140 items.push(item);
141 index += 1;
142 }
143
144 stack.push_front(StackItem::ListEnd {
145 level,
146 had_items: !items.is_empty(),
147 });
148
149 for (i, item) in items.into_iter().enumerate().rev() {
151 stack.push_front(StackItem::ListItem {
152 peek: item,
153 level,
154 is_first: i == 0,
155 });
156 }
157 }
158 Peek::Map(pm) => {
159 write!(writer, "{{")?;
160 if indent {
161 writeln!(writer)?;
162 }
163
164 let entries: Vec<(PeekValue<'_>, Peek<'_>)> = pm
166 .iter()
167 .map(|(key, value)| (key.as_value(), value))
168 .collect();
169
170 stack.push_front(StackItem::MapEnd {
171 level,
172 had_entries: !entries.is_empty(),
173 });
174
175 for (i, (key, value)) in entries.into_iter().enumerate().rev() {
177 stack.push_front(StackItem::MapEntry {
178 key: Peek::Value(key),
179 value,
180 level,
181 is_first: i == 0,
182 });
183 }
184 }
185 _ => todo!("unsupported peek type: {:?}", peek),
186 }
187 }
188 StackItem::StructField {
189 field_name,
190 peek,
191 level,
192 is_first,
193 } => {
194 if !is_first {
195 write!(writer, ",")?;
196 if indent {
197 writeln!(writer)?;
198 }
199 }
200
201 if indent {
202 write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
203 }
204 write!(writer, "\"{}\":", field_name)?;
205 if indent {
206 write!(writer, " ")?;
207 }
208
209 stack.push_front(StackItem::Value {
210 peek,
211 level: level + 1,
212 });
213 }
214 StackItem::StructEnd { level, had_fields } => {
215 if had_fields && indent {
216 writeln!(writer)?;
217 write!(writer, "{:indent$}", "", indent = level * 2)?;
218 }
219 write!(writer, "}}")?;
220 }
221 StackItem::ListItem {
222 peek,
223 level,
224 is_first,
225 } => {
226 if !is_first {
227 write!(writer, ",")?;
228 if indent {
229 writeln!(writer)?;
230 }
231 }
232
233 if indent {
234 write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
235 }
236
237 stack.push_front(StackItem::Value {
238 peek,
239 level: level + 1,
240 });
241 }
242 StackItem::ListEnd { level, had_items } => {
243 if had_items && indent {
244 writeln!(writer)?;
245 write!(writer, "{:indent$}", "", indent = level * 2)?;
246 }
247 write!(writer, "]")?;
248 }
249 StackItem::MapEntry {
250 key,
251 value,
252 level,
253 is_first,
254 } => {
255 if !is_first {
256 write!(writer, ",")?;
257 if indent {
258 writeln!(writer)?;
259 }
260 }
261
262 if indent {
263 write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
264 }
265
266 let mut temp_writer = Vec::new();
268 let mut temp_stack = VecDeque::new();
269 temp_stack.push_back(StackItem::Value {
270 peek: key,
271 level: 0,
272 });
273
274 while let Some(temp_item) = temp_stack.pop_front() {
275 match temp_item {
276 StackItem::Value { peek, level: _ } => match peek {
277 Peek::Value(pv) => {
278 trace!("{:?}", pv.shape());
279 peek_value_to_json(pv, &mut temp_writer)?;
280 }
281 _ => {
282 write!(&mut temp_writer, "\"<complex_key>\"")?;
283 }
284 },
285 _ => {
286 write!(&mut temp_writer, "\"<invalid_key>\"")?;
288 }
289 }
290 }
291
292 let key_string = String::from_utf8(temp_writer).unwrap();
293 write!(writer, "{}:", key_string)?;
294
295 if indent {
296 write!(writer, " ")?;
297 }
298
299 stack.push_front(StackItem::Value {
300 peek: value,
301 level: level + 1,
302 });
303 }
304 StackItem::MapEnd { level, had_entries } => {
305 if had_entries && indent {
306 writeln!(writer)?;
307 write!(writer, "{:indent$}", "", indent = level * 2)?;
308 }
309 write!(writer, "}}")?;
310 }
311 }
312 }
313
314 Ok(())
315}
316
317pub fn to_json_string(peek: Peek<'_>, indent: bool) -> String {
341 let mut buffer = Vec::new();
342 to_json(peek, &mut buffer, indent).unwrap();
343 String::from_utf8(buffer).unwrap()
344}