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 let neg = x < 0.0;
159 let sci = format!("{:e}", x.abs());
161 let (mant, exp) = sci.split_once('e').unwrap_or((&sci, "0"));
162 let e: i32 = exp.parse().unwrap_or(0);
163 let digits: String = mant.chars().filter(|c| *c != '.').collect();
164 let k = digits.len() as i32;
165 let n = e + 1;
167 let body = if k <= n && n <= 21 {
168 format!("{}{}", digits, "0".repeat((n - k) as usize))
169 } else if 0 < n && n <= 21 {
170 let (a, b) = digits.split_at(n as usize);
171 format!("{}.{}", a, b)
172 } else if -6 < n && n <= 0 {
173 format!("0.{}{}", "0".repeat((-n) as usize), digits)
174 } else {
175 let ee = n - 1;
176 let esign = if ee < 0 { "-" } else { "+" };
177 if k == 1 {
178 format!("{}e{}{}", digits, esign, ee.abs())
179 } else {
180 let (a, b) = digits.split_at(1);
181 format!("{}.{}e{}{}", a, b, esign, ee.abs())
182 }
183 };
184 if neg {
185 format!("-{}", body)
186 } else {
187 body
188 }
189}
190
191pub fn js_date_string(ms: i64) -> String {
193 let days = ms.div_euclid(86_400_000);
194 let rem = ms.rem_euclid(86_400_000);
195 let (y, m, d) = civil_from_days(days);
196 let h = rem / 3_600_000;
197 let mi = (rem / 60_000) % 60;
198 let s = (rem / 1000) % 60;
199 let milli = rem % 1000;
200 let year = if (0..=9999).contains(&y) {
201 format!("{:04}", y)
202 } else if y < 0 {
203 format!("-{:06}", -y)
204 } else {
205 format!("+{:06}", y)
206 };
207 format!(
208 "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
209 year, m, d, h, mi, s, milli
210 )
211}
212
213fn civil_from_days(z: i64) -> (i64, u32, u32) {
215 let z = z + 719_468;
216 let era = z.div_euclid(146_097);
217 let doe = z.rem_euclid(146_097);
218 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
219 let y = yoe + era * 400;
220 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
221 let mp = (5 * doy + 2) / 153;
222 let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
223 let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
224 (if m <= 2 { y + 1 } else { y }, m, d)
225}
226
227pub fn json_text(s: &str) -> String {
229 let mut out = String::with_capacity(s.len() + 2);
230 out.push('"');
231 for c in s.chars() {
232 match c {
233 '"' => out.push_str("\\\""),
234 '\\' => out.push_str("\\\\"),
235 '\n' => out.push_str("\\n"),
236 '\r' => out.push_str("\\r"),
237 '\t' => out.push_str("\\t"),
238 '\u{8}' => out.push_str("\\b"),
239 '\u{c}' => out.push_str("\\f"),
240 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
241 c => out.push(c),
242 }
243 }
244 out.push('"');
245 out
246}
247
248pub fn json_render(v: &Value) -> String {
252 match v {
253 Value::Undefined | Value::Null => "null".to_string(),
254 Value::Bool(b) => b.to_string(),
255 Value::Num(n) => {
256 if n.is_nan() {
257 "\"NaN\"".to_string()
258 } else if n.is_infinite() {
259 "null".to_string()
260 } else {
261 js_number(*n)
262 }
263 }
264 Value::Str(s) => json_text(s),
265 Value::BigInt(b) => format!("\"{}\"", b),
266 Value::Date(ms) => format!("\"{}\"", js_date_string(*ms)),
267 Value::Func(_) => "null".to_string(),
268 Value::Arr(a) => {
269 let parts: Vec<String> = a.iter().map(json_render).collect();
270 format!("[{}]", parts.join(","))
271 }
272 Value::Obj(m) => {
273 let parts: Vec<String> = m
274 .iter()
275 .filter(|(_, v)| !v.is_undefined())
276 .map(|(k, v)| format!("{}:{}", json_text(k), json_render(v)))
277 .collect();
278 format!("{{{}}}", parts.join(","))
279 }
280 }
281}
282
283pub const ERR_VALUE_LIMIT: usize = 111;
285
286pub fn truncate_text(s: &str, limit: usize) -> String {
288 let n = s.chars().count();
289 if n <= limit {
290 return s.to_string();
291 }
292 if limit < 3 {
293 return s.chars().take(limit).collect();
294 }
295 let head: String = s.chars().take(limit - 3).collect();
296 format!("{}...", head)
297}
298
299pub fn value_to_string(v: &Value) -> String {
301 match v {
302 Value::Undefined => "undefined".to_string(),
303 Value::Null => "null".to_string(),
304 Value::Str(s) => truncate_text(&json_text(s).replace('"', ""), ERR_VALUE_LIMIT),
305 Value::Num(n) => js_number(*n),
306 Value::Bool(b) => b.to_string(),
307 Value::BigInt(b) => b.to_string(),
308 Value::Date(ms) => js_date_string(*ms),
309 Value::Func(_) => "function".to_string(),
310 Value::Arr(_) | Value::Obj(_) => {
311 truncate_text(&json_render(v).replace('"', ""), ERR_VALUE_LIMIT)
312 }
313 }
314}
315
316pub fn value_kind(v: &Value) -> &'static str {
319 match v {
320 Value::Undefined | Value::Null => "value",
321 Value::Num(n) if n.is_nan() => "value",
322 Value::Num(_) => "number",
323 Value::Str(_) => "string",
324 Value::Bool(_) => "boolean",
325 Value::BigInt(_) => "bigint",
326 Value::Arr(_) => "array",
327 Value::Obj(_) | Value::Date(_) => "object",
328 Value::Func(_) => "function",
329 }
330}
331
332impl fmt::Display for Value {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 f.write_str(&value_to_string(self))
335 }
336}
337
338pub fn is_integer(x: f64) -> bool {
340 x.is_finite() && x == x.trunc()
341}
342
343#[cfg(feature = "serde")]
344mod serde_conv {
345 use super::{Map, Value};
346
347 impl From<serde_json::Value> for Value {
348 fn from(v: serde_json::Value) -> Self {
349 match v {
350 serde_json::Value::Null => Value::Null,
351 serde_json::Value::Bool(b) => Value::Bool(b),
352 serde_json::Value::Number(n) => Value::Num(n.as_f64().unwrap_or(f64::NAN)),
353 serde_json::Value::String(s) => Value::Str(s),
354 serde_json::Value::Array(a) => Value::Arr(a.into_iter().map(Value::from).collect()),
355 serde_json::Value::Object(m) => {
356 let mut out = Map::with_capacity(m.len());
357 for (k, v) in m {
358 out.insert(k, Value::from(v));
359 }
360 Value::Obj(out)
361 }
362 }
363 }
364 }
365
366 impl From<Value> for serde_json::Value {
367 fn from(v: Value) -> Self {
370 match v {
371 Value::Undefined | Value::Null => serde_json::Value::Null,
372 Value::Bool(b) => serde_json::Value::Bool(b),
373 Value::Num(n) if n.fract() == 0.0 && n.abs() < 9007199254740992.0 => {
376 serde_json::Value::from(n as i64)
377 }
378 Value::Num(n) => serde_json::Number::from_f64(n)
379 .map(serde_json::Value::Number)
380 .unwrap_or(serde_json::Value::Null),
381 Value::Str(s) => serde_json::Value::String(s),
382 Value::BigInt(b) => serde_json::Value::String(b.to_string()),
383 Value::Date(ms) => serde_json::Value::String(super::js_date_string(ms)),
384 Value::Func(_) => serde_json::Value::Null,
385 Value::Arr(a) => {
386 serde_json::Value::Array(a.into_iter().map(serde_json::Value::from).collect())
387 }
388 Value::Obj(m) => {
389 let mut out = serde_json::Map::with_capacity(m.len());
390 for (k, v) in m {
391 if !v.is_undefined() {
392 out.insert(k, serde_json::Value::from(v));
393 }
394 }
395 serde_json::Value::Object(out)
396 }
397 }
398 }
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 #[test]
407 fn numbers_print_as_javascript_does() {
408 let cases: [(f64, &str); 14] = [
409 (0.0, "0"),
410 (1.0, "1"),
411 (-1.0, "-1"),
412 (1.5, "1.5"),
413 (0.1, "0.1"),
414 (100.0, "100"),
415 (1e21, "1e+21"),
416 (1e20, "100000000000000000000"),
417 (123456789012345680000.0, "123456789012345680000"),
418 (1e-7, "1e-7"),
419 (0.000001, "0.000001"),
420 (1.5e-7, "1.5e-7"),
421 (1.2345e25, "1.2345e+25"),
422 (f64::NAN, "NaN"),
423 ];
424 for (x, want) in cases {
425 assert_eq!(js_number(x), want, "{}", x);
426 }
427 assert_eq!(js_number(f64::INFINITY), "Infinity");
428 assert_eq!(js_number(f64::NEG_INFINITY), "-Infinity");
429 }
430
431 #[test]
432 fn dates_print_as_iso() {
433 assert_eq!(js_date_string(0), "1970-01-01T00:00:00.000Z");
434 assert_eq!(
435 js_date_string(4_765_132_800_000),
436 "2121-01-01T00:00:00.000Z"
437 );
438 assert_eq!(js_date_string(-1), "1969-12-31T23:59:59.999Z");
439 assert_eq!(
440 js_date_string(-62_198_755_200_000),
441 "-000001-01-01T00:00:00.000Z"
442 );
443 assert_eq!(
444 js_date_string(253_402_300_800_000),
445 "+010000-01-01T00:00:00.000Z"
446 );
447 }
448
449 #[test]
450 fn strings_render_with_json_escapes_and_no_quotes() {
451 assert_eq!(json_text("a\"b\\c\n\u{1}"), "\"a\\\"b\\\\c\\n\\u0001\"");
452 assert_eq!(value_to_string(&Value::from("a\"b\\c")), "a\\b\\\\c");
453 let long = "x".repeat(200);
454 let t = value_to_string(&Value::from(long.as_str()));
455 assert_eq!(t.len(), ERR_VALUE_LIMIT);
456 assert!(t.ends_with("..."));
457 assert_eq!(truncate_text("abcdef", 2), "ab");
458 }
459
460 #[test]
461 fn containers_render_in_insertion_order() {
462 let mut m = Map::new();
463 m.insert("b".into(), Value::from(1));
464 m.insert(
465 "a".into(),
466 Value::Arr(vec![
467 Value::Undefined,
468 Value::from("x"),
469 Value::Num(f64::NAN),
470 ]),
471 );
472 m.insert("u".into(), Value::Undefined);
473 m.insert("d".into(), Value::Date(0));
474 assert_eq!(
475 value_to_string(&Value::Obj(m)),
476 "{b:1,a:[null,x,NaN],d:1970-01-01T00:00:00.000Z}"
477 );
478 assert_eq!(json_render(&Value::Num(f64::INFINITY)), "null");
479 assert_eq!(value_kind(&Value::Arr(vec![])), "array");
480 assert_eq!(value_kind(&Value::Num(f64::NAN)), "value");
481 assert_eq!(value_kind(&Value::Date(0)), "object");
482 assert_eq!(value_to_string(&Value::BigInt(BigInt::from(7))), "7");
483 assert_eq!(json_render(&Value::BigInt(BigInt::from(7))), "\"7\"");
484 assert_eq!(value_to_string(&Value::Func(1)), "function");
485 assert_eq!(value_to_string(&Value::Date(0)), "1970-01-01T00:00:00.000Z");
486 assert_eq!(json_text("\r\t"), "\"\\r\\t\"");
487 assert_eq!(json_render(&Value::Func(1)), "null");
488 assert_eq!(value_kind(&Value::Func(1)), "function");
489 assert_eq!(value_kind(&Value::BigInt(BigInt::from(1))), "bigint");
490 assert_eq!(Value::Func(1).type_of(), "function");
491 assert_eq!(Value::Null.type_of(), "object");
492 assert_eq!(Value::Undefined.type_of(), "undefined");
493 assert_eq!(Value::BigInt(BigInt::from(1)).type_of(), "bigint");
494 assert_eq!(Value::Date(0).type_of(), "object");
495 assert_eq!(format!("{}", Value::Null), "null");
496 assert_eq!(format!("{}", Value::Undefined), "undefined");
497 assert_eq!(format!("{}", Value::Bool(true)), "true");
498 assert!(is_integer(3.0) && !is_integer(3.5) && !is_integer(f64::NAN));
499 }
500
501 #[test]
502 fn conversions() {
503 assert_eq!(Value::from(true), Value::Bool(true));
504 assert_eq!(Value::from(2i64), Value::Num(2.0));
505 assert_eq!(Value::from(2i32), Value::Num(2.0));
506 assert_eq!(Value::from(2u32), Value::Num(2.0));
507 assert_eq!(Value::from(2usize), Value::Num(2.0));
508 assert_eq!(Value::from(String::from("s")), Value::Str("s".into()));
509 assert_eq!(
510 Value::from(vec![Value::Null]),
511 Value::Arr(vec![Value::Null])
512 );
513 assert_eq!(Value::from(Map::new()), Value::Obj(Map::new()));
514 assert_eq!(Value::from(BigInt::from(1)), Value::BigInt(BigInt::from(1)));
515 assert_eq!(Value::default(), Value::Undefined);
516 assert!(Value::Null.is_null());
517 assert_eq!(Value::from(1.5).as_f64(), Some(1.5));
518 assert_eq!(Value::from("x").as_str(), Some("x"));
519 assert!(Value::Null.as_str().is_none() && Value::Null.as_f64().is_none());
520 assert!(Value::Null.as_obj().is_none() && Value::Null.as_arr().is_none());
521 assert!(Value::Obj(Map::new()).as_obj().is_some());
522 assert!(Value::Arr(vec![]).as_arr().is_some());
523 }
524
525 #[cfg(feature = "serde")]
526 #[test]
527 fn serde_round_trip() {
528 let j: serde_json::Value =
529 serde_json::from_str(r#"{"b":1,"a":[null,"x",true],"c":{"z":1.5}}"#).unwrap();
530 let v = Value::from(j.clone());
531 let keys: Vec<&String> = v.as_obj().unwrap().keys().collect();
532 assert_eq!(keys, vec!["b", "a", "c"]);
533 assert_eq!(serde_json::Value::from(v), j);
534 let mut m = Map::new();
535 m.insert("u".into(), Value::Undefined);
536 m.insert("n".into(), Value::Num(f64::NAN));
537 m.insert("d".into(), Value::Date(0));
538 m.insert("b".into(), Value::BigInt(BigInt::from(3)));
539 m.insert("f".into(), Value::Func(0));
540 m.insert("e".into(), Value::Arr(vec![Value::Undefined]));
541 let out = serde_json::Value::from(Value::Obj(m)).to_string();
542 assert_eq!(
543 out,
544 r#"{"n":null,"d":"1970-01-01T00:00:00.000Z","b":"3","f":null,"e":[null]}"#
545 );
546 }
547
548 #[test]
549 fn remaining_kinds_render() {
550 let m = Map::new();
551 let cases: Vec<(Value, &str, &str)> = vec![
552 (Value::Num(1.0), "number", "number"),
553 (Value::Str("s".into()), "string", "string"),
554 (Value::BigInt(BigInt::from(2)), "bigint", "bigint"),
555 (Value::Arr(vec![]), "object", "array"),
556 (Value::Obj(m), "object", "object"),
557 (Value::Date(0), "object", "object"),
558 (Value::Func(1), "function", "function"),
559 (Value::Bool(true), "boolean", "boolean"),
560 (Value::Null, "object", "value"),
561 (Value::Undefined, "undefined", "value"),
562 (Value::Num(f64::NAN), "number", "value"),
563 ];
564 for (v, ty, kind) in cases {
565 assert_eq!(v.type_of(), ty, "{:?}", v);
566 assert_eq!(value_kind(&v), kind, "{:?}", v);
567 }
568 assert_eq!(value_to_string(&Value::Func(1)), "function");
569 assert_eq!(value_to_string(&Value::Date(0)), "1970-01-01T00:00:00.000Z");
570 assert_eq!(json_text("\r\t"), "\"\\r\\t\"");
571 assert_eq!(json_text("a\u{8}b\u{c}c\u{1}d"), "\"a\\bb\\fc\\u0001d\"");
572 }
573}