1#[derive(Debug, Clone, PartialEq)]
7pub struct J {
8 pub line: usize,
9 pub v: V,
10}
11
12#[derive(Debug, Clone, PartialEq)]
13pub enum V {
14 Null,
15 Bool(bool),
16 Num(f64),
17 Str(String),
18 Arr(Vec<J>),
19 Obj(Vec<(String, J)>),
21}
22
23impl J {
24 pub fn get(&self, key: &str) -> Option<&J> {
25 match &self.v {
26 V::Obj(members) => members.iter().find(|(k, _)| k == key).map(|(_, v)| v),
27 _ => None,
28 }
29 }
30
31 pub fn members(&self) -> &[(String, J)] {
32 match &self.v {
33 V::Obj(m) => m,
34 _ => &[],
35 }
36 }
37
38 pub fn items(&self) -> &[J] {
39 match &self.v {
40 V::Arr(a) => a,
41 _ => &[],
42 }
43 }
44
45 pub fn str(&self) -> Option<&str> {
46 match &self.v {
47 V::Str(s) => Some(s),
48 _ => None,
49 }
50 }
51
52 pub fn num(&self) -> Option<f64> {
53 match self.v {
54 V::Num(n) => Some(n),
55 _ => None,
56 }
57 }
58
59 pub fn bool(&self) -> Option<bool> {
60 match self.v {
61 V::Bool(b) => Some(b),
62 _ => None,
63 }
64 }
65
66 pub fn is_obj(&self) -> bool {
67 matches!(self.v, V::Obj(_))
68 }
69
70 pub fn is_arr(&self) -> bool {
71 matches!(self.v, V::Arr(_))
72 }
73
74 pub fn kind(&self) -> &'static str {
76 match self.v {
77 V::Null => "null",
78 V::Bool(_) => "a boolean",
79 V::Num(_) => "a number",
80 V::Str(_) => "a string",
81 V::Arr(_) => "an array",
82 V::Obj(_) => "an object",
83 }
84 }
85
86 #[cfg(test)]
88 pub fn to_value(&self) -> serde_json::Value {
89 use serde_json::Value;
90 match &self.v {
91 V::Null => Value::Null,
92 V::Bool(b) => Value::Bool(*b),
93 V::Num(n) => num_value(*n),
94 V::Str(s) => Value::String(s.clone()),
95 V::Arr(a) => Value::Array(a.iter().map(J::to_value).collect()),
96 V::Obj(m) => Value::Object(m.iter().map(|(k, v)| (k.clone(), v.to_value())).collect()),
97 }
98 }
99}
100
101pub fn num_value(n: f64) -> serde_json::Value {
103 if n.fract() == 0.0 && n.abs() < 1e15 {
104 serde_json::Value::from(n as i64)
105 } else {
106 serde_json::Number::from_f64(n)
107 .map(serde_json::Value::Number)
108 .unwrap_or(serde_json::Value::Null)
109 }
110}
111
112impl J {
114 pub fn of(v: V) -> J {
115 J { line: 0, v }
116 }
117
118 pub fn obj<K: Into<String>>(members: impl IntoIterator<Item = (K, J)>) -> J {
119 J::of(V::Obj(
120 members.into_iter().map(|(k, v)| (k.into(), v)).collect(),
121 ))
122 }
123
124 pub fn number(n: f64) -> J {
125 J::of(V::Num(n))
126 }
127
128 pub fn string(s: impl Into<String>) -> J {
129 J::of(V::Str(s.into()))
130 }
131
132 pub fn nums(ns: &[f64]) -> J {
133 J::of(V::Arr(ns.iter().map(|n| J::number(*n)).collect()))
134 }
135
136 pub fn from_value(v: &serde_json::Value) -> J {
138 use serde_json::Value;
139 J::of(match v {
140 Value::Null => V::Null,
141 Value::Bool(b) => V::Bool(*b),
142 Value::Number(n) => V::Num(n.as_f64().unwrap_or(0.0)),
143 Value::String(s) => V::Str(s.clone()),
144 Value::Array(a) => V::Arr(a.iter().map(J::from_value).collect()),
145 Value::Object(m) => V::Obj(
146 m.iter()
147 .map(|(k, v)| (k.clone(), J::from_value(v)))
148 .collect(),
149 ),
150 })
151 }
152
153 pub fn set(&mut self, key: &str, value: J) {
155 if let V::Obj(m) = &mut self.v {
156 match m.iter_mut().find(|(k, _)| k == key) {
157 Some((_, v)) => *v = value,
158 None => m.push((key.to_string(), value)),
159 }
160 }
161 }
162}
163
164pub fn to_pretty(j: &J) -> String {
167 fn scalar(j: &J) -> Option<String> {
168 Some(match &j.v {
169 V::Null => "null".into(),
170 V::Bool(b) => b.to_string(),
171 V::Num(n) => num_value(*n).to_string(),
172 V::Str(s) => serde_json::Value::String(s.clone()).to_string(),
173 _ => return None,
174 })
175 }
176 fn inline(j: &J) -> Option<String> {
177 if let Some(s) = scalar(j) {
178 return Some(s);
179 }
180 let s = match &j.v {
181 V::Arr(a)
182 if a.iter()
183 .all(|i| matches!(i.v, V::Num(_) | V::Bool(_) | V::Null)) =>
184 {
185 format!(
186 "[{}]",
187 a.iter().filter_map(scalar).collect::<Vec<_>>().join(", ")
188 )
189 }
190 V::Obj(m)
191 if !m.is_empty()
192 && m.iter()
193 .all(|(_, v)| inline(v).is_some() && !matches!(v.v, V::Obj(_))) =>
194 {
195 let parts: Vec<String> = m
196 .iter()
197 .filter_map(|(k, v)| {
198 Some(format!(
199 "{}: {}",
200 serde_json::Value::String(k.clone()),
201 inline(v)?
202 ))
203 })
204 .collect();
205 format!("{{ {} }}", parts.join(", "))
206 }
207 V::Obj(m) if m.is_empty() => "{}".into(),
208 V::Arr(a) if a.is_empty() => "[]".into(),
209 _ => return None,
210 };
211 (s.len() <= 72).then_some(s)
212 }
213 fn write(j: &J, indent: usize, out: &mut String) {
214 if let Some(s) = inline(j) {
215 out.push_str(&s);
216 return;
217 }
218 let pad = " ".repeat(indent + 1);
219 match &j.v {
220 V::Arr(a) => {
221 out.push_str("[\n");
222 for (i, item) in a.iter().enumerate() {
223 out.push_str(&pad);
224 write(item, indent + 1, out);
225 out.push_str(if i + 1 < a.len() { ",\n" } else { "\n" });
226 }
227 out.push_str(&" ".repeat(indent));
228 out.push(']');
229 }
230 V::Obj(m) => {
231 out.push_str("{\n");
232 for (i, (k, v)) in m.iter().enumerate() {
233 out.push_str(&pad);
234 out.push_str(&serde_json::Value::String(k.clone()).to_string());
235 out.push_str(": ");
236 write(v, indent + 1, out);
237 out.push_str(if i + 1 < m.len() { ",\n" } else { "\n" });
238 }
239 out.push_str(&" ".repeat(indent));
240 out.push('}');
241 }
242 _ => {}
243 }
244 }
245 let mut out = String::new();
246 write(j, 0, &mut out);
247 out.push('\n');
248 out
249}
250
251pub fn parse(text: &str) -> Result<J, String> {
253 let mut p = P {
254 s: text.as_bytes(),
255 i: 0,
256 line: 1,
257 };
258 if p.s.starts_with(b"\xEF\xBB\xBF") {
259 p.i = 3;
260 }
261 p.ws();
262 let v = p.value(0)?;
263 p.ws();
264 if p.i < p.s.len() {
265 return Err(p.err("unexpected text after the JSON value"));
266 }
267 Ok(v)
268}
269
270struct P<'a> {
271 s: &'a [u8],
272 i: usize,
273 line: usize,
274}
275
276impl P<'_> {
277 fn err(&self, msg: &str) -> String {
278 format!("line {}: {msg}", self.line)
279 }
280
281 fn peek(&self) -> Option<u8> {
282 self.s.get(self.i).copied()
283 }
284
285 fn ws(&mut self) {
286 while let Some(c) = self.peek() {
287 match c {
288 b'\n' => {
289 self.line += 1;
290 self.i += 1;
291 }
292 b' ' | b'\t' | b'\r' => self.i += 1,
293 b'/' if self.s.get(self.i + 1) == Some(&b'/') => {
295 while self.peek().is_some_and(|c| c != b'\n') {
296 self.i += 1;
297 }
298 }
299 _ => break,
300 }
301 }
302 }
303
304 fn eat(&mut self, c: u8) -> bool {
305 if self.peek() == Some(c) {
306 self.i += 1;
307 true
308 } else {
309 false
310 }
311 }
312
313 fn value(&mut self, depth: usize) -> Result<J, String> {
314 if depth > 64 {
315 return Err(self.err("nested too deeply"));
316 }
317 let line = self.line;
318 let v = match self.peek() {
319 Some(b'{') => {
320 self.i += 1;
321 let mut members: Vec<(String, J)> = Vec::new();
322 self.ws();
323 if !self.eat(b'}') {
324 loop {
325 self.ws();
326 let key_line = self.line;
327 if self.peek() != Some(b'"') {
328 return Err(self.err("expected a \"key\""));
329 }
330 let key = self.string()?;
331 if members.iter().any(|(k, _)| *k == key) {
332 return Err(format!("line {key_line}: duplicate key {key:?}"));
333 }
334 self.ws();
335 if !self.eat(b':') {
336 return Err(self.err("expected `:` after the key"));
337 }
338 self.ws();
339 let mut v = self.value(depth + 1)?;
340 v.line = key_line;
342 members.push((key, v));
343 self.ws();
344 if self.eat(b',') {
345 self.ws();
346 if self.eat(b'}') {
348 break;
349 }
350 continue;
351 }
352 if self.eat(b'}') {
353 break;
354 }
355 return Err(self.err("expected `,` or `}`"));
356 }
357 }
358 V::Obj(members)
359 }
360 Some(b'[') => {
361 self.i += 1;
362 let mut items = Vec::new();
363 self.ws();
364 if !self.eat(b']') {
365 loop {
366 self.ws();
367 items.push(self.value(depth + 1)?);
368 self.ws();
369 if self.eat(b',') {
370 self.ws();
371 if self.eat(b']') {
372 break;
373 }
374 continue;
375 }
376 if self.eat(b']') {
377 break;
378 }
379 return Err(self.err("expected `,` or `]`"));
380 }
381 }
382 V::Arr(items)
383 }
384 Some(b'"') => V::Str(self.string()?),
385 Some(b't') if self.s[self.i..].starts_with(b"true") => {
386 self.i += 4;
387 V::Bool(true)
388 }
389 Some(b'f') if self.s[self.i..].starts_with(b"false") => {
390 self.i += 5;
391 V::Bool(false)
392 }
393 Some(b'n') if self.s[self.i..].starts_with(b"null") => {
394 self.i += 4;
395 V::Null
396 }
397 Some(c) if c == b'-' || c.is_ascii_digit() => {
398 let start = self.i;
399 self.i += 1;
400 while self.peek().is_some_and(|c| {
401 c.is_ascii_digit() || matches!(c, b'.' | b'e' | b'E' | b'+' | b'-')
402 }) {
403 self.i += 1;
404 }
405 let lit = std::str::from_utf8(&self.s[start..self.i]).unwrap_or("");
406 V::Num(
407 lit.parse()
408 .map_err(|_| self.err(&format!("`{lit}` isn't a number")))?,
409 )
410 }
411 None => return Err(self.err("unexpected end of file")),
412 Some(_) => return Err(self.err("expected a JSON value")),
413 };
414 Ok(J { line, v })
415 }
416
417 fn string(&mut self) -> Result<String, String> {
418 if !self.eat(b'"') {
419 return Err(self.err("expected `\"`"));
420 }
421 let mut out: Vec<u8> = Vec::new();
422 loop {
423 let Some(c) = self.peek() else {
424 return Err(self.err("unterminated string"));
425 };
426 self.i += 1;
427 match c {
428 b'"' => break,
429 b'\n' => return Err(self.err("line break inside a string (write \\n)")),
430 b'\\' => {
431 let Some(e) = self.peek() else {
432 return Err(self.err("unterminated string"));
433 };
434 self.i += 1;
435 match e {
436 b'"' => out.push(b'"'),
437 b'\\' => out.push(b'\\'),
438 b'/' => out.push(b'/'),
439 b'b' => out.push(8),
440 b'f' => out.push(12),
441 b'n' => out.push(b'\n'),
442 b'r' => out.push(b'\r'),
443 b't' => out.push(b'\t'),
444 b'u' => {
445 let mut code = self.hex4()?;
446 if (0xD800..0xDC00).contains(&code)
447 && self.s[self.i..].starts_with(b"\\u")
448 {
449 self.i += 2;
450 let low = self.hex4()?;
451 code = 0x10000
452 + ((code - 0xD800) << 10)
453 + (low.wrapping_sub(0xDC00) & 0x3FF);
454 }
455 let ch = char::from_u32(code).unwrap_or('\u{FFFD}');
456 let mut buf = [0u8; 4];
457 out.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
458 }
459 _ => return Err(self.err("unknown escape in a string")),
460 }
461 }
462 c => out.push(c),
463 }
464 }
465 String::from_utf8(out).map_err(|_| self.err("invalid UTF-8 in a string"))
466 }
467
468 fn hex4(&mut self) -> Result<u32, String> {
469 let hex = self
470 .s
471 .get(self.i..self.i + 4)
472 .and_then(|h| std::str::from_utf8(h).ok())
473 .and_then(|h| u32::from_str_radix(h, 16).ok())
474 .ok_or_else(|| self.err("bad \\u escape"))?;
475 self.i += 4;
476 Ok(hex)
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483
484 #[test]
485 fn records_lines_of_members() {
486 let j = parse("{\n \"a\": 1,\n \"b\": [\n true,\n \"x\"\n ]\n}").unwrap();
487 assert_eq!(j.line, 1);
488 assert_eq!(j.get("a").unwrap().line, 2);
489 let b = j.get("b").unwrap();
490 assert_eq!(b.line, 3);
491 assert_eq!(b.items()[1].line, 5);
492 assert_eq!(b.items()[1].str(), Some("x"));
493 }
494
495 #[test]
496 fn rejects_duplicates_and_reports_line() {
497 let e = parse("{\n\"a\": 1,\n\"a\": 2}").unwrap_err();
498 assert!(e.starts_with("line 3:"), "{e}");
499 }
500
501 #[test]
502 fn tolerates_comments_and_trailing_commas() {
503 let j = parse("{ // note\n \"a\": [1, 2,],\n}").unwrap();
504 assert_eq!(j.get("a").unwrap().items().len(), 2);
505 }
506
507 #[test]
508 fn pretty_prints_in_order() {
509 let j = J::obj([
510 ("b", J::nums(&[0.0, 16.0])),
511 ("a", J::obj([("x", J::string("y"))])),
512 ]);
513 assert_eq!(
514 to_pretty(&j),
515 "{\n \"b\": [0, 16],\n \"a\": { \"x\": \"y\" }\n}\n"
516 );
517 assert_eq!(parse(&to_pretty(&j)).unwrap().to_value(), j.to_value());
518 }
519
520 #[test]
521 fn integral_numbers_stay_integral() {
522 assert_eq!(num_value(16.0).to_string(), "16");
523 assert_eq!(num_value(22.5).to_string(), "22.5");
524 }
525}