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}