facet_json_write/
serialize.rs

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