facet_json/
serialize.rs

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