1#[derive(Debug, Clone, PartialEq)]
21pub enum JsonValue {
22 Null,
23 Bool(bool),
24 Number {
27 as_f64: f64,
28 as_i64: Option<i64>,
29 },
30 String(String),
31 Array(Vec<JsonValue>),
32 Object(Vec<(String, JsonValue)>),
34}
35
36impl JsonValue {
37 pub fn parse(text: &str) -> Result<JsonValue, String> {
39 let chars: Vec<char> = text.chars().collect();
40 let mut p = Parser { chars, pos: 0 };
41 p.skip_ws();
42 let v = p.parse_value()?;
43 p.skip_ws();
44 if p.pos != p.chars.len() {
45 return Err(format!("trailing characters at position {}", p.pos));
46 }
47 Ok(v)
48 }
49
50 pub fn is_object(&self) -> bool {
52 matches!(self, JsonValue::Object(_))
53 }
54
55 pub fn as_object(&self) -> Option<&[(String, JsonValue)]> {
57 match self {
58 JsonValue::Object(m) => Some(m),
59 _ => None,
60 }
61 }
62
63 pub fn as_array(&self) -> Option<&[JsonValue]> {
65 match self {
66 JsonValue::Array(a) => Some(a),
67 _ => None,
68 }
69 }
70
71 pub fn as_str(&self) -> Option<&str> {
73 match self {
74 JsonValue::String(s) => Some(s),
75 _ => None,
76 }
77 }
78
79 pub fn as_bool(&self) -> Option<bool> {
81 match self {
82 JsonValue::Bool(b) => Some(*b),
83 _ => None,
84 }
85 }
86
87 pub fn get(&self, key: &str) -> Option<&JsonValue> {
89 match self {
90 JsonValue::Object(m) => m.iter().find(|(k, _)| k == key).map(|(_, v)| v),
91 _ => None,
92 }
93 }
94
95 pub fn contains_key(&self, key: &str) -> bool {
97 self.get(key).is_some()
98 }
99}
100
101struct Parser {
102 chars: Vec<char>,
103 pos: usize,
104}
105
106impl Parser {
107 fn skip_ws(&mut self) {
108 while self.pos < self.chars.len() {
109 match self.chars[self.pos] {
110 ' ' | '\t' | '\n' | '\r' => self.pos += 1,
111 _ => break,
112 }
113 }
114 }
115
116 fn peek(&self) -> Option<char> {
117 self.chars.get(self.pos).copied()
118 }
119
120 fn parse_value(&mut self) -> Result<JsonValue, String> {
121 self.skip_ws();
122 match self.peek() {
123 Some('{') => self.parse_object(),
124 Some('[') => self.parse_array(),
125 Some('"') => Ok(JsonValue::String(self.parse_string()?)),
126 Some('t') | Some('f') => self.parse_bool(),
127 Some('n') => self.parse_null(),
128 Some(c) if c == '-' || c.is_ascii_digit() => self.parse_number(),
129 Some(c) => Err(format!("unexpected character '{c}' at {}", self.pos)),
130 None => Err("unexpected end of input".to_string()),
131 }
132 }
133
134 fn expect(&mut self, c: char) -> Result<(), String> {
135 if self.peek() == Some(c) {
136 self.pos += 1;
137 Ok(())
138 } else {
139 Err(format!("expected '{c}' at position {}", self.pos))
140 }
141 }
142
143 fn parse_object(&mut self) -> Result<JsonValue, String> {
144 self.expect('{')?;
145 let mut entries: Vec<(String, JsonValue)> = Vec::new();
146 self.skip_ws();
147 if self.peek() == Some('}') {
148 self.pos += 1;
149 return Ok(JsonValue::Object(entries));
150 }
151 loop {
152 self.skip_ws();
153 let key = self.parse_string()?;
154 self.skip_ws();
155 self.expect(':')?;
156 let value = self.parse_value()?;
157 if let Some(slot) = entries.iter_mut().find(|(k, _)| *k == key) {
159 slot.1 = value;
160 } else {
161 entries.push((key, value));
162 }
163 self.skip_ws();
164 match self.peek() {
165 Some(',') => self.pos += 1,
166 Some('}') => {
167 self.pos += 1;
168 break;
169 }
170 _ => return Err(format!("expected ',' or '}}' at {}", self.pos)),
171 }
172 }
173 Ok(JsonValue::Object(entries))
174 }
175
176 fn parse_array(&mut self) -> Result<JsonValue, String> {
177 self.expect('[')?;
178 let mut items = Vec::new();
179 self.skip_ws();
180 if self.peek() == Some(']') {
181 self.pos += 1;
182 return Ok(JsonValue::Array(items));
183 }
184 loop {
185 items.push(self.parse_value()?);
186 self.skip_ws();
187 match self.peek() {
188 Some(',') => self.pos += 1,
189 Some(']') => {
190 self.pos += 1;
191 break;
192 }
193 _ => return Err(format!("expected ',' or ']' at {}", self.pos)),
194 }
195 }
196 Ok(JsonValue::Array(items))
197 }
198
199 fn parse_string(&mut self) -> Result<String, String> {
200 self.expect('"')?;
201 let mut s = String::new();
202 loop {
203 match self.peek() {
204 None => return Err("unterminated string".to_string()),
205 Some('"') => {
206 self.pos += 1;
207 break;
208 }
209 Some('\\') => {
210 self.pos += 1;
211 let esc = self.peek().ok_or("dangling escape")?;
212 self.pos += 1;
213 match esc {
214 '"' => s.push('"'),
215 '\\' => s.push('\\'),
216 '/' => s.push('/'),
217 'b' => s.push('\u{0008}'),
218 'f' => s.push('\u{000C}'),
219 'n' => s.push('\n'),
220 'r' => s.push('\r'),
221 't' => s.push('\t'),
222 'u' => {
223 let cp = self.parse_hex4()?;
224 if (0xD800..=0xDBFF).contains(&cp) {
225 if self.peek() == Some('\\') {
227 self.pos += 1;
228 self.expect('u')?;
229 let lo = self.parse_hex4()?;
230 let c = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
231 s.push(char::from_u32(c).ok_or("invalid surrogate pair")?);
232 } else {
233 return Err("lone high surrogate".to_string());
234 }
235 } else {
236 s.push(char::from_u32(cp).ok_or("invalid \\u escape")?);
237 }
238 }
239 other => return Err(format!("bad escape '\\{other}'")),
240 }
241 }
242 Some(c) => {
243 self.pos += 1;
244 s.push(c);
245 }
246 }
247 }
248 Ok(s)
249 }
250
251 fn parse_hex4(&mut self) -> Result<u32, String> {
252 let mut v = 0u32;
253 for _ in 0..4 {
254 let c = self.peek().ok_or("short \\u escape")?;
255 let d = c.to_digit(16).ok_or("bad hex digit")?;
256 v = v * 16 + d;
257 self.pos += 1;
258 }
259 Ok(v)
260 }
261
262 fn parse_bool(&mut self) -> Result<JsonValue, String> {
263 if self.matches_literal("true") {
264 Ok(JsonValue::Bool(true))
265 } else if self.matches_literal("false") {
266 Ok(JsonValue::Bool(false))
267 } else {
268 Err(format!("invalid literal at {}", self.pos))
269 }
270 }
271
272 fn parse_null(&mut self) -> Result<JsonValue, String> {
273 if self.matches_literal("null") {
274 Ok(JsonValue::Null)
275 } else {
276 Err(format!("invalid literal at {}", self.pos))
277 }
278 }
279
280 fn matches_literal(&mut self, lit: &str) -> bool {
281 let lit_chars: Vec<char> = lit.chars().collect();
282 if self.pos + lit_chars.len() > self.chars.len() {
283 return false;
284 }
285 if self.chars[self.pos..self.pos + lit_chars.len()] == lit_chars[..] {
286 self.pos += lit_chars.len();
287 true
288 } else {
289 false
290 }
291 }
292
293 fn parse_number(&mut self) -> Result<JsonValue, String> {
294 let start = self.pos;
295 let mut is_int = true;
296 if self.peek() == Some('-') {
297 self.pos += 1;
298 }
299 while let Some(c) = self.peek() {
300 match c {
301 '0'..='9' => self.pos += 1,
302 '.' | 'e' | 'E' | '+' | '-' => {
303 is_int = false;
304 self.pos += 1;
305 }
306 _ => break,
307 }
308 }
309 let text: String = self.chars[start..self.pos].iter().collect();
310 let as_f64: f64 = text
311 .parse()
312 .map_err(|_| format!("invalid number '{text}'"))?;
313 let as_i64 = if is_int {
314 text.parse::<i64>().ok()
315 } else {
316 None
317 };
318 Ok(JsonValue::Number { as_f64, as_i64 })
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn parses_ordered_object() {
328 let v = JsonValue::parse(r#"{"b":1,"a":2,"c":3}"#).unwrap();
329 let keys: Vec<&str> = v
330 .as_object()
331 .unwrap()
332 .iter()
333 .map(|(k, _)| k.as_str())
334 .collect();
335 assert_eq!(keys, ["b", "a", "c"]);
336 }
337
338 #[test]
339 fn parses_scalars_and_nesting() {
340 let v = JsonValue::parse(r#"{"x":[true,null,-3.5,"hi"]}"#).unwrap();
341 let arr = v.get("x").unwrap().as_array().unwrap();
342 assert_eq!(arr[0], JsonValue::Bool(true));
343 assert_eq!(arr[1], JsonValue::Null);
344 assert_eq!(arr[3].as_str(), Some("hi"));
345 }
346
347 #[test]
348 fn integer_flag_is_exact() {
349 let v = JsonValue::parse("42").unwrap();
350 match v {
351 JsonValue::Number { as_i64, .. } => assert_eq!(as_i64, Some(42)),
352 _ => panic!("expected number"),
353 }
354 let f = JsonValue::parse("42.0").unwrap();
355 match f {
356 JsonValue::Number { as_i64, .. } => assert_eq!(as_i64, None),
357 _ => panic!("expected number"),
358 }
359 }
360
361 #[test]
362 fn rejects_trailing_junk() {
363 assert!(JsonValue::parse("{} extra").is_err());
364 assert!(JsonValue::parse("{").is_err());
365 }
366
367 #[test]
368 fn parses_escapes() {
369 let v = JsonValue::parse(r#""a\nbA""#).unwrap();
370 assert_eq!(v.as_str(), Some("a\nbA"));
371 }
372}