1use std::collections::{BTreeMap, HashMap};
2use std::fmt::Write as _;
3
4use crate::value::{Value, aver_repr, list_slice};
5
6#[derive(Debug, Clone, PartialEq)]
7pub enum JsonValue {
8 Null,
9 Bool(bool),
10 Int(i64),
11 Float(f64),
12 String(String),
13 Array(Vec<JsonValue>),
14 Object(BTreeMap<String, JsonValue>),
15}
16
17pub fn parse_json(input: &str) -> Result<JsonValue, String> {
18 JsonParser::new(input).parse()
19}
20
21pub fn json_to_string(value: &JsonValue) -> String {
22 let mut out = String::new();
23 write_json_compact(&mut out, value);
24 out
25}
26
27pub fn format_json(value: &JsonValue) -> String {
28 let mut out = String::new();
29 write_json_pretty(&mut out, value, 0);
30 out
31}
32
33fn get_required<'a>(
34 obj: &'a BTreeMap<String, JsonValue>,
35 key: &str,
36 path: &str,
37) -> Result<&'a JsonValue, String> {
38 obj.get(key)
39 .ok_or_else(|| format!("{}: missing required field '{}'", path, key))
40}
41
42fn expect_object<'a>(
43 value: &'a JsonValue,
44 path: &str,
45) -> Result<&'a BTreeMap<String, JsonValue>, String> {
46 match value {
47 JsonValue::Object(obj) => Ok(obj),
48 _ => Err(format!("{} must be an object", path)),
49 }
50}
51
52fn parse_array<'a>(value: &'a JsonValue, path: &str) -> Result<&'a Vec<JsonValue>, String> {
53 match value {
54 JsonValue::Array(arr) => Ok(arr),
55 _ => Err(format!("{} must be an array", path)),
56 }
57}
58
59fn parse_string<'a>(value: &'a JsonValue, path: &str) -> Result<&'a str, String> {
60 match value {
61 JsonValue::String(s) => Ok(s),
62 _ => Err(format!("{} must be a string", path)),
63 }
64}
65
66pub fn values_to_json(values: &[Value]) -> Result<Vec<JsonValue>, String> {
67 values.iter().map(value_to_json).collect()
68}
69
70pub fn values_to_json_lossy(values: &[Value]) -> Vec<JsonValue> {
71 values.iter().map(value_to_json_lossy).collect()
72}
73
74pub fn json_values_to_values(values: &[JsonValue]) -> Result<Vec<Value>, String> {
75 values.iter().map(json_to_value).collect()
76}
77
78pub fn value_to_json(value: &Value) -> Result<JsonValue, String> {
79 match value {
80 Value::Int(i) => Ok(JsonValue::Int(*i)),
81 Value::Float(f) => {
82 if !f.is_finite() {
83 return Err("cannot serialize non-finite float (NaN/inf)".to_string());
84 }
85 Ok(JsonValue::Float(*f))
86 }
87 Value::Str(s) => Ok(JsonValue::String(s.clone())),
88 Value::Bool(b) => Ok(JsonValue::Bool(*b)),
89 Value::Unit => Ok(JsonValue::Null),
90 Value::Ok(inner) => Ok(wrap_marker("$ok", value_to_json(inner)?)),
91 Value::Err(inner) => Ok(wrap_marker("$err", value_to_json(inner)?)),
92 Value::Some(inner) => Ok(wrap_marker("$some", value_to_json(inner)?)),
93 Value::None => Ok(wrap_marker("$none", JsonValue::Bool(true))),
94 Value::List(_) | Value::ListSlice { .. } => {
95 let items =
96 list_slice(value).ok_or_else(|| "invalid list representation".to_string())?;
97 let mut arr = Vec::with_capacity(items.len());
98 for item in items {
99 arr.push(value_to_json(item)?);
100 }
101 Ok(JsonValue::Array(arr))
102 }
103 Value::Tuple(items) => {
104 let mut arr = Vec::with_capacity(items.len());
105 for item in items {
106 arr.push(value_to_json(item)?);
107 }
108 Ok(wrap_marker("$tuple", JsonValue::Array(arr)))
109 }
110 Value::Map(entries) => {
111 if entries.keys().all(|k| matches!(k, Value::Str(_))) {
112 let mut obj = BTreeMap::new();
113 for (k, v) in entries {
114 let Value::Str(key) = k else {
115 unreachable!("checked above");
116 };
117 obj.insert(key.clone(), value_to_json(v)?);
118 }
119 Ok(JsonValue::Object(obj))
120 } else {
121 let mut pairs = Vec::with_capacity(entries.len());
122 for (k, v) in entries {
123 pairs.push(JsonValue::Array(vec![value_to_json(k)?, value_to_json(v)?]));
124 }
125 Ok(wrap_marker("$map", JsonValue::Array(pairs)))
126 }
127 }
128 Value::Record { type_name, fields } => {
129 let mut fields_obj = BTreeMap::new();
130 for (name, field_value) in fields {
131 fields_obj.insert(name.clone(), value_to_json(field_value)?);
132 }
133 let mut payload = BTreeMap::new();
134 payload.insert("type".to_string(), JsonValue::String(type_name.clone()));
135 payload.insert("fields".to_string(), JsonValue::Object(fields_obj));
136 Ok(wrap_marker("$record", JsonValue::Object(payload)))
137 }
138 Value::Variant {
139 type_name,
140 variant,
141 fields,
142 } => {
143 let mut field_vals = Vec::with_capacity(fields.len());
144 for field in fields {
145 field_vals.push(value_to_json(field)?);
146 }
147 let mut payload = BTreeMap::new();
148 payload.insert("type".to_string(), JsonValue::String(type_name.clone()));
149 payload.insert("name".to_string(), JsonValue::String(variant.clone()));
150 payload.insert("fields".to_string(), JsonValue::Array(field_vals));
151 Ok(wrap_marker("$variant", JsonValue::Object(payload)))
152 }
153 Value::Fn { .. } | Value::Builtin(_) | Value::Namespace { .. } => Err(format!(
154 "cannot serialize non-replay-safe value: {}",
155 aver_repr(value)
156 )),
157 }
158}
159
160pub fn json_to_value(json: &JsonValue) -> Result<Value, String> {
161 match json {
162 JsonValue::Null => Ok(Value::Unit),
163 JsonValue::Bool(b) => Ok(Value::Bool(*b)),
164 JsonValue::Int(i) => Ok(Value::Int(*i)),
165 JsonValue::Float(f) => Ok(Value::Float(*f)),
166 JsonValue::String(s) => Ok(Value::Str(s.clone())),
167 JsonValue::Array(items) => {
168 let mut out = Vec::with_capacity(items.len());
169 for item in items {
170 out.push(json_to_value(item)?);
171 }
172 Ok(Value::List(out))
173 }
174 JsonValue::Object(obj) => {
175 if let Some((marker, payload)) = marker_single_key(obj) {
176 return decode_marker(marker, payload);
177 }
178 let mut map = HashMap::with_capacity(obj.len());
179 for (k, v) in obj {
180 map.insert(Value::Str(k.clone()), json_to_value(v)?);
181 }
182 Ok(Value::Map(map))
183 }
184 }
185}
186
187pub fn first_diff_path(expected: &JsonValue, got: &JsonValue) -> Option<String> {
188 first_diff_path_inner(expected, got, "$")
189}
190
191pub fn value_to_json_lossy(value: &Value) -> JsonValue {
192 match value_to_json(value) {
193 Ok(v) => v,
194 Err(_) => {
195 let mut obj = BTreeMap::new();
196 obj.insert("$opaque".to_string(), JsonValue::String(aver_repr(value)));
197 JsonValue::Object(obj)
198 }
199 }
200}
201
202fn wrap_marker(name: &str, value: JsonValue) -> JsonValue {
203 let mut obj = BTreeMap::new();
204 obj.insert(name.to_string(), value);
205 JsonValue::Object(obj)
206}
207
208fn marker_single_key(obj: &BTreeMap<String, JsonValue>) -> Option<(&str, &JsonValue)> {
209 if obj.len() != 1 {
210 return None;
211 }
212 obj.iter().next().map(|(k, v)| (k.as_str(), v))
213}
214
215fn decode_marker(marker: &str, payload: &JsonValue) -> Result<Value, String> {
216 match marker {
217 "$ok" => Ok(Value::Ok(Box::new(json_to_value(payload)?))),
218 "$err" => Ok(Value::Err(Box::new(json_to_value(payload)?))),
219 "$some" => Ok(Value::Some(Box::new(json_to_value(payload)?))),
220 "$none" => Ok(Value::None),
221 "$tuple" => decode_tuple(payload),
222 "$map" => decode_map(payload),
223 "$record" => decode_record(payload),
224 "$variant" => decode_variant(payload),
225 _ => Err(format!("unknown replay marker '{}'", marker)),
226 }
227}
228
229fn decode_tuple(payload: &JsonValue) -> Result<Value, String> {
230 let items = parse_array(payload, "$tuple")?;
231 let mut out = Vec::with_capacity(items.len());
232 for item in items {
233 out.push(json_to_value(item)?);
234 }
235 Ok(Value::Tuple(out))
236}
237
238fn decode_map(payload: &JsonValue) -> Result<Value, String> {
239 let pairs = parse_array(payload, "$map")?;
240 let mut out = HashMap::with_capacity(pairs.len());
241 for (idx, pair_json) in pairs.iter().enumerate() {
242 let pair = parse_array(pair_json, &format!("$map[{}]", idx))?;
243 if pair.len() != 2 {
244 return Err(format!("$map[{}] must be a 2-element array", idx));
245 }
246 let key = json_to_value(&pair[0])?;
247 let value = json_to_value(&pair[1])?;
248 out.insert(key, value);
249 }
250 Ok(Value::Map(out))
251}
252
253fn decode_record(payload: &JsonValue) -> Result<Value, String> {
254 let obj = expect_object(payload, "$record")?;
255 let type_name =
256 parse_string(get_required(obj, "type", "$record")?, "$record.type")?.to_string();
257 let fields_obj = expect_object(get_required(obj, "fields", "$record")?, "$record.fields")?;
258 let mut fields = Vec::with_capacity(fields_obj.len());
259 for (key, field_val) in fields_obj {
260 fields.push((key.clone(), json_to_value(field_val)?));
261 }
262 Ok(Value::Record { type_name, fields })
263}
264
265fn decode_variant(payload: &JsonValue) -> Result<Value, String> {
266 let obj = expect_object(payload, "$variant")?;
267 let type_name =
268 parse_string(get_required(obj, "type", "$variant")?, "$variant.type")?.to_string();
269 let variant =
270 parse_string(get_required(obj, "name", "$variant")?, "$variant.name")?.to_string();
271 let fields_arr = parse_array(get_required(obj, "fields", "$variant")?, "$variant.fields")?;
272 let mut fields = Vec::with_capacity(fields_arr.len());
273 for val in fields_arr {
274 fields.push(json_to_value(val)?);
275 }
276 Ok(Value::Variant {
277 type_name,
278 variant,
279 fields,
280 })
281}
282
283fn first_diff_path_inner(expected: &JsonValue, got: &JsonValue, path: &str) -> Option<String> {
284 match (expected, got) {
285 (JsonValue::Object(a), JsonValue::Object(b)) => {
286 let mut keys = a.keys().chain(b.keys()).cloned().collect::<Vec<_>>();
287 keys.sort();
288 keys.dedup();
289 for key in keys {
290 let next_path = if path == "$" {
291 format!("$.{}", key)
292 } else {
293 format!("{}.{}", path, key)
294 };
295 match (a.get(&key), b.get(&key)) {
296 (Some(av), Some(bv)) => {
297 if let Some(diff) = first_diff_path_inner(av, bv, &next_path) {
298 return Some(diff);
299 }
300 }
301 _ => return Some(next_path),
302 }
303 }
304 None
305 }
306 (JsonValue::Array(a), JsonValue::Array(b)) => {
307 if a.len() != b.len() {
308 return Some(format!("{}[len]", path));
309 }
310 for (idx, (av, bv)) in a.iter().zip(b.iter()).enumerate() {
311 let next_path = format!("{}[{}]", path, idx);
312 if let Some(diff) = first_diff_path_inner(av, bv, &next_path) {
313 return Some(diff);
314 }
315 }
316 None
317 }
318 _ => {
319 if expected == got {
320 None
321 } else {
322 Some(path.to_string())
323 }
324 }
325 }
326}
327
328fn write_json_compact(out: &mut String, value: &JsonValue) {
329 match value {
330 JsonValue::Null => out.push_str("null"),
331 JsonValue::Bool(true) => out.push_str("true"),
332 JsonValue::Bool(false) => out.push_str("false"),
333 JsonValue::Int(i) => {
334 let _ = write!(out, "{}", i);
335 }
336 JsonValue::Float(f) => out.push_str(&format_float(*f)),
337 JsonValue::String(s) => write_json_string(out, s),
338 JsonValue::Array(arr) => {
339 out.push('[');
340 for (idx, item) in arr.iter().enumerate() {
341 if idx > 0 {
342 out.push(',');
343 }
344 write_json_compact(out, item);
345 }
346 out.push(']');
347 }
348 JsonValue::Object(obj) => {
349 out.push('{');
350 for (idx, (k, v)) in obj.iter().enumerate() {
351 if idx > 0 {
352 out.push(',');
353 }
354 write_json_string(out, k);
355 out.push(':');
356 write_json_compact(out, v);
357 }
358 out.push('}');
359 }
360 }
361}
362
363fn write_json_pretty(out: &mut String, value: &JsonValue, indent: usize) {
364 match value {
365 JsonValue::Array(arr) => {
366 if arr.is_empty() {
367 out.push_str("[]");
368 return;
369 }
370 out.push_str("[\n");
371 for (idx, item) in arr.iter().enumerate() {
372 push_indent(out, indent + 2);
373 write_json_pretty(out, item, indent + 2);
374 if idx + 1 < arr.len() {
375 out.push(',');
376 }
377 out.push('\n');
378 }
379 push_indent(out, indent);
380 out.push(']');
381 }
382 JsonValue::Object(obj) => {
383 if obj.is_empty() {
384 out.push_str("{}");
385 return;
386 }
387 out.push_str("{\n");
388 for (idx, (k, v)) in obj.iter().enumerate() {
389 push_indent(out, indent + 2);
390 write_json_string(out, k);
391 out.push_str(": ");
392 write_json_pretty(out, v, indent + 2);
393 if idx + 1 < obj.len() {
394 out.push(',');
395 }
396 out.push('\n');
397 }
398 push_indent(out, indent);
399 out.push('}');
400 }
401 _ => write_json_compact(out, value),
402 }
403}
404
405fn push_indent(out: &mut String, indent: usize) {
406 for _ in 0..indent {
407 out.push(' ');
408 }
409}
410
411fn write_json_string(out: &mut String, s: &str) {
412 out.push('"');
413 for ch in s.chars() {
414 match ch {
415 '"' => out.push_str("\\\""),
416 '\\' => out.push_str("\\\\"),
417 '\n' => out.push_str("\\n"),
418 '\r' => out.push_str("\\r"),
419 '\t' => out.push_str("\\t"),
420 '\u{08}' => out.push_str("\\b"),
421 '\u{0C}' => out.push_str("\\f"),
422 c if c < '\u{20}' => {
423 let _ = write!(out, "\\u{:04X}", c as u32);
424 }
425 c => out.push(c),
426 }
427 }
428 out.push('"');
429}
430
431fn format_float(f: f64) -> String {
432 let mut s = format!("{}", f);
433 if !s.contains('.') && !s.contains('e') && !s.contains('E') {
434 s.push_str(".0");
435 }
436 s
437}
438
439struct JsonParser<'a> {
440 src: &'a str,
441 bytes: &'a [u8],
442 pos: usize,
443}
444
445impl<'a> JsonParser<'a> {
446 fn new(src: &'a str) -> Self {
447 Self {
448 src,
449 bytes: src.as_bytes(),
450 pos: 0,
451 }
452 }
453
454 fn parse(mut self) -> Result<JsonValue, String> {
455 self.skip_ws();
456 let value = self.parse_value()?;
457 self.skip_ws();
458 if self.pos != self.bytes.len() {
459 return Err(self.error("trailing characters after JSON value"));
460 }
461 Ok(value)
462 }
463
464 fn parse_value(&mut self) -> Result<JsonValue, String> {
465 self.skip_ws();
466 let Some(byte) = self.peek() else {
467 return Err(self.error("unexpected end of input"));
468 };
469
470 match byte {
471 b'n' => {
472 self.expect_keyword("null")?;
473 Ok(JsonValue::Null)
474 }
475 b't' => {
476 self.expect_keyword("true")?;
477 Ok(JsonValue::Bool(true))
478 }
479 b'f' => {
480 self.expect_keyword("false")?;
481 Ok(JsonValue::Bool(false))
482 }
483 b'"' => Ok(JsonValue::String(self.parse_string()?)),
484 b'[' => self.parse_array(),
485 b'{' => self.parse_object(),
486 b'-' | b'0'..=b'9' => self.parse_number(),
487 _ => Err(self.error("unexpected token")),
488 }
489 }
490
491 fn parse_array(&mut self) -> Result<JsonValue, String> {
492 self.expect_byte(b'[')?;
493 self.skip_ws();
494
495 let mut items = Vec::new();
496 if self.peek() == Some(b']') {
497 self.pos += 1;
498 return Ok(JsonValue::Array(items));
499 }
500
501 loop {
502 items.push(self.parse_value()?);
503 self.skip_ws();
504 match self.peek() {
505 Some(b',') => {
506 self.pos += 1;
507 self.skip_ws();
508 }
509 Some(b']') => {
510 self.pos += 1;
511 break;
512 }
513 _ => return Err(self.error("expected ',' or ']' in array")),
514 }
515 }
516
517 Ok(JsonValue::Array(items))
518 }
519
520 fn parse_object(&mut self) -> Result<JsonValue, String> {
521 self.expect_byte(b'{')?;
522 self.skip_ws();
523
524 let mut fields = BTreeMap::new();
525 if self.peek() == Some(b'}') {
526 self.pos += 1;
527 return Ok(JsonValue::Object(fields));
528 }
529
530 loop {
531 let key = self.parse_string()?;
532 self.skip_ws();
533 self.expect_byte(b':')?;
534 self.skip_ws();
535 let value = self.parse_value()?;
536 fields.insert(key, value);
537 self.skip_ws();
538
539 match self.peek() {
540 Some(b',') => {
541 self.pos += 1;
542 self.skip_ws();
543 }
544 Some(b'}') => {
545 self.pos += 1;
546 break;
547 }
548 _ => return Err(self.error("expected ',' or '}' in object")),
549 }
550 }
551
552 Ok(JsonValue::Object(fields))
553 }
554
555 fn parse_string(&mut self) -> Result<String, String> {
556 self.expect_byte(b'"')?;
557 let mut out = String::new();
558 let mut chunk_start = self.pos;
559
560 while self.pos < self.bytes.len() {
561 let b = self.bytes[self.pos];
562 match b {
563 b'"' => {
564 if chunk_start < self.pos {
565 out.push_str(
566 std::str::from_utf8(&self.bytes[chunk_start..self.pos])
567 .map_err(|_| self.error("invalid UTF-8 in string"))?,
568 );
569 }
570 self.pos += 1;
571 return Ok(out);
572 }
573 b'\\' => {
574 if chunk_start < self.pos {
575 out.push_str(
576 std::str::from_utf8(&self.bytes[chunk_start..self.pos])
577 .map_err(|_| self.error("invalid UTF-8 in string"))?,
578 );
579 }
580 self.pos += 1;
581 out.push(self.parse_escape_sequence()?);
582 chunk_start = self.pos;
583 }
584 0x00..=0x1F => {
585 return Err(self.error("control character in string literal"));
586 }
587 _ => {
588 self.pos += 1;
589 }
590 }
591 }
592
593 Err(self.error("unterminated string literal"))
594 }
595
596 fn parse_escape_sequence(&mut self) -> Result<char, String> {
597 let Some(ch) = self.next_byte() else {
598 return Err(self.error("unterminated escape sequence"));
599 };
600
601 match ch {
602 b'"' => Ok('"'),
603 b'\\' => Ok('\\'),
604 b'/' => Ok('/'),
605 b'b' => Ok('\u{08}'),
606 b'f' => Ok('\u{0C}'),
607 b'n' => Ok('\n'),
608 b'r' => Ok('\r'),
609 b't' => Ok('\t'),
610 b'u' => self.parse_unicode_escape(),
611 _ => Err(self.error("invalid escape sequence")),
612 }
613 }
614
615 fn parse_unicode_escape(&mut self) -> Result<char, String> {
616 let first = self.parse_hex_u16()?;
617
618 if (0xD800..=0xDBFF).contains(&first) {
619 self.expect_byte(b'\\')?;
620 self.expect_byte(b'u')?;
621 let second = self.parse_hex_u16()?;
622 if !(0xDC00..=0xDFFF).contains(&second) {
623 return Err(self.error("invalid low surrogate in unicode escape"));
624 }
625 let high = (first as u32) - 0xD800;
626 let low = (second as u32) - 0xDC00;
627 let codepoint = 0x10000 + ((high << 10) | low);
628 return char::from_u32(codepoint)
629 .ok_or_else(|| self.error("invalid unicode codepoint"));
630 }
631
632 if (0xDC00..=0xDFFF).contains(&first) {
633 return Err(self.error("unexpected low surrogate in unicode escape"));
634 }
635
636 char::from_u32(first as u32).ok_or_else(|| self.error("invalid unicode codepoint"))
637 }
638
639 fn parse_hex_u16(&mut self) -> Result<u16, String> {
640 let mut value: u16 = 0;
641 for _ in 0..4 {
642 let Some(b) = self.next_byte() else {
643 return Err(self.error("incomplete unicode escape"));
644 };
645 value = value
646 .checked_mul(16)
647 .ok_or_else(|| self.error("unicode escape overflow"))?;
648 value = value
649 .checked_add(hex_digit(b).ok_or_else(|| self.error("invalid hex digit"))? as u16)
650 .ok_or_else(|| self.error("unicode escape overflow"))?;
651 }
652 Ok(value)
653 }
654
655 fn parse_number(&mut self) -> Result<JsonValue, String> {
656 let start = self.pos;
657
658 if self.peek() == Some(b'-') {
659 self.pos += 1;
660 }
661
662 match self.peek() {
663 Some(b'0') => {
664 self.pos += 1;
665 if let Some(b'0'..=b'9') = self.peek() {
666 return Err(self.error("leading zero in number"));
667 }
668 }
669 Some(b'1'..=b'9') => {
670 self.pos += 1;
671 while let Some(b'0'..=b'9') = self.peek() {
672 self.pos += 1;
673 }
674 }
675 _ => return Err(self.error("invalid number")),
676 }
677
678 let mut is_float = false;
679
680 if self.peek() == Some(b'.') {
681 is_float = true;
682 self.pos += 1;
683 let frac_start = self.pos;
684 while let Some(b'0'..=b'9') = self.peek() {
685 self.pos += 1;
686 }
687 if self.pos == frac_start {
688 return Err(self.error("missing digits after decimal point"));
689 }
690 }
691
692 if matches!(self.peek(), Some(b'e' | b'E')) {
693 is_float = true;
694 self.pos += 1;
695 if matches!(self.peek(), Some(b'+' | b'-')) {
696 self.pos += 1;
697 }
698 let exp_start = self.pos;
699 while let Some(b'0'..=b'9') = self.peek() {
700 self.pos += 1;
701 }
702 if self.pos == exp_start {
703 return Err(self.error("missing exponent digits"));
704 }
705 }
706
707 let number_text = &self.src[start..self.pos];
708 if is_float {
709 let value = number_text
710 .parse::<f64>()
711 .map_err(|_| self.error("invalid floating-point number"))?;
712 if !value.is_finite() {
713 return Err(self.error("non-finite number is not allowed"));
714 }
715 Ok(JsonValue::Float(value))
716 } else {
717 let value = number_text
718 .parse::<i64>()
719 .map_err(|_| self.error("integer out of i64 range"))?;
720 Ok(JsonValue::Int(value))
721 }
722 }
723
724 fn expect_keyword(&mut self, keyword: &str) -> Result<(), String> {
725 let end = self.pos + keyword.len();
726 if end > self.bytes.len() || &self.src[self.pos..end] != keyword {
727 return Err(self.error(&format!("expected '{}'", keyword)));
728 }
729 self.pos = end;
730 Ok(())
731 }
732
733 fn expect_byte(&mut self, expected: u8) -> Result<(), String> {
734 match self.next_byte() {
735 Some(b) if b == expected => Ok(()),
736 _ => Err(self.error(&format!("expected '{}'", expected as char))),
737 }
738 }
739
740 fn peek(&self) -> Option<u8> {
741 self.bytes.get(self.pos).copied()
742 }
743
744 fn next_byte(&mut self) -> Option<u8> {
745 let b = self.peek()?;
746 self.pos += 1;
747 Some(b)
748 }
749
750 fn skip_ws(&mut self) {
751 while let Some(b) = self.peek() {
752 if matches!(b, b' ' | b'\n' | b'\r' | b'\t') {
753 self.pos += 1;
754 } else {
755 break;
756 }
757 }
758 }
759
760 fn error(&self, msg: &str) -> String {
761 format!("JSON parse error at byte {}: {}", self.pos, msg)
762 }
763}
764
765fn hex_digit(byte: u8) -> Option<u8> {
766 match byte {
767 b'0'..=b'9' => Some(byte - b'0'),
768 b'a'..=b'f' => Some(byte - b'a' + 10),
769 b'A'..=b'F' => Some(byte - b'A' + 10),
770 _ => None,
771 }
772}