1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! The dynamic value model — the Rust replacement for Java varar-core's `Object`
//! with `instanceof Map`/`List`/`String` duck-typing (see `CellDiff.java`,
//! `DocStringDiff.java`, `ParamDiff.java`). One closed enum carries handler
//! arguments, handler returns, thread-through state, row objects, table rows,
//! and the conformance wire values.
//!
//! Equality is derived `PartialEq`, the analog of Java's `Objects.equals`:
//! `Int(2) != Float(2.0)` (Java `Integer(2).equals(Double(2.0))` is false), and
//! `Map` equality is order-insensitive (`BTreeMap`), matching `Map.of(...)`
//! vs `LinkedHashMap` equality in the Java tests.
use std::collections::BTreeMap;
/// A dynamic JSON-ish value. `BTreeMap` gives order-insensitive map equality and
/// a free recursive key-sort for canonical JSON.
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Null,
Bool(bool),
/// Integer (Java `Integer`/`Long`; `{int}` transforms here).
Int(i64),
/// Floating-point (Java `Double`); serialized as an integer when integral.
Float(f64),
String(String),
List(Vec<Value>),
Map(BTreeMap<String, Value>),
}
impl Value {
/// A short type name (for `ReturnShapeError` messages, mirroring Java's
/// `getClass().getSimpleName()`).
pub fn type_name(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "Boolean",
Value::Int(_) => "Integer",
Value::Float(_) => "Double",
Value::String(_) => "String",
Value::List(_) => "List",
Value::Map(_) => "Map",
}
}
/// Builds a [`Value::List`] from anything iterable of `Value`.
pub fn list(items: impl IntoIterator<Item = Value>) -> Value {
Value::List(items.into_iter().collect())
}
/// Builds a [`Value::Map`] from `(String, Value)` pairs.
pub fn map(entries: impl IntoIterator<Item = (String, Value)>) -> Value {
Value::Map(entries.into_iter().collect())
}
}
impl From<i64> for Value {
fn from(v: i64) -> Value {
Value::Int(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Value {
Value::Int(i64::from(v))
}
}
impl From<bool> for Value {
fn from(v: bool) -> Value {
Value::Bool(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Value {
Value::Float(v)
}
}
impl From<&str> for Value {
fn from(v: &str) -> Value {
Value::String(v.to_string())
}
}
impl From<String> for Value {
fn from(v: String) -> Value {
Value::String(v)
}
}
impl From<Vec<Value>> for Value {
fn from(v: Vec<Value>) -> Value {
Value::List(v)
}
}