use std::io::{self, Read}; #[derive(Debug, PartialEq)] enum Json { Null, Bool(bool), Number(String), String(String), Array(Vec), Object(Vec<(String, Json)>), } struct Parser<'a> { input: &'a str, pos: usize, } impl<'a> Parser<'a> { fn new(input: &'a str) -> Self { Self { input, pos: 0 } } fn parse(mut self) -> Result { let value = self.value()?; self.whitespace(); if self.pos != self.input.len() { return Err(self.error("unexpected trailing characters")); } Ok(value) } fn value(&mut self) -> Result { self.whitespace(); match self.peek() { Some(b'n') => { self.keyword("null")?; Ok(Json::Null) } Some(b't') => { self.keyword("true")?; Ok(Json::Bool(true)) } Some(b'f') => { self.keyword("false")?; Ok(Json::Bool(false)) } Some(b'"') => Ok(Json::String(self.string()?)), Some(b'[') => self.array(), Some(b'{') => self.object(), Some(b'-' | b'0'..=b'9') => Ok(Json::Number(self.number()?)), Some(_) => Err(self.error("expected a JSON value")), None => Err(self.error("unexpected end of input")), } } fn array(&mut self) -> Result { self.expect(b'[')?; self.whitespace(); let mut values = Vec::new(); if self.consume(b']') { return Ok(Json::Array(values)); } loop { values.push(self.value()?); self.whitespace(); if self.consume(b']') { break; } self.expect(b',')?; } Ok(Json::Array(values)) } fn object(&mut self) -> Result { self.expect(b'{')?; self.whitespace(); let mut entries = Vec::new(); if self.consume(b'}') { return Ok(Json::Object(entries)); } loop { self.whitespace(); if self.peek() != Some(b'"') { return Err(self.error("expected an object key")); } let key = self.string()?; self.whitespace(); self.expect(b':')?; entries.push((key, self.value()?)); self.whitespace(); if self.consume(b'}') { break; } self.expect(b',')?; } Ok(Json::Object(entries)) } // Strings are kept in their original escaped form. fn string(&mut self) -> Result { let start = self.pos; self.expect(b'"')?; loop { match self.next() { Some(b'"') => return Ok(self.input[start..self.pos].to_owned()), Some(b'\\') => match self.next() { Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => {} Some(b'u') => { for _ in 0..4 { match self.next() { Some(b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F') => {} _ => return Err(self.error("invalid Unicode escape")), } } } _ => return Err(self.error("invalid escape sequence")), }, Some(0x00..=0x1f) => { return Err(self.error("unescaped control character in string")) } Some(_) => {} None => return Err(self.error("unterminated string")), } } } fn number(&mut self) -> Result { let start = self.pos; self.consume(b'-'); if self.consume(b'0') { if matches!(self.peek(), Some(b'0'..=b'9')) { return Err(self.error("leading zero in number")); } } else { self.digits()?; } if self.consume(b'.') { self.digits()?; } if matches!(self.peek(), Some(b'e' | b'E')) { self.pos += 1; if matches!(self.peek(), Some(b'+' | b'-')) { self.pos += 1; } self.digits()?; } Ok(self.input[start..self.pos].to_owned()) } fn digits(&mut self) -> Result<(), String> { let start = self.pos; while matches!(self.peek(), Some(b'0'..=b'9')) { self.pos += 1; } if self.pos == start { Err(self.error("expected a digit")) } else { Ok(()) } } fn keyword(&mut self, word: &str) -> Result<(), String> { if self.input[self.pos..].starts_with(word) { self.pos += word.len(); Ok(()) } else { Err(self.error("invalid keyword")) } } fn whitespace(&mut self) { while matches!(self.peek(), Some(b' ' | b'\n' | b'\r' | b'\t')) { self.pos += 1; } } fn expect(&mut self, byte: u8) -> Result<(), String> { if self.consume(byte) { Ok(()) } else { Err(self.error(&format!("expected '{}'", byte as char))) } } fn consume(&mut self, byte: u8) -> bool { if self.peek() == Some(byte) { self.pos += 1; true } else { false } } fn peek(&self) -> Option { self.input.as_bytes().get(self.pos).copied() } fn next(&mut self) -> Option { let byte = self.peek()?; self.pos += 1; Some(byte) } fn error(&self, message: &str) -> String { format!("{message} at byte {}", self.pos) } } fn write_pretty(value: &Json, output: &mut String, depth: usize) { match value { Json::Null => output.push_str("null"), Json::Bool(value) => output.push_str(if *value { "true" } else { "false" }), Json::Number(value) | Json::String(value) => output.push_str(value), Json::Array(values) => { if values.is_empty() { output.push_str("[]"); return; } output.push_str("[\n"); for (index, value) in values.iter().enumerate() { indent(output, depth + 1); write_pretty(value, output, depth + 1); if index + 1 != values.len() { output.push(','); } output.push('\n'); } indent(output, depth); output.push(']'); } Json::Object(entries) => { if entries.is_empty() { output.push_str("{}"); return; } output.push_str("{\n"); for (index, (key, value)) in entries.iter().enumerate() { indent(output, depth + 1); output.push_str(key); output.push_str(": "); write_pretty(value, output, depth + 1); if index + 1 != entries.len() { output.push(','); } output.push('\n'); } indent(output, depth); output.push('}'); } } } fn indent(output: &mut String, depth: usize) { for _ in 0..depth { output.push_str(" "); } } fn pretty_print(input: &str) -> Result { let value = Parser::new(input).parse()?; let mut output = String::new(); write_pretty(&value, &mut output, 0); Ok(output) } fn main() { let mut input = String::new(); if let Err(error) = io::stdin().read_to_string(&mut input) { eprintln!("failed to read stdin: {error}"); std::process::exit(1); } match pretty_print(&input) { Ok(output) => println!("{output}"), Err(error) => { eprintln!("invalid JSON: {error}"); std::process::exit(1); } } } #[cfg(test)] mod tests { use super::*; #[test] fn formats_nested_json() { let input = r#"{"name":"Ada","values":[1,true,null],"empty":{}}"#; let expected = r#"{ "name": "Ada", "values": [ 1, true, null ], "empty": {} }"#; assert_eq!(pretty_print(input).unwrap(), expected); } #[test] fn rejects_invalid_json() { assert!(pretty_print(r#"{"answer": 42,}"#).is_err()); assert!(pretty_print("01").is_err()); } }