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