facet_json_write/
serialize.rs

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
60/// Serializes any Facet type to JSON
61pub 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                        // Push fields in reverse order so they'll be processed in the correct order
122                        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                        // Push items in reverse order
150                        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                        // Collect entries using the iterator and convert them to the format expected by MapEntry
165                        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                        // Push entries in reverse order
176                        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                // Process key first (inline with no indentation)
267                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                            // This should not happen for simple key serialization
287                            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
317/// Serializes any Facet type to JSON and returns it as a String
318///
319/// # Example
320///
321/// ```rust
322/// use facet::Facet;
323/// use facet_json_write::to_json_string;
324/// use facet_peek::Peek;
325///
326/// #[derive(facet::Facet)]
327/// struct Foo {
328///   bar: String,
329///   baz: u32,
330/// }
331///
332/// let foo = Foo {
333///   bar: "Hello, World!".to_string(),
334///   baz: 42,
335/// };
336/// let foo = Peek::new(&foo);
337///
338/// println!("{}", to_json_string(foo, true));
339/// ```
340pub 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}