facet_json_write/
serialize.rs

1use facet_poke::{Peek, PeekValue};
2use facet_trait::ShapeExt as _;
3use std::collections::VecDeque;
4use std::io::{self, Write};
5
6/// Serializes any Facet type to JSON
7pub 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                            // For other types, we'll use a placeholder
66                            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                        // Push fields in reverse order so they'll be processed in the correct order
82                        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                        // Push items in reverse order
110                        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                        // Collect entries using the iterator and convert them to the format expected by MapEntry
125                        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                        // Push entries in reverse order
136                        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                // Process key first (inline with no indentation)
227                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                            // This should not happen for simple key serialization
259                            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
289/// Serializes any Facet type to JSON and returns it as a String
290pub 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}