1use std::collections::BTreeMap;
2use std::fmt::{Display, Formatter};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum JsonValue {
6 Null,
7 Bool(bool),
8 Number(i64),
9 String(String),
10 Array(Vec<JsonValue>),
11 Object(BTreeMap<String, JsonValue>),
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct JsonError {
16 message: String,
17}
18
19impl JsonError {
20 #[must_use]
21 pub fn new(message: impl Into<String>) -> Self {
22 Self {
23 message: message.into(),
24 }
25 }
26}
27
28impl Display for JsonError {
29 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30 write!(f, "{}", self.message)
31 }
32}
33
34impl std::error::Error for JsonError {}
35
36impl JsonValue {
37 #[must_use]
38 pub fn render(&self) -> String {
39 match self {
40 Self::Null => "null".to_string(),
41 Self::Bool(value) => value.to_string(),
42 Self::Number(value) => value.to_string(),
43 Self::String(value) => render_string(value),
44 Self::Array(values) => {
45 let rendered = values
46 .iter()
47 .map(Self::render)
48 .collect::<Vec<_>>()
49 .join(",");
50 format!("[{rendered}]")
51 }
52 Self::Object(entries) => {
53 let rendered = entries
54 .iter()
55 .map(|(key, value)| format!("{}:{}", render_string(key), value.render()))
56 .collect::<Vec<_>>()
57 .join(",");
58 format!("{{{rendered}}}")
59 }
60 }
61 }
62
63 pub fn parse(source: &str) -> Result<Self, JsonError> {
64 let mut parser = Parser::new(source);
65 let value = parser.parse_value()?;
66 parser.skip_whitespace();
67 if parser.is_eof() {
68 Ok(value)
69 } else {
70 Err(JsonError::new("unexpected trailing content"))
71 }
72 }
73
74 #[must_use]
75 pub fn as_object(&self) -> Option<&BTreeMap<String, JsonValue>> {
76 match self {
77 Self::Object(value) => Some(value),
78 _ => None,
79 }
80 }
81
82 #[must_use]
83 pub fn as_array(&self) -> Option<&[JsonValue]> {
84 match self {
85 Self::Array(value) => Some(value),
86 _ => None,
87 }
88 }
89
90 #[must_use]
91 pub fn as_str(&self) -> Option<&str> {
92 match self {
93 Self::String(value) => Some(value),
94 _ => None,
95 }
96 }
97
98 #[must_use]
99 pub fn as_bool(&self) -> Option<bool> {
100 match self {
101 Self::Bool(value) => Some(*value),
102 _ => None,
103 }
104 }
105
106 #[must_use]
107 pub fn as_i64(&self) -> Option<i64> {
108 match self {
109 Self::Number(value) => Some(*value),
110 _ => None,
111 }
112 }
113}
114
115fn render_string(value: &str) -> String {
116 let mut rendered = String::with_capacity(value.len() + 2);
117 rendered.push('"');
118 for ch in value.chars() {
119 match ch {
120 '"' => rendered.push_str("\\\""),
121 '\\' => rendered.push_str("\\\\"),
122 '\n' => rendered.push_str("\\n"),
123 '\r' => rendered.push_str("\\r"),
124 '\t' => rendered.push_str("\\t"),
125 '\u{08}' => rendered.push_str("\\b"),
126 '\u{0C}' => rendered.push_str("\\f"),
127 control if control.is_control() => push_unicode_escape(&mut rendered, control),
128 plain => rendered.push(plain),
129 }
130 }
131 rendered.push('"');
132 rendered
133}
134
135fn push_unicode_escape(rendered: &mut String, control: char) {
136 const HEX: &[u8; 16] = b"0123456789abcdef";
137
138 rendered.push_str("\\u");
139 let value = u32::from(control);
140 for shift in [12_u32, 8, 4, 0] {
141 let nibble = ((value >> shift) & 0xF) as usize;
142 rendered.push(char::from(HEX[nibble]));
143 }
144}
145
146struct Parser<'a> {
147 chars: Vec<char>,
148 index: usize,
149 _source: &'a str,
150}
151
152impl<'a> Parser<'a> {
153 fn new(source: &'a str) -> Self {
154 Self {
155 chars: source.chars().collect(),
156 index: 0,
157 _source: source,
158 }
159 }
160
161 fn parse_value(&mut self) -> Result<JsonValue, JsonError> {
162 self.skip_whitespace();
163 match self.peek() {
164 Some('n') => self.parse_literal("null", JsonValue::Null),
165 Some('t') => self.parse_literal("true", JsonValue::Bool(true)),
166 Some('f') => self.parse_literal("false", JsonValue::Bool(false)),
167 Some('"') => self.parse_string().map(JsonValue::String),
168 Some('[') => self.parse_array(),
169 Some('{') => self.parse_object(),
170 Some('-' | '0'..='9') => self.parse_number().map(JsonValue::Number),
171 Some(other) => Err(JsonError::new(format!("unexpected character: {other}"))),
172 None => Err(JsonError::new("unexpected end of input")),
173 }
174 }
175
176 fn parse_literal(&mut self, expected: &str, value: JsonValue) -> Result<JsonValue, JsonError> {
177 for expected_char in expected.chars() {
178 if self.next() != Some(expected_char) {
179 return Err(JsonError::new(format!(
180 "invalid literal: expected {expected}"
181 )));
182 }
183 }
184 Ok(value)
185 }
186
187 fn parse_string(&mut self) -> Result<String, JsonError> {
188 self.expect('"')?;
189 let mut value = String::new();
190 while let Some(ch) = self.next() {
191 match ch {
192 '"' => return Ok(value),
193 '\\' => value.push(self.parse_escape()?),
194 plain => value.push(plain),
195 }
196 }
197 Err(JsonError::new("unterminated string"))
198 }
199
200 fn parse_escape(&mut self) -> Result<char, JsonError> {
201 match self.next() {
202 Some('"') => Ok('"'),
203 Some('\\') => Ok('\\'),
204 Some('/') => Ok('/'),
205 Some('b') => Ok('\u{08}'),
206 Some('f') => Ok('\u{0C}'),
207 Some('n') => Ok('\n'),
208 Some('r') => Ok('\r'),
209 Some('t') => Ok('\t'),
210 Some('u') => self.parse_unicode_escape(),
211 Some(other) => Err(JsonError::new(format!("invalid escape sequence: {other}"))),
212 None => Err(JsonError::new("unexpected end of input in escape sequence")),
213 }
214 }
215
216 fn parse_unicode_escape(&mut self) -> Result<char, JsonError> {
217 let mut value = 0_u32;
218 for _ in 0..4 {
219 let Some(ch) = self.next() else {
220 return Err(JsonError::new("unexpected end of input in unicode escape"));
221 };
222 value = (value << 4)
223 | ch.to_digit(16)
224 .ok_or_else(|| JsonError::new("invalid unicode escape"))?;
225 }
226 char::from_u32(value).ok_or_else(|| JsonError::new("invalid unicode scalar value"))
227 }
228
229 fn parse_array(&mut self) -> Result<JsonValue, JsonError> {
230 self.expect('[')?;
231 let mut values = Vec::new();
232 loop {
233 self.skip_whitespace();
234 if self.try_consume(']') {
235 break;
236 }
237 values.push(self.parse_value()?);
238 self.skip_whitespace();
239 if self.try_consume(']') {
240 break;
241 }
242 self.expect(',')?;
243 }
244 Ok(JsonValue::Array(values))
245 }
246
247 fn parse_object(&mut self) -> Result<JsonValue, JsonError> {
248 self.expect('{')?;
249 let mut entries = BTreeMap::new();
250 loop {
251 self.skip_whitespace();
252 if self.try_consume('}') {
253 break;
254 }
255 let key = self.parse_string()?;
256 self.skip_whitespace();
257 self.expect(':')?;
258 let value = self.parse_value()?;
259 entries.insert(key, value);
260 self.skip_whitespace();
261 if self.try_consume('}') {
262 break;
263 }
264 self.expect(',')?;
265 }
266 Ok(JsonValue::Object(entries))
267 }
268
269 fn parse_number(&mut self) -> Result<i64, JsonError> {
270 let mut value = String::new();
271 if self.try_consume('-') {
272 value.push('-');
273 }
274
275 while let Some(ch @ '0'..='9') = self.peek() {
276 value.push(ch);
277 self.index += 1;
278 }
279
280 if value.is_empty() || value == "-" {
281 return Err(JsonError::new("invalid number"));
282 }
283
284 value
285 .parse::<i64>()
286 .map_err(|_| JsonError::new("number out of range"))
287 }
288
289 fn expect(&mut self, expected: char) -> Result<(), JsonError> {
290 match self.next() {
291 Some(actual) if actual == expected => Ok(()),
292 Some(actual) => Err(JsonError::new(format!(
293 "expected '{expected}', found '{actual}'"
294 ))),
295 None => Err(JsonError::new(format!(
296 "expected '{expected}', found end of input"
297 ))),
298 }
299 }
300
301 fn try_consume(&mut self, expected: char) -> bool {
302 if self.peek() == Some(expected) {
303 self.index += 1;
304 true
305 } else {
306 false
307 }
308 }
309
310 fn skip_whitespace(&mut self) {
311 while matches!(self.peek(), Some(' ' | '\n' | '\r' | '\t')) {
312 self.index += 1;
313 }
314 }
315
316 fn peek(&self) -> Option<char> {
317 self.chars.get(self.index).copied()
318 }
319
320 fn next(&mut self) -> Option<char> {
321 let ch = self.peek()?;
322 self.index += 1;
323 Some(ch)
324 }
325
326 fn is_eof(&self) -> bool {
327 self.index >= self.chars.len()
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::{render_string, JsonValue};
334 use std::collections::BTreeMap;
335
336 #[test]
337 fn renders_and_parses_json_values() {
338 let mut object = BTreeMap::new();
339 object.insert("flag".to_string(), JsonValue::Bool(true));
340 object.insert(
341 "items".to_string(),
342 JsonValue::Array(vec![
343 JsonValue::Number(4),
344 JsonValue::String("ok".to_string()),
345 ]),
346 );
347
348 let rendered = JsonValue::Object(object).render();
349 let parsed = JsonValue::parse(&rendered).expect("json should parse");
350
351 assert_eq!(parsed.as_object().expect("object").len(), 2);
352 }
353
354 #[test]
355 fn escapes_control_characters() {
356 assert_eq!(render_string("a\n\t\"b"), "\"a\\n\\t\\\"b\"");
357 }
358
359 #[test]
360 fn renders_all_escape_sequences() {
361 assert_eq!(render_string("\r"), "\"\\r\"");
362 assert_eq!(render_string("\u{08}"), "\"\\b\"");
363 assert_eq!(render_string("\u{0C}"), "\"\\f\"");
364 assert_eq!(render_string("\\"), "\"\\\\\"");
365 assert!(render_string("\u{01}").contains("\\u0001"));
366 }
367
368 #[test]
369 fn renders_primitive_types() {
370 assert_eq!(JsonValue::Null.render(), "null");
371 assert_eq!(JsonValue::Bool(true).render(), "true");
372 assert_eq!(JsonValue::Bool(false).render(), "false");
373 assert_eq!(JsonValue::Number(42).render(), "42");
374 assert_eq!(JsonValue::Number(-7).render(), "-7");
375 assert_eq!(JsonValue::String("hi".into()).render(), "\"hi\"");
376 assert_eq!(JsonValue::Array(vec![]).render(), "[]");
377 }
378
379 #[test]
380 fn as_accessors_return_none_for_wrong_types() {
381 let null = JsonValue::Null;
382 assert!(null.as_object().is_none());
383 assert!(null.as_array().is_none());
384 assert!(null.as_str().is_none());
385 assert!(null.as_bool().is_none());
386 assert!(null.as_i64().is_none());
387
388 let num = JsonValue::Number(5);
389 assert!(num.as_str().is_none());
390 assert!(num.as_bool().is_none());
391 assert!(num.as_object().is_none());
392 assert!(num.as_array().is_none());
393 assert_eq!(num.as_i64(), Some(5));
394
395 let b = JsonValue::Bool(true);
396 assert_eq!(b.as_bool(), Some(true));
397 assert!(b.as_i64().is_none());
398 }
399
400 #[test]
401 fn parses_null_true_false_literals() {
402 assert_eq!(JsonValue::parse("null").unwrap(), JsonValue::Null);
403 assert_eq!(JsonValue::parse("true").unwrap(), JsonValue::Bool(true));
404 assert_eq!(JsonValue::parse("false").unwrap(), JsonValue::Bool(false));
405 }
406
407 #[test]
408 fn parses_numbers_including_negative() {
409 assert_eq!(JsonValue::parse("0").unwrap(), JsonValue::Number(0));
410 assert_eq!(JsonValue::parse("-42").unwrap(), JsonValue::Number(-42));
411 assert_eq!(JsonValue::parse("12345").unwrap(), JsonValue::Number(12345));
412 }
413
414 #[test]
415 fn parses_string_escapes() {
416 let parsed = JsonValue::parse(r#""a\nb\t\\\/\"\u0041""#).unwrap();
417 assert_eq!(parsed.as_str().unwrap(), "a\nb\t\\/\"A");
418 }
419
420 #[test]
421 fn parses_arrays_and_nested_objects() {
422 let parsed = JsonValue::parse(r#"[1, "two", [3], {"k": null}]"#).unwrap();
423 let arr = parsed.as_array().unwrap();
424 assert_eq!(arr.len(), 4);
425 assert_eq!(arr[0].as_i64(), Some(1));
426 assert_eq!(arr[2].as_array().unwrap().len(), 1);
427 }
428
429 #[test]
430 fn rejects_trailing_content() {
431 let err = JsonValue::parse("null extra").unwrap_err();
432 assert!(err.to_string().contains("trailing"));
433 }
434
435 #[test]
436 fn rejects_invalid_literal() {
437 assert!(JsonValue::parse("nul").is_err());
438 assert!(JsonValue::parse("tru").is_err());
439 assert!(JsonValue::parse("fals").is_err());
440 }
441
442 #[test]
443 fn rejects_unexpected_character() {
444 let err = JsonValue::parse("@").unwrap_err();
445 assert!(err.to_string().contains("unexpected character"));
446 }
447
448 #[test]
449 fn rejects_empty_input() {
450 let err = JsonValue::parse("").unwrap_err();
451 assert!(err.to_string().contains("end of input"));
452 }
453
454 #[test]
455 fn rejects_unterminated_string() {
456 let err = JsonValue::parse(r#""no end"#).unwrap_err();
457 assert!(err.to_string().contains("unterminated"));
458 }
459
460 #[test]
461 fn rejects_invalid_escape() {
462 let err = JsonValue::parse(r#""\x""#).unwrap_err();
463 assert!(err.to_string().contains("invalid escape"));
464 }
465
466 #[test]
467 fn rejects_truncated_unicode_escape() {
468 let err = JsonValue::parse(r#""\u00""#).unwrap_err();
469 assert!(err.to_string().contains("unicode escape"));
470 }
471
472 #[test]
473 fn rejects_bad_unicode_hex() {
474 let err = JsonValue::parse(r#""\u00GG""#).unwrap_err();
475 assert!(err.to_string().contains("unicode"));
476 }
477
478 #[test]
479 fn rejects_bare_minus() {
480 let err = JsonValue::parse("-").unwrap_err();
481 assert!(err.to_string().contains("invalid number"));
482 }
483
484 #[test]
485 fn rejects_number_overflow() {
486 let err = JsonValue::parse("99999999999999999999").unwrap_err();
487 assert!(err.to_string().contains("out of range"));
488 }
489
490 #[test]
491 fn rejects_bad_array_separator() {
492 let err = JsonValue::parse("[1 2]").unwrap_err();
493 assert!(err.to_string().contains("expected"));
494 }
495
496 #[test]
497 fn rejects_bad_object_separator() {
498 let err = JsonValue::parse(r#"{"a":1 "b":2}"#).unwrap_err();
499 assert!(err.to_string().contains("expected"));
500 }
501
502 #[test]
503 fn json_error_display() {
504 let err = super::JsonError::new("test error");
505 assert_eq!(err.to_string(), "test error");
506 }
507}