rill_json/
value.rs

1//! Contains the `JsonValue` enum, a native Rust representation of any
2//! valid JSON value.
3//!
4//! This module also includes the "stringify" (serialization) logic
5//! for converting a `JsonValue` back into a JSON string.
6use crate::{parse_streaming, ParseError, ParserEvent, StreamingParser};
7use std::collections::BTreeMap;
8use std::fmt;
9
10/// A native Rust representation of any valid JSON number.
11///
12/// This enum is used to store numbers without precision loss,
13/// supporting `i64`, `u64`, and `f64`.
14#[derive(Debug, PartialEq, Clone, Copy)]
15pub enum JsonNumber {
16    /// Represents a signed 64-bit integer.
17    I64(i64),
18    /// Represents an unsigned 64-bit integer.
19    U64(u64),
20    /// Represents a 64-bit floating-point number.
21    F64(f64),
22}
23
24/// Implement Display to allow `write!(w, "{}", ...)`
25impl fmt::Display for JsonNumber {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            JsonNumber::I64(n) => write!(f, "{}", n),
29            JsonNumber::U64(n) => write!(f, "{}", n),
30            JsonNumber::F64(n) => write!(f, "{}", n),
31        }
32    }
33}
34
35/// A native Rust representation of any valid JSON value.
36///
37/// This enum is used by the `stringify` functions to serialize
38/// Rust data *into* a JSON string.
39#[derive(Debug, PartialEq, Clone)]
40pub enum JsonValue {
41    /// Represents a JSON `null`.
42    Null,
43    /// Represents a JSON `true` or `false`.
44    Boolean(bool),
45    /// Represents a JSON number.
46    Number(JsonNumber),
47    /// Represents a JSON string.
48    String(String),
49    /// Represents a JSON array (list).
50    Array(Vec<JsonValue>),
51    /// Represents a JSON object (map).
52    /// We use BTreeMap to guarantee deterministic (alphabetical) key order
53    /// during serialization.
54    Object(BTreeMap<String, JsonValue>),
55}
56
57impl JsonValue {
58    /// Parses a JSON string into a `JsonValue`.
59    ///
60    /// This function builds an in-memory `JsonValue` from the input string.
61    /// For large inputs, using the `parse_streaming` iterator is more memory-efficient.
62    ///
63    /// # Errors
64    /// Returns a `ParseError` if the JSON is invalid, empty, or has trailing tokens.
65    pub fn parse(input: &str) -> Result<JsonValue, ParseError> {
66        let mut parser = parse_streaming(input)?.peekable();
67
68        // Check for empty input
69        if parser.peek().is_none() {
70            return Err(ParseError {
71                message: "Empty input".to_string(),
72                line: 1,
73                column: 1,
74            });
75        }
76
77        // Recursive helper
78        fn parse_one(
79            parser: &mut std::iter::Peekable<StreamingParser<'_>>,
80        ) -> Result<JsonValue, ParseError> {
81            // Consume next event
82            let event = match parser.next() {
83                Some(Ok(event)) => event,
84                Some(Err(e)) => return Err(e),
85                None => {
86                    return Err(ParseError {
87                        message: "Unexpected end of input".to_string(),
88                        line: 1, // This is a best-effort location
89                        column: 1,
90                    });
91                }
92            };
93
94            match event {
95                // Base cases
96                ParserEvent::String(s) => Ok(JsonValue::String(s.into_owned())),
97                ParserEvent::Number(n) => Ok(JsonValue::Number(n)),
98                ParserEvent::Boolean(b) => Ok(JsonValue::Boolean(b)),
99                ParserEvent::Null => Ok(JsonValue::Null),
100
101                // Recursive cases
102                ParserEvent::StartArray => {
103                    let mut arr = Vec::new();
104                    // Loop until we see `EndArray`
105                    loop {
106                        match parser.peek() {
107                            Some(Ok(ParserEvent::EndArray)) => {
108                                parser.next(); // Consume the EndArray
109                                break Ok(JsonValue::Array(arr));
110                            }
111                            Some(Ok(_)) => {
112                                // It's a value, recurse
113                                arr.push(parse_one(parser)?);
114                            }
115                            Some(Err(_)) => {
116                                // Propagate the error
117                                return Err(parser.next().unwrap().unwrap_err());
118                            }
119                            None => {
120                                // This branch should be unreachable.
121                                // The StreamingParser's `next()` will return
122                                // `Some(Err("Unclosed array"))` when it hits EOF
123                                // in this state, which `peek()` will cache.
124                                // The `Some(Err(_))` branch will be taken instead.
125                                unreachable!("Unclosed array branch hit None");
126                            }
127                        }
128                    }
129                }
130
131                ParserEvent::StartObject => {
132                    let mut obj = BTreeMap::new();
133                    // Loop until we see `EndObject`
134                    loop {
135                        match parser.peek() {
136                            Some(Ok(ParserEvent::EndObject)) => {
137                                parser.next(); // Consume the EndObject
138                                break Ok(JsonValue::Object(obj));
139                            }
140                            Some(Ok(ParserEvent::Key(_))) => {
141                                // Get the key
142                                let key = match parser.next() {
143                                    Some(Ok(ParserEvent::Key(key))) => key.into_owned(),
144                                    _ => unreachable!(), // We just peeked
145                                };
146                                // Get the value
147                                let val = parse_one(parser)?;
148                                obj.insert(key, val);
149                            }
150                            Some(Ok(_)) => {
151                                // This branch should be unreachable.
152                                // The StreamingParser would see a non-string token
153                                // (like a number) and return
154                                // `Err("Expected '}' or a string key")`.
155                                // This `Err` would be caught by `Some(Err(_))`.
156                                unreachable!("Invalid event in object");
157                            }
158                            Some(Err(_)) => {
159                                // Propagate the error
160                                return Err(parser.next().unwrap().unwrap_err());
161                            }
162                            None => {
163                                // This branch should be unreachable.
164                                // The StreamingParser will return `Some(Err("Unclosed object"))`
165                                // which `peek()` will cache, causing the
166                                // `Some(Err(_))` branch to be taken.
167                                unreachable!("Unclosed object branch hit None");
168                            }
169                        }
170                    }
171                }
172
173                // Invalid start
174                ParserEvent::Key(_) | ParserEvent::EndArray | ParserEvent::EndObject => {
175                    // This should be unreachable. The StreamingParser's
176                    // state machine should never emit these events when
177                    // `parse_one` is expecting a value. It would
178                    // return `Err("Expected a value")` instead.
179                    unreachable!("Invalid start event: {:?}", event)
180                }
181            }
182        } // End of `parse_one`
183
184        // Parse the root value
185        let root = parse_one(&mut parser)?;
186
187        // Check for trailing tokens
188        match parser.next() {
189            None => Ok(root),
190            Some(Err(e)) => Err(e),
191            // This branch is unreachable. The StreamingParser's `next()`
192            // will return `Some(Err("Unexpected trailing token"))` if it
193            // finds a token when its state stack is empty.
194            Some(Ok(event)) => unreachable!("Trailing token was not an error: {:?}", event),
195        }
196    }
197}
198
199impl JsonValue {
200    /// Serializes the `JsonValue` into a compact, minified JSON string.
201    ///
202    /// # Errors
203    /// Returns `fmt::Error` if the value contains `f64::NAN` or `f64::INFINITY`.
204    pub fn stringify(&self) -> Result<String, fmt::Error> {
205        let mut output = String::new();
206        Self::write_value(self, &mut output)?;
207        Ok(output)
208    }
209
210    /// Recursive helper function to write any `JsonValue` to a string buffer.
211    fn write_value<W: fmt::Write>(value: &JsonValue, w: &mut W) -> fmt::Result {
212        match value {
213            JsonValue::Null => w.write_str("null"),
214            JsonValue::Boolean(b) => w.write_str(if *b { "true" } else { "false" }),
215            // Check for NaN/inf (which are invalid JSON) and use JsonNumber.
216            JsonValue::Number(n) => match n {
217                JsonNumber::F64(f) if f.is_nan() || f.is_infinite() => {
218                    Err(fmt::Error) // Hard error
219                }
220                _ => write!(w, "{}", n), // Use JsonNumber's Display impl
221            },
222            JsonValue::String(s) => Self::write_string(s, w),
223            JsonValue::Array(a) => Self::write_array(a, w),
224            JsonValue::Object(o) => Self::write_object(o, w),
225        }
226    }
227
228    /// Helper to write a JSON array (compact).
229    fn write_array<W: fmt::Write>(arr: &Vec<JsonValue>, w: &mut W) -> fmt::Result {
230        w.write_char('[')?;
231        let mut first = true;
232        for val in arr {
233            if !first {
234                w.write_char(',')?;
235            }
236            Self::write_value(val, w)?;
237            first = false;
238        }
239        w.write_char(']')
240    }
241
242    /// Helper to write a JSON object (compact).
243    fn write_object<W: fmt::Write>(obj: &BTreeMap<String, JsonValue>, w: &mut W) -> fmt::Result {
244        w.write_char('{')?;
245        let mut first = true;
246        // Note: BTreeMap iteration order IS guaranteed (alphabetical).
247        for (key, val) in obj {
248            if !first {
249                w.write_char(',')?;
250            }
251            Self::write_string(key, w)?; // Write the key (which must be a string)
252            w.write_char(':')?;
253            Self::write_value(val, w)?; // Write the value
254            first = false;
255        }
256        w.write_char('}')
257    }
258
259    /// Helper to write an escaped JSON string.
260    /// This handles all required JSON escape sequences (e.g., `\"`, `\\`, `\n`).
261    fn write_string<W: fmt::Write>(s: &str, w: &mut W) -> fmt::Result {
262        w.write_char('"')?;
263        for c in s.chars() {
264            match c {
265                // Standard escapes
266                '"' => w.write_str("\\\""),
267                '\\' => w.write_str("\\\\"),
268                '/' => w.write_str("\\/"), // Optional, but good practice
269                '\u{0008}' => w.write_str("\\b"), // Backspace
270                '\u{000C}' => w.write_str("\\f"), // Form feed
271                '\n' => w.write_str("\\n"), // Newline
272                '\r' => w.write_str("\\r"), // Carriage return
273                '\t' => w.write_str("\\t"), // Tab
274                // Control characters must be escaped as \uXXXX
275                '\u{0000}'..='\u{001F}' => {
276                    write!(w, "\\u{:04x}", c as u32)
277                }
278                _ => w.write_char(c),
279            }?;
280        }
281        w.write_char('"')
282    }
283
284    // --- Pretty-Printing Logic ---
285    /// The indentation string to use for pretty-printing (two spaces).
286    const INDENT: &'static str = "  ";
287
288    /// Serializes the `JsonValue` into a human-readable,
289    /// indented JSON string ("pretty-print").
290    ///
291    /// # Errors
292    /// Returns `fmt::Error` if the value contains `f64::NAN` or `f64::INFINITY`.
293    pub fn stringify_pretty(&self) -> Result<String, fmt::Error> {
294        let mut output = String::new();
295        Self::write_value_pretty(self, &mut output, 0)?;
296        Ok(output)
297    }
298
299    /// Recursive helper for pretty-printing a value.
300    fn write_value_pretty<W: fmt::Write>(
301        value: &JsonValue,
302        w: &mut W,
303        depth: usize,
304    ) -> fmt::Result {
305        match value {
306            // Primitives
307            JsonValue::Null => w.write_str("null"),
308            JsonValue::Boolean(b) => w.write_str(if *b { "true" } else { "false" }),
309            JsonValue::Number(n) => match n {
310                JsonNumber::F64(f) if f.is_nan() || f.is_infinite() => {
311                    Err(fmt::Error) // Hard error
312                }
313                _ => write!(w, "{}", n), // Use JsonNumber's Display impl
314            },
315            JsonValue::String(s) => Self::write_string(s, w),
316            // Composites
317            JsonValue::Array(a) => Self::write_array_pretty(a, w, depth),
318            JsonValue::Object(o) => Self::write_object_pretty(o, w, depth),
319        }
320    }
321
322    /// Helper to pretty-print a JSON array.
323    fn write_array_pretty<W: fmt::Write>(
324        arr: &Vec<JsonValue>,
325        w: &mut W,
326        depth: usize,
327    ) -> fmt::Result {
328        // Empty array is just "[]"
329        if arr.is_empty() {
330            return w.write_str("[]");
331        }
332
333        let new_depth = depth + 1;
334        let indent = Self::INDENT.repeat(new_depth);
335        let closing_indent = Self::INDENT.repeat(depth);
336
337        w.write_str("[\n")?; // Opening bracket and newline
338
339        let mut first = true;
340        for val in arr {
341            if !first {
342                w.write_str(",\n")?; // Comma and newline before next item
343            }
344            w.write_str(&indent)?; // Indent
345            Self::write_value_pretty(val, w, new_depth)?; // Write the value
346            first = false;
347        }
348
349        write!(w, "\n{}", closing_indent)?; // Newline and closing indent
350        w.write_char(']') // Closing bracket
351    }
352
353    /// Helper to pretty-print a JSON object.
354    fn write_object_pretty<W: fmt::Write>(
355        obj: &BTreeMap<String, JsonValue>,
356        w: &mut W,
357        depth: usize,
358    ) -> fmt::Result {
359        // Empty object is just "{}"
360        if obj.is_empty() {
361            return w.write_str("{}");
362        }
363
364        let new_depth = depth + 1;
365        let indent = Self::INDENT.repeat(new_depth);
366        let closing_indent = Self::INDENT.repeat(depth);
367
368        w.write_str("{\n")?; // Opening brace and newline
369
370        let mut first = true;
371        for (key, val) in obj {
372            if !first {
373                w.write_str(",\n")?; // Comma and newline before next item
374            }
375            w.write_str(&indent)?; // Indent
376            Self::write_string(key, w)?; // Write the key
377            w.write_str(": ")?; // Colon and space
378            Self::write_value_pretty(val, w, new_depth)?; // Write the value
379            first = false;
380        }
381
382        write!(w, "\n{}", closing_indent)?; // Newline and closing indent
383        w.write_char('}') // Closing brace
384    }
385}