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