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 std::collections::HashMap;
7use std::fmt;
8
9// --- 5. JSON Value Enum (for Stage 16) ---
10
11/// A native Rust representation of any valid JSON value.
12///
13/// This enum is used by the `stringify` functions to serialize
14/// Rust data *into* a JSON string.
15#[derive(Debug, PartialEq, Clone)]
16pub enum JsonValue {
17    /// Represents a JSON `null`.
18    Null,
19    /// Represents a JSON `true` or `false`.
20    Boolean(bool),
21    /// Represents a JSON number (stored as `f64`).
22    Number(f64),
23    /// Represents a JSON string.
24    String(String),
25    /// Represents a JSON array (list).
26    Array(Vec<JsonValue>),
27    /// Represents a JSON object (map).
28    Object(HashMap<String, JsonValue>),
29}
30
31// --- 7. Stringify (Serialization - Stage 16) ---
32impl JsonValue {
33    /// Serializes the `JsonValue` into a compact, minified JSON string.
34    ///
35    /// # Examples
36    /// ```
37    /// use rill_json::JsonValue;
38    /// let val = JsonValue::Number(123.0);
39    /// assert_eq!(val.stringify(), "123");
40    /// ```
41    pub fn stringify(&self) -> String {
42        let mut output = String::new();
43        // This unwrap is safe because writing to a String never fails.
44        Self::write_value(self, &mut output).unwrap();
45        output
46    }
47
48    /// Recursive helper function to write any `JsonValue` to a string buffer.
49    fn write_value<W: fmt::Write>(value: &JsonValue, w: &mut W) -> fmt::Result {
50        match value {
51            JsonValue::Null => w.write_str("null"),
52            JsonValue::Boolean(b) => w.write_str(if *b { "true" } else { "false" }),
53            JsonValue::Number(n) => write!(w, "{}", n),
54            JsonValue::String(s) => Self::write_string(s, w),
55            JsonValue::Array(a) => Self::write_array(a, w),
56            JsonValue::Object(o) => Self::write_object(o, w),
57        }
58    }
59
60    /// Helper to write a JSON array (compact).
61    fn write_array<W: fmt::Write>(arr: &Vec<JsonValue>, w: &mut W) -> fmt::Result {
62        w.write_char('[')?;
63        let mut first = true;
64        for val in arr {
65            if !first {
66                w.write_char(',')?;
67            }
68            Self::write_value(val, w)?;
69            first = false;
70        }
71        w.write_char(']')
72    }
73
74    /// Helper to write a JSON object (compact).
75    fn write_object<W: fmt::Write>(obj: &HashMap<String, JsonValue>, w: &mut W) -> fmt::Result {
76        w.write_char('{')?;
77        let mut first = true;
78        // Note: HashMap iteration order is not guaranteed,
79        // but this is fine according to the JSON specification.
80        for (key, val) in obj {
81            if !first {
82                w.write_char(',')?;
83            }
84            Self::write_string(key, w)?; // Write the key (which must be a string)
85            w.write_char(':')?;
86            Self::write_value(val, w)?; // Write the value
87            first = false;
88        }
89        w.write_char('}')
90    }
91
92    /// Helper to write an escaped JSON string.
93    /// This handles all required JSON escape sequences (e.g., `\"`, `\\`, `\n`).
94    fn write_string<W: fmt::Write>(s: &str, w: &mut W) -> fmt::Result {
95        w.write_char('"')?;
96        for c in s.chars() {
97            match c {
98                // Standard escapes
99                '"' => w.write_str("\\\""),
100                '\\' => w.write_str("\\\\"),
101                '/' => w.write_str("\\/"), // Optional, but good practice
102                '\u{0008}' => w.write_str("\\b"), // Backspace
103                '\u{000C}' => w.write_str("\\f"), // Form feed
104                '\n' => w.write_str("\\n"), // Newline
105                '\r' => w.write_str("\\r"), // Carriage return
106                '\t' => w.write_str("\\t"), // Tab
107                // Control characters must be escaped as \uXXXX
108                '\u{0000}'..='\u{001F}' => {
109                    write!(w, "\\u{:04x}", c as u32)
110                }
111                _ => w.write_char(c),
112            }?;
113        }
114        w.write_char('"')
115    }
116
117    // --- Pretty Print Bonus ---
118
119    /// The indentation string to use for pretty-printing (two spaces).
120    const INDENT: &'static str = "  ";
121
122    /// Serializes the `JsonValue` into a human-readable,
123    /// indented JSON string ("pretty-print").
124    ///
125    /// # Examples
126    /// ```
127    /// use rill_json::JsonValue;
128    /// use std::collections::HashMap;
129    ///
130    /// let mut obj = HashMap::new();
131    /// obj.insert("key".to_string(), JsonValue::String("value".to_string()));
132    /// let val = JsonValue::Object(obj);
133    ///
134    /// let pretty = val.stringify_pretty();
135    /// assert!(pretty.starts_with("{\n"));
136    /// assert!(pretty.contains("\n  \"key\": \"value\"\n"));
137    /// assert!(pretty.ends_with("\n}"));
138    /// ```
139    pub fn stringify_pretty(&self) -> String {
140        let mut output = String::new();
141        // This unwrap is safe because writing to a String never fails.
142        Self::write_value_pretty(self, &mut output, 0).unwrap();
143        output
144    }
145
146    /// Recursive helper for pretty-printing a value.
147    fn write_value_pretty<W: fmt::Write>(
148        value: &JsonValue,
149        w: &mut W,
150        depth: usize,
151    ) -> fmt::Result {
152        match value {
153            // Primitives are written the same as compact
154            JsonValue::Null => w.write_str("null"),
155            JsonValue::Boolean(b) => w.write_str(if *b { "true" } else { "false" }),
156            JsonValue::Number(n) => write!(w, "{}", n),
157            JsonValue::String(s) => Self::write_string(s, w),
158            // Composites (Array/Object) get new logic
159            JsonValue::Array(a) => Self::write_array_pretty(a, w, depth),
160            JsonValue::Object(o) => Self::write_object_pretty(o, w, depth),
161        }
162    }
163
164    /// Helper to pretty-print a JSON array.
165    fn write_array_pretty<W: fmt::Write>(
166        arr: &Vec<JsonValue>,
167        w: &mut W,
168        depth: usize,
169    ) -> fmt::Result {
170        // Empty array is just "[]"
171        if arr.is_empty() {
172            return w.write_str("[]");
173        }
174
175        let new_depth = depth + 1;
176        let indent = Self::INDENT.repeat(new_depth);
177        let closing_indent = Self::INDENT.repeat(depth);
178
179        w.write_str("[\n")?; // Opening bracket and newline
180
181        let mut first = true;
182        for val in arr {
183            if !first {
184                w.write_str(",\n")?; // Comma and newline before next item
185            }
186            w.write_str(&indent)?; // Indent
187            Self::write_value_pretty(val, w, new_depth)?; // Write the value
188            first = false;
189        }
190
191        write!(w, "\n{}", closing_indent)?; // Newline and closing indent
192        w.write_char(']') // Closing bracket
193    }
194
195    /// Helper to pretty-print a JSON object.
196    fn write_object_pretty<W: fmt::Write>(
197        obj: &HashMap<String, JsonValue>,
198        w: &mut W,
199        depth: usize,
200    ) -> fmt::Result {
201        // Empty object is just "{}"
202        if obj.is_empty() {
203            return w.write_str("{}");
204        }
205
206        let new_depth = depth + 1;
207        let indent = Self::INDENT.repeat(new_depth);
208        let closing_indent = Self::INDENT.repeat(depth);
209
210        w.write_str("{\n")?; // Opening brace and newline
211
212        let mut first = true;
213        for (key, val) in obj {
214            if !first {
215                w.write_str(",\n")?; // Comma and newline before next item
216            }
217            w.write_str(&indent)?; // Indent
218            Self::write_string(key, w)?; // Write the key
219            w.write_str(": ")?; // Colon and space
220            Self::write_value_pretty(val, w, new_depth)?; // Write the value
221            first = false;
222        }
223
224        write!(w, "\n{}", closing_indent)?; // Newline and closing indent
225        w.write_char('}') // Closing brace
226    }
227}