Skip to main content

alibi_core/utils/
json.rs

1//! JavaScript JSON numbers at request, response, callback and storage boundaries.
2//!
3//! `JsValue` carries IEEE754 numbers, including Infinity and signed zero, without
4//! enabling `serde_json` features that reserve otherwise valid application keys.
5
6use indexmap::IndexMap;
7use serde::{
8    Deserialize, Deserializer, Serialize, Serializer,
9    de::{DeserializeOwned, IntoDeserializer, MapAccess, SeqAccess, Visitor},
10};
11use serde_json::{Number, Value};
12use std::{
13    any::{Any, TypeId},
14    fmt,
15};
16
17#[derive(Clone, Debug, PartialEq)]
18pub enum JsValue {
19    Null,
20    Bool(bool),
21    Number(f64),
22    String(String),
23    Array(Vec<Self>),
24    Object(IndexMap<String, Self>),
25}
26
27impl JsValue {
28    /// JavaScript String coercion used by trusted endpoint ID schemas.
29    /// # Errors
30    /// Returns an error when an own toString value prevents object coercion.
31    pub fn coerce_string(&self) -> Result<String, &'static str> {
32        match self {
33            Self::Null => Ok("null".to_owned()),
34            Self::Bool(value) => Ok(value.to_string()),
35            Self::String(value) => Ok(value.clone()),
36            Self::Number(value) => Ok(ryu_js::Buffer::new().format(*value).to_owned()),
37            Self::Array(values) => values
38                .iter()
39                .map(|value| match value {
40                    Self::Null => Ok(String::new()),
41                    value => value.coerce_string(),
42                })
43                .collect::<Result<Vec<_>, _>>()
44                .map(|values| values.join(",")),
45            Self::Object(value) if value.contains_key("toString") => {
46                Err("Cannot convert object to primitive value")
47            }
48            Self::Object(_) => Ok("[object Object]".to_owned()),
49        }
50    }
51
52    #[must_use]
53    pub const fn as_f64(&self) -> Option<f64> {
54        if let Self::Number(n) = self {
55            Some(*n)
56        } else {
57            None
58        }
59    }
60    #[must_use]
61    pub fn as_str(&self) -> Option<&str> {
62        if let Self::String(s) = self {
63            Some(s)
64        } else {
65            None
66        }
67    }
68    #[must_use]
69    pub const fn as_bool(&self) -> Option<bool> {
70        if let Self::Bool(b) = self {
71            Some(*b)
72        } else {
73            None
74        }
75    }
76    #[must_use]
77    pub const fn as_object(&self) -> Option<&IndexMap<String, Self>> {
78        if let Self::Object(m) = self {
79            Some(m)
80        } else {
81            None
82        }
83    }
84    #[must_use]
85    pub fn as_array(&self) -> Option<&[Self]> {
86        if let Self::Array(a) = self {
87            Some(a)
88        } else {
89            None
90        }
91    }
92    #[must_use]
93    pub fn get(&self, key: &str) -> Option<&Self> {
94        self.as_object()?.get(key)
95    }
96    /// JavaScript truthiness.
97    #[must_use]
98    pub fn is_truthy(&self) -> bool {
99        match self {
100            Self::Null => false,
101            Self::Bool(value) => *value,
102            Self::Number(value) => *value != 0.0 && !value.is_nan(),
103            Self::String(value) => !value.is_empty(),
104            Self::Array(_) | Self::Object(_) => true,
105        }
106    }
107    #[must_use]
108    pub const fn is_null(&self) -> bool {
109        matches!(self, Self::Null)
110    }
111    #[must_use]
112    pub const fn is_string(&self) -> bool {
113        matches!(self, Self::String(_))
114    }
115    #[must_use]
116    pub const fn is_boolean(&self) -> bool {
117        matches!(self, Self::Bool(_))
118    }
119    #[must_use]
120    pub const fn is_array(&self) -> bool {
121        matches!(self, Self::Array(_))
122    }
123    #[must_use]
124    pub const fn is_object(&self) -> bool {
125        matches!(self, Self::Object(_))
126    }
127    /// Apply JSON.stringify before converting to `serde_json`'s finite representation.
128    ///
129    /// # Errors
130    ///
131    /// Returns an error if the JavaScript value cannot be represented as JSON.
132    pub fn to_json_value(&self) -> Result<Value, serde_json::Error> {
133        finite_value(self)
134    }
135}
136
137impl From<Value> for JsValue {
138    fn from(value: Value) -> Self {
139        match value {
140            Value::Null => Self::Null,
141            Value::Bool(b) => Self::Bool(b),
142            Value::Number(n) => Self::Number(n.as_f64().unwrap_or_default()),
143            Value::String(s) => Self::String(s),
144            Value::Array(a) => Self::Array(a.into_iter().map(Self::from).collect()),
145            Value::Object(m) => {
146                Self::Object(m.into_iter().map(|(k, v)| (k, Self::from(v))).collect())
147            }
148        }
149    }
150}
151
152impl Serialize for JsValue {
153    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
154        match self {
155            Self::Null => serializer.serialize_unit(),
156            Self::Bool(b) => serializer.serialize_bool(*b),
157            Self::Number(n) => serializer.serialize_f64(*n),
158            Self::String(s) => serializer.serialize_str(s),
159            Self::Array(a) => a.serialize(serializer),
160            Self::Object(m) => m.serialize(serializer),
161        }
162    }
163}
164
165impl<'de> Deserialize<'de> for JsValue {
166    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
167        struct JsVisitor;
168        impl<'de> Visitor<'de> for JsVisitor {
169            type Value = JsValue;
170            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
171                formatter.write_str("a JSON value")
172            }
173            fn visit_unit<E>(self) -> Result<Self::Value, E> {
174                Ok(JsValue::Null)
175            }
176            fn visit_none<E>(self) -> Result<Self::Value, E> {
177                Ok(JsValue::Null)
178            }
179            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> {
180                Ok(JsValue::Bool(v))
181            }
182            #[expect(
183                clippy::as_conversions,
184                clippy::cast_precision_loss,
185                reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
186            )]
187            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
188                Ok(JsValue::Number(v as f64))
189            }
190            #[expect(
191                clippy::as_conversions,
192                clippy::cast_precision_loss,
193                reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
194            )]
195            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
196                Ok(JsValue::Number(v as f64))
197            }
198            fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
199                Ok(JsValue::Number(v))
200            }
201            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
202                Ok(JsValue::String(v.into()))
203            }
204            fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
205                Ok(JsValue::String(v))
206            }
207            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
208                let mut values = Vec::new();
209                while let Some(value) = seq.next_element()? {
210                    values.push(value);
211                }
212                Ok(JsValue::Array(values))
213            }
214            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
215                let mut values = IndexMap::new();
216                while let Some((key, value)) = map.next_entry()? {
217                    _ = values.insert(key, value);
218                }
219                Ok(JsValue::Object(values))
220            }
221        }
222        deserializer.deserialize_any(JsVisitor)
223    }
224}
225
226impl IntoDeserializer<'_, serde_json::Error> for JsValue {
227    type Deserializer = Self;
228    fn into_deserializer(self) -> Self {
229        self
230    }
231}
232
233/// Integral numbers within the target's range deserialize as integers; everything
234/// else defers to `deserialize_any`.
235macro_rules! integer_number {
236    ($visit:ident, $cast:ty, $range:expr, $expect:meta, $($method:ident),*) => {$(
237        #[$expect]
238        fn $method<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
239            match self {
240                Self::Number(n) if n.is_finite() && n.fract() == 0.0 && $range.contains(&n) => {
241                    visitor.$visit(n as $cast)
242                }
243                value => value.deserialize_any(visitor),
244            }
245        }
246    )*};
247}
248
249impl<'de> Deserializer<'de> for JsValue {
250    type Error = serde_json::Error;
251    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
252        match self {
253            Self::Null => visitor.visit_unit(),
254            Self::Bool(b) => visitor.visit_bool(b),
255            Self::Number(n) => visitor.visit_f64(n),
256            Self::String(s) => visitor.visit_string(s),
257            Self::Array(a) => {
258                visitor.visit_seq(serde::de::value::SeqDeserializer::new(a.into_iter()))
259            }
260            Self::Object(m) => {
261                visitor.visit_map(serde::de::value::MapDeserializer::new(m.into_iter()))
262            }
263        }
264    }
265    fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
266        if self.is_null() {
267            visitor.visit_none()
268        } else {
269            visitor.visit_some(self)
270        }
271    }
272    fn deserialize_newtype_struct<V: Visitor<'de>>(
273        self,
274        _name: &'static str,
275        visitor: V,
276    ) -> Result<V::Value, Self::Error> {
277        visitor.visit_newtype_struct(self)
278    }
279    fn deserialize_enum<V: Visitor<'de>>(
280        self,
281        _name: &'static str,
282        _variants: &'static [&'static str],
283        visitor: V,
284    ) -> Result<V::Value, Self::Error> {
285        match self {
286            Self::String(s) => visitor.visit_enum(s.into_deserializer()),
287            Self::Object(map) => visitor.visit_enum(serde::de::value::MapAccessDeserializer::new(
288                serde::de::value::MapDeserializer::new(map.into_iter()),
289            )),
290            value @ (Self::Null | Self::Bool(_) | Self::Number(_) | Self::Array(_)) => {
291                value.deserialize_any(visitor)
292            }
293        }
294    }
295    integer_number!(
296        visit_i64,
297        i64,
298        (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0),
299        expect(
300            clippy::as_conversions,
301            clippy::cast_possible_truncation,
302            reason = "Finite integral values are checked against the target integer bounds before conversion"
303        ),
304        deserialize_i8,
305        deserialize_i16,
306        deserialize_i32,
307        deserialize_i64,
308        deserialize_i128
309    );
310    integer_number!(
311        visit_u64,
312        u64,
313        (0.0..18_446_744_073_709_552_000.0),
314        expect(
315            clippy::as_conversions,
316            clippy::cast_possible_truncation,
317            clippy::cast_sign_loss,
318            reason = "Finite integral values are checked against the target integer bounds before conversion"
319        ),
320        deserialize_u8,
321        deserialize_u16,
322        deserialize_u32,
323        deserialize_u64,
324        deserialize_u128
325    );
326    serde::forward_to_deserialize_any! {
327        bool f32 f64 char str string bytes
328        byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any
329    }
330}
331
332struct Parser<'a> {
333    input: &'a str,
334    position: usize,
335}
336
337impl Parser<'_> {
338    fn peek(&self) -> Option<u8> {
339        self.input.as_bytes().get(self.position).copied()
340    }
341    fn whitespace(&mut self) {
342        while self
343            .peek()
344            .is_some_and(|b| matches!(b, b' ' | b'\n' | b'\r' | b'\t'))
345        {
346            self.position += 1;
347        }
348    }
349    fn take(&mut self, byte: u8) -> bool {
350        if self.peek() == Some(byte) {
351            self.position += 1;
352            true
353        } else {
354            false
355        }
356    }
357    fn string(&mut self) -> Result<String, serde_json::Error> {
358        let start = self.position;
359        if !self.take(b'"') {
360            return Err(invalid("expected JSON string"));
361        }
362        loop {
363            match self.peek() {
364                Some(b'"') => {
365                    self.position += 1;
366                    return serde_json::from_str(
367                        self.input
368                            .get(start..self.position)
369                            .ok_or_else(|| invalid("invalid JSON string bounds"))?,
370                    );
371                }
372                Some(b'\\') => {
373                    self.position += 1;
374                    if self.peek().is_none() {
375                        return Err(invalid("unterminated JSON escape"));
376                    }
377                    self.position += 1;
378                }
379                Some(_) => self.position += 1,
380                None => return Err(invalid("unterminated JSON string")),
381            }
382        }
383    }
384    fn value(&mut self, depth: usize) -> Result<JsValue, serde_json::Error> {
385        self.whitespace();
386        match self.peek() {
387            Some(b'"') => self.string().map(JsValue::String),
388            Some(b'{') => {
389                if depth >= 127 {
390                    return Err(invalid("recursion limit exceeded"));
391                }
392                self.position += 1;
393                self.whitespace();
394                let mut values = IndexMap::new();
395                if self.take(b'}') {
396                    return Ok(JsValue::Object(values));
397                }
398                loop {
399                    self.whitespace();
400                    let key = self.string()?;
401                    self.whitespace();
402                    if !self.take(b':') {
403                        return Err(invalid("expected JSON colon"));
404                    }
405                    let value = self.value(depth + 1)?;
406                    _ = values.insert(key, value);
407                    self.whitespace();
408                    if self.take(b'}') {
409                        break;
410                    }
411                    if !self.take(b',') {
412                        return Err(invalid("expected JSON object delimiter"));
413                    }
414                }
415                Ok(JsValue::Object(values))
416            }
417            Some(b'[') => {
418                if depth >= 127 {
419                    return Err(invalid("recursion limit exceeded"));
420                }
421                self.position += 1;
422                self.whitespace();
423                let mut values = Vec::new();
424                if self.take(b']') {
425                    return Ok(JsValue::Array(values));
426                }
427                loop {
428                    values.push(self.value(depth + 1)?);
429                    self.whitespace();
430                    if self.take(b']') {
431                        break;
432                    }
433                    if !self.take(b',') {
434                        return Err(invalid("expected JSON array delimiter"));
435                    }
436                }
437                Ok(JsValue::Array(values))
438            }
439            Some(b't') => self.literal("true", JsValue::Bool(true)),
440            Some(b'f') => self.literal("false", JsValue::Bool(false)),
441            Some(b'n') => self.literal("null", JsValue::Null),
442            Some(b'-' | b'0'..=b'9') => self.number(),
443            _ => Err(invalid("expected JSON value")),
444        }
445    }
446    fn literal(&mut self, text: &str, value: JsValue) -> Result<JsValue, serde_json::Error> {
447        if self
448            .input
449            .get(self.position..)
450            .is_some_and(|rest| rest.starts_with(text))
451        {
452            self.position += text.len();
453            Ok(value)
454        } else {
455            Err(invalid("invalid JSON literal"))
456        }
457    }
458    fn digits(&mut self) -> bool {
459        let start = self.position;
460        while self.peek().is_some_and(|b| b.is_ascii_digit()) {
461            self.position += 1;
462        }
463        self.position > start
464    }
465    fn number(&mut self) -> Result<JsValue, serde_json::Error> {
466        let start = self.position;
467        _ = self.take(b'-');
468        if !self.take(b'0') && !self.digits() {
469            return Err(invalid("invalid JSON number"));
470        }
471        if self.take(b'.') && !self.digits() {
472            return Err(invalid("invalid JSON fraction"));
473        }
474        if self.take(b'e') || self.take(b'E') {
475            _ = self.take(b'+') || self.take(b'-');
476            if !self.digits() {
477                return Err(invalid("invalid JSON exponent"));
478            }
479        }
480        self.input
481            .get(start..self.position)
482            .ok_or_else(|| invalid("invalid JSON number bounds"))?
483            .parse::<f64>()
484            .map(JsValue::Number)
485            .map_err(|_| invalid("invalid JSON number"))
486    }
487}
488
489/// Parse RFC8259 structure with JavaScript f64 conversion. Strings use `serde_json`'s
490/// escape/UTF8 validation; structural recursion is bounded to its default depth.
491///
492/// # Errors
493///
494/// Returns an error if the input is not valid JSON.
495pub fn parse_value(input: &str) -> Result<JsValue, serde_json::Error> {
496    let mut parser = Parser { input, position: 0 };
497    let value = parser.value(0)?;
498    parser.whitespace();
499    if parser.position != input.len() {
500        return Err(invalid("trailing JSON input"));
501    }
502    Ok(value)
503}
504
505/// # Errors
506///
507/// Returns an error if the input is not valid JSON or cannot be deserialized into the requested type.
508pub fn from_slice<T: DeserializeOwned + 'static>(input: &[u8]) -> Result<T, serde_json::Error> {
509    let text = std::str::from_utf8(input).map_err(|_| invalid("invalid JSON UTF8"))?;
510    from_value(parse_value(text)?)
511}
512
513/// Decode an owned DTO.
514///
515/// Root `Value` is constructed without `serde_json`'s private map markers. Arbitrary nested
516/// `Value` fields must use `deserialize_value` or `deserialize_optional_value`; `JsValue` fields
517/// require no adapter.
518///
519/// # Errors
520///
521/// Returns an error if the value cannot be deserialized into the requested type.
522pub fn from_value<T: DeserializeOwned + 'static>(input: JsValue) -> Result<T, serde_json::Error> {
523    if TypeId::of::<T>() == TypeId::of::<Value>() {
524        let value: Box<dyn Any> = Box::new(input.to_json_value()?);
525        return value
526            .downcast::<T>()
527            .map(|value| *value)
528            .map_err(|_| invalid("invalid JSON value type"));
529    }
530    T::deserialize(input)
531}
532
533/// Preserve arbitrary JSON object keys in a typed `serde_json::Value` field.
534///
535/// # Errors
536///
537/// Propagates errors from deserializing the JavaScript-compatible JSON representation.
538pub fn deserialize_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Value, D::Error> {
539    JsValue::deserialize(deserializer)?
540        .to_json_value()
541        .map_err(serde::de::Error::custom)
542}
543
544/// Optional arbitrary JSON field adapter; null retains normal Option semantics.
545///
546/// # Errors
547///
548/// Propagates errors from deserializing the optional JSON representation.
549pub fn deserialize_optional_value<'de, D: Deserializer<'de>>(
550    deserializer: D,
551) -> Result<Option<Value>, D::Error> {
552    Option::<JsValue>::deserialize(deserializer)?
553        .map(|value| value.to_json_value().map_err(serde::de::Error::custom))
554        .transpose()
555}
556
557/// Typed object adapter for arbitrary values inside a string-keyed map.
558///
559/// # Errors
560///
561/// Returns an error if the deserializer does not supply a valid JSON object.
562pub fn deserialize_map<'de, D: Deserializer<'de>>(
563    deserializer: D,
564) -> Result<serde_json::Map<String, Value>, D::Error> {
565    match deserialize_value(deserializer)? {
566        Value::Object(map) => Ok(map),
567        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Array(_) => {
568            Err(serde::de::Error::custom("expected JSON object"))
569        }
570    }
571}
572
573/// Deserialize arbitrary values in an optional object field.
574///
575/// # Errors
576///
577/// Returns an error if the supplied non-null value is not a valid JSON object.
578pub fn deserialize_optional_map<'de, D: Deserializer<'de>>(
579    deserializer: D,
580) -> Result<Option<serde_json::Map<String, Value>>, D::Error> {
581    deserialize_optional_value(deserializer)?
582        .map(|value| match value {
583            Value::Object(map) => Ok(map),
584            Value::Null
585            | Value::Bool(_)
586            | Value::Number(_)
587            | Value::String(_)
588            | Value::Array(_) => Err(serde::de::Error::custom("expected JSON object")),
589        })
590        .transpose()
591}
592
593/// Preserve arbitrary values in flattened public view extension fields.
594///
595/// # Errors
596///
597/// Returns an error if the deserializer does not supply a valid JSON object.
598pub fn deserialize_btree_map<'de, D: Deserializer<'de>>(
599    deserializer: D,
600) -> Result<std::collections::BTreeMap<String, Value>, D::Error> {
601    deserialize_map(deserializer).map(|map| map.into_iter().collect())
602}
603
604fn invalid(message: &str) -> serde_json::Error {
605    <serde_json::Error as serde::de::Error>::custom(message)
606}
607
608#[must_use]
609pub fn number_as_f64(number: &Number) -> Option<f64> {
610    number.as_f64()
611}
612
613/// # Errors
614///
615/// Returns an error if the number cannot be serialized in JavaScript-compatible notation.
616pub fn number_to_string(number: &Number) -> Result<String, serde_json::Error> {
617    Ok(ryu_js::Buffer::new()
618        .format(number_as_f64(number).ok_or_else(|| invalid("invalid JSON number"))?)
619        .to_owned())
620}
621
622/// Encode Serialize data exactly as JSON.stringify would encode its numbers.
623///
624/// # Errors
625///
626/// Returns an error if the input cannot be serialized as JavaScript-compatible JSON.
627pub fn to_vec<T: Serialize + ?Sized>(data: &T) -> Result<Vec<u8>, serde_json::Error> {
628    let value = JsValue::from(serde_json::to_value(data)?);
629    let mut bytes = Vec::new();
630    write_value(&value, &mut bytes)?;
631    Ok(bytes)
632}
633
634/// # Errors
635///
636/// Returns an error if the input cannot be serialized as JavaScript-compatible JSON.
637pub fn to_string<T: Serialize + ?Sized>(data: &T) -> Result<String, serde_json::Error> {
638    String::from_utf8(to_vec(data)?).map_err(|_| invalid("invalid JSON output UTF8"))
639}
640
641/// # Errors
642///
643/// Returns an error if the `OpenAPI` document cannot be serialized.
644pub fn to_value<T: Serialize + ?Sized>(data: &T) -> Result<Value, serde_json::Error> {
645    finite_value(&JsValue::from(serde_json::to_value(data)?))
646}
647
648/// # Errors
649///
650/// Propagates errors from the serializer.
651pub fn serialize<T: Serialize + ?Sized, S: Serializer>(
652    data: &T,
653    serializer: S,
654) -> Result<S::Ok, S::Error> {
655    to_value(data)
656        .map_err(serde::ser::Error::custom)?
657        .serialize(serializer)
658}
659
660#[expect(
661    clippy::as_conversions,
662    clippy::cast_possible_truncation,
663    clippy::cast_sign_loss,
664    reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
665)]
666fn finite_value(value: &JsValue) -> Result<Value, serde_json::Error> {
667    match value {
668        JsValue::Null => Ok(Value::Null),
669        JsValue::Bool(b) => Ok(Value::Bool(*b)),
670        JsValue::Number(n) if !n.is_finite() => Ok(Value::Null),
671        JsValue::Number(n)
672            if n.fract() == 0.0
673                && (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0).contains(n) =>
674        {
675            Ok(Value::Number(Number::from(*n as i64)))
676        }
677        JsValue::Number(n)
678            if n.fract() == 0.0 && (0.0..18_446_744_073_709_552_000.0).contains(n) =>
679        {
680            Ok(Value::Number(Number::from(*n as u64)))
681        }
682        JsValue::Number(n) => Number::from_f64(*n)
683            .map(Value::Number)
684            .ok_or_else(|| invalid("invalid finite JSON number")),
685        JsValue::String(s) => Ok(Value::String(s.clone())),
686        JsValue::Array(a) => a
687            .iter()
688            .map(finite_value)
689            .collect::<Result<_, _>>()
690            .map(Value::Array),
691        JsValue::Object(m) => {
692            let mut entries: Vec<_> = m.iter().collect();
693            entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
694            let mut values = serde_json::Map::new();
695            for (key, value) in entries {
696                _ = values.insert(key.clone(), finite_value(value)?);
697            }
698            Ok(Value::Object(values))
699        }
700    }
701}
702
703fn write_value(value: &JsValue, bytes: &mut Vec<u8>) -> Result<(), serde_json::Error> {
704    match value {
705        JsValue::Null => bytes.extend_from_slice(b"null"),
706        JsValue::Bool(true) => bytes.extend_from_slice(b"true"),
707        JsValue::Bool(false) => bytes.extend_from_slice(b"false"),
708        JsValue::Number(n) => {
709            if n.is_finite() {
710                bytes.extend_from_slice(ryu_js::Buffer::new().format_finite(*n).as_bytes());
711            } else {
712                bytes.extend_from_slice(b"null");
713            }
714        }
715        JsValue::String(s) => serde_json::to_writer(bytes, s)?,
716        JsValue::Array(a) => {
717            bytes.push(b'[');
718            for (index, item) in a.iter().enumerate() {
719                if index != 0 {
720                    bytes.push(b',');
721                }
722                write_value(item, bytes)?;
723            }
724            bytes.push(b']');
725        }
726        JsValue::Object(m) => {
727            let mut entries: Vec<_> = m.iter().collect();
728            entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
729            bytes.push(b'{');
730            for (index, (key, value)) in entries.into_iter().enumerate() {
731                if index != 0 {
732                    bytes.push(b',');
733                }
734                serde_json::to_writer(&mut *bytes, key)?;
735                bytes.push(b':');
736                write_value(value, bytes)?;
737            }
738            bytes.push(b'}');
739        }
740    }
741    Ok(())
742}
743
744fn array_index(name: &str) -> Option<u32> {
745    let index = name.parse::<u32>().ok()?;
746    (index != u32::MAX && index.to_string() == name).then_some(index)
747}