1use indexmap::IndexMap;
6use num_bigint::BigInt;
7use std::fmt;
8
9pub type Map = IndexMap<String, Value>;
11
12#[derive(Clone, Debug, PartialEq, Default)]
14pub enum Value {
15 #[default]
18 Undefined,
19 Null,
21 Bool(bool),
22 Num(f64),
24 Str(String),
25 BigInt(BigInt),
26 Arr(Vec<Value>),
27 Obj(Map),
28 Date(i64),
30 Func(u64),
32}
33
34impl Value {
35 pub fn is_undefined(&self) -> bool {
36 matches!(self, Value::Undefined)
37 }
38
39 pub fn is_null(&self) -> bool {
40 matches!(self, Value::Null)
41 }
42
43 pub fn type_of(&self) -> &'static str {
46 match self {
47 Value::Undefined => "undefined",
48 Value::Null => "object",
49 Value::Bool(_) => "boolean",
50 Value::Num(_) => "number",
51 Value::Str(_) => "string",
52 Value::BigInt(_) => "bigint",
53 Value::Arr(_) => "object",
54 Value::Obj(_) => "object",
55 Value::Date(_) => "object",
56 Value::Func(_) => "function",
57 }
58 }
59
60 pub fn as_obj(&self) -> Option<&Map> {
61 match self {
62 Value::Obj(m) => Some(m),
63 _ => None,
64 }
65 }
66
67 pub fn as_arr(&self) -> Option<&Vec<Value>> {
68 match self {
69 Value::Arr(a) => Some(a),
70 _ => None,
71 }
72 }
73
74 pub fn as_str(&self) -> Option<&str> {
75 match self {
76 Value::Str(s) => Some(s),
77 _ => None,
78 }
79 }
80
81 pub fn as_f64(&self) -> Option<f64> {
82 match self {
83 Value::Num(n) => Some(*n),
84 _ => None,
85 }
86 }
87}
88
89impl From<bool> for Value {
90 fn from(b: bool) -> Self {
91 Value::Bool(b)
92 }
93}
94impl From<f64> for Value {
95 fn from(n: f64) -> Self {
96 Value::Num(n)
97 }
98}
99impl From<i64> for Value {
100 fn from(n: i64) -> Self {
101 Value::Num(n as f64)
102 }
103}
104impl From<i32> for Value {
105 fn from(n: i32) -> Self {
106 Value::Num(n as f64)
107 }
108}
109impl From<u32> for Value {
110 fn from(n: u32) -> Self {
111 Value::Num(n as f64)
112 }
113}
114impl From<usize> for Value {
115 fn from(n: usize) -> Self {
116 Value::Num(n as f64)
117 }
118}
119impl From<&str> for Value {
120 fn from(s: &str) -> Self {
121 Value::Str(s.to_string())
122 }
123}
124impl From<String> for Value {
125 fn from(s: String) -> Self {
126 Value::Str(s)
127 }
128}
129impl From<Vec<Value>> for Value {
130 fn from(a: Vec<Value>) -> Self {
131 Value::Arr(a)
132 }
133}
134impl From<Map> for Value {
135 fn from(m: Map) -> Self {
136 Value::Obj(m)
137 }
138}
139impl From<BigInt> for Value {
140 fn from(b: BigInt) -> Self {
141 Value::BigInt(b)
142 }
143}
144
145pub fn js_number(x: f64) -> String {
149 if x.is_nan() {
150 return "NaN".to_string();
151 }
152 if x == 0.0 {
153 return "0".to_string();
154 }
155 if x.is_infinite() {
156 return if x > 0.0 { "Infinity" } else { "-Infinity" }.to_string();
157 }
158 if x.abs() < 9007199254740992.0 && x == x.trunc() {
160 return (x as i64).to_string();
161 }
162 let neg = x < 0.0;
163 let sci = format!("{:e}", x.abs());
165 let (mant, exp) = sci.split_once('e').unwrap_or((&sci, "0"));
166 let e: i32 = exp.parse().unwrap_or(0);
167 let digits: String = mant.chars().filter(|c| *c != '.').collect();
168 let k = digits.len() as i32;
169 let n = e + 1;
171 let body = if k <= n && n <= 21 {
172 format!("{}{}", digits, "0".repeat((n - k) as usize))
173 } else if 0 < n && n <= 21 {
174 let (a, b) = digits.split_at(n as usize);
175 format!("{}.{}", a, b)
176 } else if -6 < n && n <= 0 {
177 format!("0.{}{}", "0".repeat((-n) as usize), digits)
178 } else {
179 let ee = n - 1;
180 let esign = if ee < 0 { "-" } else { "+" };
181 if k == 1 {
182 format!("{}e{}{}", digits, esign, ee.abs())
183 } else {
184 let (a, b) = digits.split_at(1);
185 format!("{}.{}e{}{}", a, b, esign, ee.abs())
186 }
187 };
188 if neg {
189 format!("-{}", body)
190 } else {
191 body
192 }
193}
194
195pub fn js_date_string(ms: i64) -> String {
197 let days = ms.div_euclid(86_400_000);
198 let rem = ms.rem_euclid(86_400_000);
199 let (y, m, d) = civil_from_days(days);
200 let h = rem / 3_600_000;
201 let mi = (rem / 60_000) % 60;
202 let s = (rem / 1000) % 60;
203 let milli = rem % 1000;
204 let year = if (0..=9999).contains(&y) {
205 format!("{:04}", y)
206 } else if y < 0 {
207 format!("-{:06}", -y)
208 } else {
209 format!("+{:06}", y)
210 };
211 format!(
212 "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
213 year, m, d, h, mi, s, milli
214 )
215}
216
217fn civil_from_days(z: i64) -> (i64, u32, u32) {
219 let z = z + 719_468;
220 let era = z.div_euclid(146_097);
221 let doe = z.rem_euclid(146_097);
222 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
223 let y = yoe + era * 400;
224 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
225 let mp = (5 * doy + 2) / 153;
226 let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
227 let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
228 (if m <= 2 { y + 1 } else { y }, m, d)
229}
230
231pub fn json_text(s: &str) -> String {
233 let mut out = String::with_capacity(s.len() + 2);
234 out.push('"');
235 for c in s.chars() {
236 match c {
237 '"' => out.push_str("\\\""),
238 '\\' => out.push_str("\\\\"),
239 '\n' => out.push_str("\\n"),
240 '\r' => out.push_str("\\r"),
241 '\t' => out.push_str("\\t"),
242 '\u{8}' => out.push_str("\\b"),
243 '\u{c}' => out.push_str("\\f"),
244 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
245 c => out.push(c),
246 }
247 }
248 out.push('"');
249 out
250}
251
252pub fn json_render(v: &Value) -> String {
256 match v {
257 Value::Undefined | Value::Null => "null".to_string(),
258 Value::Bool(b) => b.to_string(),
259 Value::Num(n) => {
260 if n.is_nan() {
261 "\"NaN\"".to_string()
262 } else if n.is_infinite() {
263 "null".to_string()
264 } else {
265 js_number(*n)
266 }
267 }
268 Value::Str(s) => json_text(s),
269 Value::BigInt(b) => format!("\"{}\"", b),
270 Value::Date(ms) => format!("\"{}\"", js_date_string(*ms)),
271 Value::Func(_) => "null".to_string(),
272 Value::Arr(a) => {
273 let parts: Vec<String> = a.iter().map(json_render).collect();
274 format!("[{}]", parts.join(","))
275 }
276 Value::Obj(m) => {
277 let parts: Vec<String> = m
278 .iter()
279 .filter(|(_, v)| !v.is_undefined())
280 .map(|(k, v)| format!("{}:{}", json_text(k), json_render(v)))
281 .collect();
282 format!("{{{}}}", parts.join(","))
283 }
284 }
285}
286
287pub const ERR_VALUE_LIMIT: usize = 111;
289
290pub fn truncate_text(s: &str, limit: usize) -> String {
292 let n = s.chars().count();
293 if n <= limit {
294 return s.to_string();
295 }
296 if limit < 3 {
297 return s.chars().take(limit).collect();
298 }
299 let head: String = s.chars().take(limit - 3).collect();
300 format!("{}...", head)
301}
302
303pub fn value_to_string(v: &Value) -> String {
305 match v {
306 Value::Undefined => "undefined".to_string(),
307 Value::Null => "null".to_string(),
308 Value::Str(s) => {
309 let mut out = String::with_capacity(s.len());
312 for c in s.chars() {
313 match c {
314 '"' => out.push('\\'),
315 '\\' => out.push_str("\\\\"),
316 '\n' => out.push_str("\\n"),
317 '\r' => out.push_str("\\r"),
318 '\t' => out.push_str("\\t"),
319 c => match c as u32 {
320 8 => out.push_str("\\b"),
321 12 => out.push_str("\\f"),
322 n if n < 0x20 => out.push_str(&format!("\\u{:04x}", n)),
323 _ => out.push(c),
324 },
325 }
326 }
327 if out.chars().count() <= ERR_VALUE_LIMIT {
328 out
329 } else {
330 truncate_text(&out, ERR_VALUE_LIMIT)
331 }
332 }
333 Value::Num(n) => js_number(*n),
334 Value::Bool(b) => b.to_string(),
335 Value::BigInt(b) => b.to_string(),
336 Value::Date(ms) => js_date_string(*ms),
337 Value::Func(_) => "function".to_string(),
338 Value::Arr(_) | Value::Obj(_) => {
339 truncate_text(&json_render(v).replace('"', ""), ERR_VALUE_LIMIT)
340 }
341 }
342}
343
344pub fn value_kind(v: &Value) -> &'static str {
347 match v {
348 Value::Undefined | Value::Null => "value",
349 Value::Num(n) if n.is_nan() => "value",
350 Value::Num(_) => "number",
351 Value::Str(_) => "string",
352 Value::Bool(_) => "boolean",
353 Value::BigInt(_) => "bigint",
354 Value::Arr(_) => "array",
355 Value::Obj(_) | Value::Date(_) => "object",
356 Value::Func(_) => "function",
357 }
358}
359
360impl fmt::Display for Value {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 f.write_str(&value_to_string(self))
363 }
364}
365
366pub fn is_integer(x: f64) -> bool {
368 x.is_finite() && (x.abs() >= 9007199254740992.0 || (x as i64) as f64 == x)
371}
372
373#[cfg(feature = "serde")]
374mod serde_conv {
375 use super::{Map, Value};
376
377 impl From<serde_json::Value> for Value {
378 fn from(v: serde_json::Value) -> Self {
379 match v {
380 serde_json::Value::Null => Value::Null,
381 serde_json::Value::Bool(b) => Value::Bool(b),
382 serde_json::Value::Number(n) => Value::Num(n.as_f64().unwrap_or(f64::NAN)),
383 serde_json::Value::String(s) => Value::Str(s),
384 serde_json::Value::Array(a) => Value::Arr(a.into_iter().map(Value::from).collect()),
385 serde_json::Value::Object(m) => {
386 let mut out = Map::with_capacity(m.len());
387 for (k, v) in m {
388 out.insert(k, Value::from(v));
389 }
390 Value::Obj(out)
391 }
392 }
393 }
394 }
395
396 impl From<Value> for serde_json::Value {
397 fn from(v: Value) -> Self {
400 match v {
401 Value::Undefined | Value::Null => serde_json::Value::Null,
402 Value::Bool(b) => serde_json::Value::Bool(b),
403 Value::Num(n) if n.fract() == 0.0 && n.abs() < 9007199254740992.0 => {
406 serde_json::Value::from(n as i64)
407 }
408 Value::Num(n) => serde_json::Number::from_f64(n)
409 .map(serde_json::Value::Number)
410 .unwrap_or(serde_json::Value::Null),
411 Value::Str(s) => serde_json::Value::String(s),
412 Value::BigInt(b) => serde_json::Value::String(b.to_string()),
413 Value::Date(ms) => serde_json::Value::String(super::js_date_string(ms)),
414 Value::Func(_) => serde_json::Value::Null,
415 Value::Arr(a) => {
416 serde_json::Value::Array(a.into_iter().map(serde_json::Value::from).collect())
417 }
418 Value::Obj(m) => {
419 let mut out = serde_json::Map::with_capacity(m.len());
420 for (k, v) in m {
421 if !v.is_undefined() {
422 out.insert(k, serde_json::Value::from(v));
423 }
424 }
425 serde_json::Value::Object(out)
426 }
427 }
428 }
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 fn numbers_print_as_javascript_does() {
438 let cases: [(f64, &str); 19] = [
439 (0.0, "0"),
440 (1.0, "1"),
441 (-1.0, "-1"),
442 (-1.5, "-1.5"),
443 (-1e21, "-1e+21"),
444 (-2.5e-7, "-2.5e-7"),
445 (9007199254740993.0, "9007199254740992"),
446 (4503599627370496.5, "4503599627370496"),
447 (1.5, "1.5"),
448 (0.1, "0.1"),
449 (100.0, "100"),
450 (1e21, "1e+21"),
451 (1e20, "100000000000000000000"),
452 (123456789012345680000.0, "123456789012345680000"),
453 (1e-7, "1e-7"),
454 (0.000001, "0.000001"),
455 (1.5e-7, "1.5e-7"),
456 (1.2345e25, "1.2345e+25"),
457 (f64::NAN, "NaN"),
458 ];
459 for (x, want) in cases {
460 assert_eq!(js_number(x), want, "{}", x);
461 }
462 assert_eq!(js_number(f64::INFINITY), "Infinity");
463 assert_eq!(js_number(f64::NEG_INFINITY), "-Infinity");
464 }
465
466 #[test]
467 fn dates_print_as_iso() {
468 assert_eq!(js_date_string(0), "1970-01-01T00:00:00.000Z");
469 assert_eq!(
470 js_date_string(4_765_132_800_000),
471 "2121-01-01T00:00:00.000Z"
472 );
473 assert_eq!(js_date_string(-1), "1969-12-31T23:59:59.999Z");
474 assert_eq!(
475 js_date_string(-62_198_755_200_000),
476 "-000001-01-01T00:00:00.000Z"
477 );
478 assert_eq!(
479 js_date_string(253_402_300_800_000),
480 "+010000-01-01T00:00:00.000Z"
481 );
482 }
483
484 #[test]
485 fn strings_render_with_json_escapes_and_no_quotes() {
486 assert_eq!(json_text("a\"b\\c\n\u{1}"), "\"a\\\"b\\\\c\\n\\u0001\"");
487 assert_eq!(value_to_string(&Value::from("a\"b\\c")), "a\\b\\\\c");
488 assert_eq!(
490 value_to_string(&Value::from("r\rt\tb\u{8}f\u{c}c\u{1}x")),
491 "r\\rt\\tb\\bf\\fc\\u0001x"
492 );
493 assert_eq!(
494 value_to_string(&Value::from("r\rt\tb\u{8}f\u{c}c\u{1}x")),
495 json_text("r\rt\tb\u{8}f\u{c}c\u{1}x").replace('"', "")
496 );
497 let long = "x".repeat(200);
498 let t = value_to_string(&Value::from(long.as_str()));
499 assert_eq!(t.len(), ERR_VALUE_LIMIT);
500 assert!(t.ends_with("..."));
501 assert_eq!(truncate_text("abcdef", 2), "ab");
502 }
503
504 #[test]
505 fn containers_render_in_insertion_order() {
506 let mut m = Map::new();
507 m.insert("b".into(), Value::from(1));
508 m.insert(
509 "a".into(),
510 Value::Arr(vec![
511 Value::Undefined,
512 Value::from("x"),
513 Value::Num(f64::NAN),
514 ]),
515 );
516 m.insert("u".into(), Value::Undefined);
517 m.insert("d".into(), Value::Date(0));
518 assert_eq!(
519 value_to_string(&Value::Obj(m)),
520 "{b:1,a:[null,x,NaN],d:1970-01-01T00:00:00.000Z}"
521 );
522 assert_eq!(json_render(&Value::Num(f64::INFINITY)), "null");
523 assert_eq!(value_kind(&Value::Arr(vec![])), "array");
524 assert_eq!(value_kind(&Value::Num(f64::NAN)), "value");
525 assert_eq!(value_kind(&Value::Date(0)), "object");
526 assert_eq!(value_to_string(&Value::BigInt(BigInt::from(7))), "7");
527 assert_eq!(json_render(&Value::BigInt(BigInt::from(7))), "\"7\"");
528 assert_eq!(value_to_string(&Value::Func(1)), "function");
529 assert_eq!(value_to_string(&Value::Date(0)), "1970-01-01T00:00:00.000Z");
530 assert_eq!(json_text("\r\t"), "\"\\r\\t\"");
531 assert_eq!(json_render(&Value::Func(1)), "null");
532 assert_eq!(value_kind(&Value::Func(1)), "function");
533 assert_eq!(value_kind(&Value::BigInt(BigInt::from(1))), "bigint");
534 assert_eq!(Value::Func(1).type_of(), "function");
535 assert_eq!(Value::Null.type_of(), "object");
536 assert_eq!(Value::Undefined.type_of(), "undefined");
537 assert_eq!(Value::BigInt(BigInt::from(1)).type_of(), "bigint");
538 assert_eq!(Value::Date(0).type_of(), "object");
539 assert_eq!(format!("{}", Value::Null), "null");
540 assert_eq!(format!("{}", Value::Undefined), "undefined");
541 assert_eq!(format!("{}", Value::Bool(true)), "true");
542 assert!(is_integer(3.0) && !is_integer(3.5) && !is_integer(f64::NAN));
543 }
544
545 #[test]
546 fn conversions() {
547 assert_eq!(Value::from(true), Value::Bool(true));
548 assert_eq!(Value::from(2i64), Value::Num(2.0));
549 assert_eq!(Value::from(2i32), Value::Num(2.0));
550 assert_eq!(Value::from(2u32), Value::Num(2.0));
551 assert_eq!(Value::from(2usize), Value::Num(2.0));
552 assert_eq!(Value::from(String::from("s")), Value::Str("s".into()));
553 assert_eq!(
554 Value::from(vec![Value::Null]),
555 Value::Arr(vec![Value::Null])
556 );
557 assert_eq!(Value::from(Map::new()), Value::Obj(Map::new()));
558 assert_eq!(Value::from(BigInt::from(1)), Value::BigInt(BigInt::from(1)));
559 assert_eq!(Value::default(), Value::Undefined);
560 assert!(Value::Null.is_null());
561 assert_eq!(Value::from(1.5).as_f64(), Some(1.5));
562 assert_eq!(Value::from("x").as_str(), Some("x"));
563 assert!(Value::Null.as_str().is_none() && Value::Null.as_f64().is_none());
564 assert!(Value::Null.as_obj().is_none() && Value::Null.as_arr().is_none());
565 assert!(Value::Obj(Map::new()).as_obj().is_some());
566 assert!(Value::Arr(vec![]).as_arr().is_some());
567 }
568
569 #[cfg(feature = "serde")]
570 #[test]
571 fn serde_round_trip() {
572 let j: serde_json::Value =
573 serde_json::from_str(r#"{"b":1,"a":[null,"x",true],"c":{"z":1.5}}"#).unwrap();
574 let v = Value::from(j.clone());
575 let keys: Vec<&String> = v.as_obj().unwrap().keys().collect();
576 assert_eq!(keys, vec!["b", "a", "c"]);
577 assert_eq!(serde_json::Value::from(v), j);
578 let mut m = Map::new();
579 m.insert("u".into(), Value::Undefined);
580 m.insert("n".into(), Value::Num(f64::NAN));
581 m.insert("d".into(), Value::Date(0));
582 m.insert("b".into(), Value::BigInt(BigInt::from(3)));
583 m.insert("f".into(), Value::Func(0));
584 m.insert("e".into(), Value::Arr(vec![Value::Undefined]));
585 let out = serde_json::Value::from(Value::Obj(m)).to_string();
586 assert_eq!(
587 out,
588 r#"{"n":null,"d":"1970-01-01T00:00:00.000Z","b":"3","f":null,"e":[null]}"#
589 );
590 }
591
592 #[test]
593 fn remaining_kinds_render() {
594 let m = Map::new();
595 let cases: Vec<(Value, &str, &str)> = vec![
596 (Value::Num(1.0), "number", "number"),
597 (Value::Str("s".into()), "string", "string"),
598 (Value::BigInt(BigInt::from(2)), "bigint", "bigint"),
599 (Value::Arr(vec![]), "object", "array"),
600 (Value::Obj(m), "object", "object"),
601 (Value::Date(0), "object", "object"),
602 (Value::Func(1), "function", "function"),
603 (Value::Bool(true), "boolean", "boolean"),
604 (Value::Null, "object", "value"),
605 (Value::Undefined, "undefined", "value"),
606 (Value::Num(f64::NAN), "number", "value"),
607 ];
608 for (v, ty, kind) in cases {
609 assert_eq!(v.type_of(), ty, "{:?}", v);
610 assert_eq!(value_kind(&v), kind, "{:?}", v);
611 }
612 assert_eq!(value_to_string(&Value::Func(1)), "function");
613 assert_eq!(value_to_string(&Value::Date(0)), "1970-01-01T00:00:00.000Z");
614 assert_eq!(json_text("\r\t"), "\"\\r\\t\"");
615 assert_eq!(json_text("a\u{8}b\u{c}c\u{1}d"), "\"a\\bb\\fc\\u0001d\"");
616 }
617}