1use 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 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 #[must_use]
97 pub const fn is_null(&self) -> bool {
98 matches!(self, Self::Null)
99 }
100 #[must_use]
101 pub const fn is_string(&self) -> bool {
102 matches!(self, Self::String(_))
103 }
104 #[must_use]
105 pub const fn is_boolean(&self) -> bool {
106 matches!(self, Self::Bool(_))
107 }
108 #[must_use]
109 pub const fn is_array(&self) -> bool {
110 matches!(self, Self::Array(_))
111 }
112 #[must_use]
113 pub const fn is_object(&self) -> bool {
114 matches!(self, Self::Object(_))
115 }
116 pub fn to_json_value(&self) -> Result<Value, serde_json::Error> {
122 finite_value(self)
123 }
124}
125
126impl From<Value> for JsValue {
127 fn from(value: Value) -> Self {
128 match value {
129 Value::Null => Self::Null,
130 Value::Bool(b) => Self::Bool(b),
131 Value::Number(n) => Self::Number(n.as_f64().unwrap_or_default()),
132 Value::String(s) => Self::String(s),
133 Value::Array(a) => Self::Array(a.into_iter().map(Self::from).collect()),
134 Value::Object(m) => {
135 Self::Object(m.into_iter().map(|(k, v)| (k, Self::from(v))).collect())
136 }
137 }
138 }
139}
140
141impl Serialize for JsValue {
142 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
143 match self {
144 Self::Null => serializer.serialize_unit(),
145 Self::Bool(b) => serializer.serialize_bool(*b),
146 Self::Number(n) => serializer.serialize_f64(*n),
147 Self::String(s) => serializer.serialize_str(s),
148 Self::Array(a) => a.serialize(serializer),
149 Self::Object(m) => m.serialize(serializer),
150 }
151 }
152}
153
154impl<'de> Deserialize<'de> for JsValue {
155 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
156 struct JsVisitor;
157 impl<'de> Visitor<'de> for JsVisitor {
158 type Value = JsValue;
159 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
160 formatter.write_str("a JSON value")
161 }
162 fn visit_unit<E>(self) -> Result<Self::Value, E> {
163 Ok(JsValue::Null)
164 }
165 fn visit_none<E>(self) -> Result<Self::Value, E> {
166 Ok(JsValue::Null)
167 }
168 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> {
169 Ok(JsValue::Bool(v))
170 }
171 #[expect(
172 clippy::as_conversions,
173 clippy::cast_precision_loss,
174 reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
175 )]
176 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
177 Ok(JsValue::Number(v as f64))
178 }
179 #[expect(
180 clippy::as_conversions,
181 clippy::cast_precision_loss,
182 reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
183 )]
184 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
185 Ok(JsValue::Number(v as f64))
186 }
187 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
188 Ok(JsValue::Number(v))
189 }
190 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
191 Ok(JsValue::String(v.into()))
192 }
193 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
194 Ok(JsValue::String(v))
195 }
196 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
197 let mut values = Vec::new();
198 while let Some(value) = seq.next_element()? {
199 values.push(value);
200 }
201 Ok(JsValue::Array(values))
202 }
203 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
204 let mut values = IndexMap::new();
205 while let Some((key, value)) = map.next_entry()? {
206 drop(values.insert(key, value));
207 }
208 Ok(JsValue::Object(values))
209 }
210 }
211 deserializer.deserialize_any(JsVisitor)
212 }
213}
214
215impl IntoDeserializer<'_, serde_json::Error> for JsValue {
216 type Deserializer = Self;
217 fn into_deserializer(self) -> Self {
218 self
219 }
220}
221
222macro_rules! signed_number {
223 ($($method:ident),*) => {$(
224#[expect(clippy::as_conversions, clippy::cast_possible_truncation, reason = "Finite integral values are checked against the target integer bounds before conversion")]
225 fn $method<V: Visitor<'de>>(self,visitor:V)->Result<V::Value,Self::Error> {
226 match self {
227 Self::Number(n) if n.is_finite() && n.fract()==0.0 && (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0).contains(&n) => visitor.visit_i64(n as i64),
228 value => value.deserialize_any(visitor),
229 }
230 }
231 )*};
232}
233
234macro_rules! unsigned_number {
235 ($($method:ident),*) => {$(
236#[expect(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "Finite integral values are checked against the target integer bounds before conversion")]
237 fn $method<V: Visitor<'de>>(self,visitor:V)->Result<V::Value,Self::Error> {
238 match self {
239 Self::Number(n) if n.is_finite() && n.fract()==0.0 && (0.0..18_446_744_073_709_552_000.0).contains(&n) => visitor.visit_u64(n as u64),
240 value => value.deserialize_any(visitor),
241 }
242 }
243 )*};
244}
245
246impl<'de> Deserializer<'de> for JsValue {
247 type Error = serde_json::Error;
248 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
249 match self {
250 Self::Null => visitor.visit_unit(),
251 Self::Bool(b) => visitor.visit_bool(b),
252 Self::Number(n) => visitor.visit_f64(n),
253 Self::String(s) => visitor.visit_string(s),
254 Self::Array(a) => {
255 visitor.visit_seq(serde::de::value::SeqDeserializer::new(a.into_iter()))
256 }
257 Self::Object(m) => {
258 visitor.visit_map(serde::de::value::MapDeserializer::new(m.into_iter()))
259 }
260 }
261 }
262 fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
263 if self.is_null() {
264 visitor.visit_none()
265 } else {
266 visitor.visit_some(self)
267 }
268 }
269 fn deserialize_newtype_struct<V: Visitor<'de>>(
270 self,
271 _name: &'static str,
272 visitor: V,
273 ) -> Result<V::Value, Self::Error> {
274 visitor.visit_newtype_struct(self)
275 }
276 fn deserialize_enum<V: Visitor<'de>>(
277 self,
278 _name: &'static str,
279 _variants: &'static [&'static str],
280 visitor: V,
281 ) -> Result<V::Value, Self::Error> {
282 match self {
283 Self::String(s) => visitor.visit_enum(s.into_deserializer()),
284 Self::Object(map) => visitor.visit_enum(serde::de::value::MapAccessDeserializer::new(
285 serde::de::value::MapDeserializer::new(map.into_iter()),
286 )),
287 value @ (Self::Null | Self::Bool(_) | Self::Number(_) | Self::Array(_)) => {
288 value.deserialize_any(visitor)
289 }
290 }
291 }
292 signed_number!(
293 deserialize_i8,
294 deserialize_i16,
295 deserialize_i32,
296 deserialize_i64,
297 deserialize_i128
298 );
299 unsigned_number!(
300 deserialize_u8,
301 deserialize_u16,
302 deserialize_u32,
303 deserialize_u64,
304 deserialize_u128
305 );
306 serde::forward_to_deserialize_any! {
307 bool f32 f64 char str string bytes
308 byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any
309 }
310}
311
312struct Parser<'a> {
313 input: &'a str,
314 position: usize,
315}
316
317impl Parser<'_> {
318 fn peek(&self) -> Option<u8> {
319 self.input.as_bytes().get(self.position).copied()
320 }
321 fn whitespace(&mut self) {
322 while self
323 .peek()
324 .is_some_and(|b| matches!(b, b' ' | b'\n' | b'\r' | b'\t'))
325 {
326 self.position += 1;
327 }
328 }
329 fn take(&mut self, byte: u8) -> bool {
330 if self.peek() == Some(byte) {
331 self.position += 1;
332 true
333 } else {
334 false
335 }
336 }
337 fn string(&mut self) -> Result<String, serde_json::Error> {
338 let start = self.position;
339 if !self.take(b'"') {
340 return Err(invalid("expected JSON string"));
341 }
342 loop {
343 match self.peek() {
344 Some(b'"') => {
345 self.position += 1;
346 return serde_json::from_str(
347 self.input
348 .get(start..self.position)
349 .ok_or_else(|| invalid("invalid JSON string bounds"))?,
350 );
351 }
352 Some(b'\\') => {
353 self.position += 1;
354 if self.peek().is_none() {
355 return Err(invalid("unterminated JSON escape"));
356 }
357 self.position += 1;
358 }
359 Some(_) => self.position += 1,
360 None => return Err(invalid("unterminated JSON string")),
361 }
362 }
363 }
364 fn value(&mut self, depth: usize) -> Result<JsValue, serde_json::Error> {
365 self.whitespace();
366 match self.peek() {
367 Some(b'"') => self.string().map(JsValue::String),
368 Some(b'{') => {
369 if depth >= 127 {
370 return Err(invalid("recursion limit exceeded"));
371 }
372 self.position += 1;
373 self.whitespace();
374 let mut values = IndexMap::new();
375 if self.take(b'}') {
376 return Ok(JsValue::Object(values));
377 }
378 loop {
379 self.whitespace();
380 let key = self.string()?;
381 self.whitespace();
382 if !self.take(b':') {
383 return Err(invalid("expected JSON colon"));
384 }
385 let value = self.value(depth + 1)?;
386 drop(values.insert(key, value));
387 self.whitespace();
388 if self.take(b'}') {
389 break;
390 }
391 if !self.take(b',') {
392 return Err(invalid("expected JSON object delimiter"));
393 }
394 }
395 Ok(JsValue::Object(values))
396 }
397 Some(b'[') => {
398 if depth >= 127 {
399 return Err(invalid("recursion limit exceeded"));
400 }
401 self.position += 1;
402 self.whitespace();
403 let mut values = Vec::new();
404 if self.take(b']') {
405 return Ok(JsValue::Array(values));
406 }
407 loop {
408 values.push(self.value(depth + 1)?);
409 self.whitespace();
410 if self.take(b']') {
411 break;
412 }
413 if !self.take(b',') {
414 return Err(invalid("expected JSON array delimiter"));
415 }
416 }
417 Ok(JsValue::Array(values))
418 }
419 Some(b't') => self.literal("true", JsValue::Bool(true)),
420 Some(b'f') => self.literal("false", JsValue::Bool(false)),
421 Some(b'n') => self.literal("null", JsValue::Null),
422 Some(b'-' | b'0'..=b'9') => self.number(),
423 _ => Err(invalid("expected JSON value")),
424 }
425 }
426 fn literal(&mut self, text: &str, value: JsValue) -> Result<JsValue, serde_json::Error> {
427 if self
428 .input
429 .get(self.position..)
430 .is_some_and(|rest| rest.starts_with(text))
431 {
432 self.position += text.len();
433 Ok(value)
434 } else {
435 Err(invalid("invalid JSON literal"))
436 }
437 }
438 fn digits(&mut self) -> bool {
439 let start = self.position;
440 while self.peek().is_some_and(|b| b.is_ascii_digit()) {
441 self.position += 1;
442 }
443 self.position > start
444 }
445 fn number(&mut self) -> Result<JsValue, serde_json::Error> {
446 let start = self.position;
447 let _ignored_take = self.take(b'-');
448 if !self.take(b'0') && !self.digits() {
449 return Err(invalid("invalid JSON number"));
450 }
451 if self.take(b'.') && !self.digits() {
452 return Err(invalid("invalid JSON fraction"));
453 }
454 if self.take(b'e') || self.take(b'E') {
455 let _ignored_take_2 = self.take(b'+') || self.take(b'-');
456 if !self.digits() {
457 return Err(invalid("invalid JSON exponent"));
458 }
459 }
460 self.input
461 .get(start..self.position)
462 .ok_or_else(|| invalid("invalid JSON number bounds"))?
463 .parse::<f64>()
464 .map(JsValue::Number)
465 .map_err(|_error| invalid("invalid JSON number"))
466 }
467}
468
469pub fn parse_value(input: &str) -> Result<JsValue, serde_json::Error> {
476 let mut parser = Parser { input, position: 0 };
477 let value = parser.value(0)?;
478 parser.whitespace();
479 if parser.position != input.len() {
480 return Err(invalid("trailing JSON input"));
481 }
482 Ok(value)
483}
484
485pub fn from_slice<T: DeserializeOwned + 'static>(input: &[u8]) -> Result<T, serde_json::Error> {
490 let text = std::str::from_utf8(input).map_err(|_error| invalid("invalid JSON UTF8"))?;
491 from_value(parse_value(text)?)
492}
493
494pub fn from_value<T: DeserializeOwned + 'static>(input: JsValue) -> Result<T, serde_json::Error> {
504 if TypeId::of::<T>() == TypeId::of::<Value>() {
505 let value: Box<dyn Any> = Box::new(input.to_json_value()?);
506 return value
507 .downcast::<T>()
508 .map(|value| *value)
509 .map_err(|_error| invalid("invalid JSON value type"));
510 }
511 T::deserialize(input)
512}
513
514pub fn deserialize_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Value, D::Error> {
520 JsValue::deserialize(deserializer)?
521 .to_json_value()
522 .map_err(serde::de::Error::custom)
523}
524
525pub fn deserialize_optional_value<'de, D: Deserializer<'de>>(
531 deserializer: D,
532) -> Result<Option<Value>, D::Error> {
533 Option::<JsValue>::deserialize(deserializer)?
534 .map(|value| value.to_json_value().map_err(serde::de::Error::custom))
535 .transpose()
536}
537
538pub fn deserialize_map<'de, D: Deserializer<'de>>(
544 deserializer: D,
545) -> Result<serde_json::Map<String, Value>, D::Error> {
546 match deserialize_value(deserializer)? {
547 Value::Object(map) => Ok(map),
548 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Array(_) => {
549 Err(serde::de::Error::custom("expected JSON object"))
550 }
551 }
552}
553
554pub fn deserialize_optional_map<'de, D: Deserializer<'de>>(
560 deserializer: D,
561) -> Result<Option<serde_json::Map<String, Value>>, D::Error> {
562 deserialize_optional_value(deserializer)?
563 .map(|value| match value {
564 Value::Object(map) => Ok(map),
565 Value::Null
566 | Value::Bool(_)
567 | Value::Number(_)
568 | Value::String(_)
569 | Value::Array(_) => Err(serde::de::Error::custom("expected JSON object")),
570 })
571 .transpose()
572}
573
574pub fn deserialize_btree_map<'de, D: Deserializer<'de>>(
580 deserializer: D,
581) -> Result<std::collections::BTreeMap<String, Value>, D::Error> {
582 deserialize_map(deserializer).map(|map| map.into_iter().collect())
583}
584
585fn invalid(message: &str) -> serde_json::Error {
586 <serde_json::Error as serde::de::Error>::custom(message)
587}
588
589#[must_use]
590pub fn number_as_f64(number: &Number) -> Option<f64> {
591 number.as_f64()
592}
593
594pub fn number_to_string(number: &Number) -> Result<String, serde_json::Error> {
599 Ok(ryu_js::Buffer::new()
600 .format(number_as_f64(number).ok_or_else(|| invalid("invalid JSON number"))?)
601 .to_owned())
602}
603
604pub fn to_vec<T: Serialize + ?Sized>(data: &T) -> Result<Vec<u8>, serde_json::Error> {
610 let value = JsValue::from(serde_json::to_value(data)?);
611 let mut bytes = Vec::new();
612 write_value(&value, &mut bytes)?;
613 Ok(bytes)
614}
615
616pub fn to_string<T: Serialize + ?Sized>(data: &T) -> Result<String, serde_json::Error> {
621 String::from_utf8(to_vec(data)?).map_err(|_error| invalid("invalid JSON output UTF8"))
622}
623
624pub fn to_value<T: Serialize + ?Sized>(data: &T) -> Result<Value, serde_json::Error> {
629 finite_value(&JsValue::from(serde_json::to_value(data)?))
630}
631
632pub fn serialize<T: Serialize + ?Sized, S: Serializer>(
637 data: &T,
638 serializer: S,
639) -> Result<S::Ok, S::Error> {
640 to_value(data)
641 .map_err(serde::ser::Error::custom)?
642 .serialize(serializer)
643}
644
645#[expect(
646 clippy::as_conversions,
647 clippy::cast_possible_truncation,
648 clippy::cast_sign_loss,
649 reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
650)]
651fn finite_value(value: &JsValue) -> Result<Value, serde_json::Error> {
652 match value {
653 JsValue::Null => Ok(Value::Null),
654 JsValue::Bool(b) => Ok(Value::Bool(*b)),
655 JsValue::Number(n) if !n.is_finite() => Ok(Value::Null),
656 JsValue::Number(n)
657 if n.fract() == 0.0
658 && (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0).contains(n) =>
659 {
660 Ok(Value::Number(Number::from(*n as i64)))
661 }
662 JsValue::Number(n)
663 if n.fract() == 0.0 && (0.0..18_446_744_073_709_552_000.0).contains(n) =>
664 {
665 Ok(Value::Number(Number::from(*n as u64)))
666 }
667 JsValue::Number(n) => Number::from_f64(*n)
668 .map(Value::Number)
669 .ok_or_else(|| invalid("invalid finite JSON number")),
670 JsValue::String(s) => Ok(Value::String(s.clone())),
671 JsValue::Array(a) => a
672 .iter()
673 .map(finite_value)
674 .collect::<Result<_, _>>()
675 .map(Value::Array),
676 JsValue::Object(m) => {
677 let mut entries: Vec<_> = m.iter().collect();
678 entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
679 let mut values = serde_json::Map::new();
680 for (key, value_2) in entries {
681 drop(values.insert(key.clone(), finite_value(value_2)?));
682 }
683 Ok(Value::Object(values))
684 }
685 }
686}
687
688fn write_value(value: &JsValue, bytes: &mut Vec<u8>) -> Result<(), serde_json::Error> {
689 match value {
690 JsValue::Null => bytes.extend_from_slice(b"null"),
691 JsValue::Bool(true) => bytes.extend_from_slice(b"true"),
692 JsValue::Bool(false) => bytes.extend_from_slice(b"false"),
693 JsValue::Number(n) => {
694 if n.is_finite() {
695 bytes.extend_from_slice(ryu_js::Buffer::new().format_finite(*n).as_bytes());
696 } else {
697 bytes.extend_from_slice(b"null");
698 }
699 }
700 JsValue::String(s) => serde_json::to_writer(bytes, s)?,
701 JsValue::Array(a) => {
702 bytes.push(b'[');
703 for (index, value_2) in a.iter().enumerate() {
704 if index != 0 {
705 bytes.push(b',');
706 }
707 write_value(value_2, bytes)?;
708 }
709 bytes.push(b']');
710 }
711 JsValue::Object(m) => {
712 let mut entries: Vec<_> = m.iter().collect();
713 entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
714 bytes.push(b'{');
715 for (index, (key, value_3)) in entries.into_iter().enumerate() {
716 if index != 0 {
717 bytes.push(b',');
718 }
719 serde_json::to_writer(&mut *bytes, key)?;
720 bytes.push(b':');
721 write_value(value_3, bytes)?;
722 }
723 bytes.push(b'}');
724 }
725 }
726 Ok(())
727}
728
729fn array_index(name: &str) -> Option<u32> {
730 let index = name.parse::<u32>().ok()?;
731 (index != u32::MAX && index.to_string() == name).then_some(index)
732}