1use std::cmp::Ordering;
13
14#[derive(Debug, Clone)]
15pub enum Value {
16 Null,
17 Bool(bool),
18 Int(i64),
19 Float(f64),
20 Str(String),
21 Array(Vec<Value>),
22 Object(Vec<(String, Value)>),
23}
24
25impl PartialEq for Value {
28 fn eq(&self, other: &Value) -> bool {
29 self.value_eq(other)
30 }
31}
32
33impl Value {
34 pub fn is_truthy(&self) -> bool {
36 !matches!(self, Value::Null | Value::Bool(false))
37 }
38
39 pub fn type_name(&self) -> &'static str {
41 match self {
42 Value::Null => "null",
43 Value::Bool(_) => "boolean",
44 Value::Int(_) | Value::Float(_) => "number",
45 Value::Str(_) => "string",
46 Value::Array(_) => "array",
47 Value::Object(_) => "object",
48 }
49 }
50
51 pub fn as_f64(&self) -> Option<f64> {
53 match self {
54 Value::Int(i) => Some(*i as f64),
55 Value::Float(f) => Some(*f),
56 _ => None,
57 }
58 }
59
60 pub fn order(&self, other: &Value) -> Ordering {
63 fn rank(v: &Value) -> u8 {
64 match v {
65 Value::Null => 0,
66 Value::Bool(_) => 1,
67 Value::Int(_) | Value::Float(_) => 2,
68 Value::Str(_) => 3,
69 Value::Array(_) => 4,
70 Value::Object(_) => 5,
71 }
72 }
73 match (self, other) {
74 (Value::Null, Value::Null) => Ordering::Equal,
75 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
76 (Value::Str(a), Value::Str(b)) => a.cmp(b),
77 (Value::Array(a), Value::Array(b)) => {
78 for (x, y) in a.iter().zip(b.iter()) {
79 let c = x.order(y);
80 if c != Ordering::Equal {
81 return c;
82 }
83 }
84 a.len().cmp(&b.len())
85 }
86 (Value::Object(a), Value::Object(b)) => {
87 let mut ka: Vec<&String> = a.iter().map(|(k, _)| k).collect();
89 let mut kb: Vec<&String> = b.iter().map(|(k, _)| k).collect();
90 ka.sort();
91 kb.sort();
92 match ka.cmp(&kb) {
93 Ordering::Equal => {}
94 ne => return ne,
95 }
96 for k in ka {
97 let va = a.iter().find(|(kk, _)| kk == k).map(|(_, v)| v);
98 let vb = b.iter().find(|(kk, _)| kk == k).map(|(_, v)| v);
99 if let (Some(va), Some(vb)) = (va, vb) {
100 let c = va.order(vb);
101 if c != Ordering::Equal {
102 return c;
103 }
104 }
105 }
106 Ordering::Equal
107 }
108 _ => {
109 if let (Some(a), Some(b)) = (self.as_f64(), other.as_f64()) {
111 a.partial_cmp(&b).unwrap_or(Ordering::Equal)
112 } else {
113 rank(self).cmp(&rank(other))
114 }
115 }
116 }
117 }
118
119 pub fn value_eq(&self, other: &Value) -> bool {
121 self.order(other) == Ordering::Equal
122 }
123
124 pub fn to_raw_string(&self) -> String {
127 match self {
128 Value::Str(s) => s.clone(),
129 Value::Null => "null".to_string(),
130 Value::Bool(b) => b.to_string(),
131 Value::Int(i) => i.to_string(),
132 Value::Float(f) => format_f64(*f),
133 Value::Array(_) | Value::Object(_) => self.to_json(),
134 }
135 }
136
137 pub fn to_json(&self) -> String {
139 let mut out = String::new();
140 self.write_json(&mut out);
141 out
142 }
143
144 fn write_json(&self, out: &mut String) {
145 match self {
146 Value::Null => out.push_str("null"),
147 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
148 Value::Int(i) => out.push_str(&i.to_string()),
149 Value::Float(f) => out.push_str(&format_f64(*f)),
150 Value::Str(s) => write_json_string(s, out),
151 Value::Array(a) => {
152 out.push('[');
153 for (i, v) in a.iter().enumerate() {
154 if i > 0 {
155 out.push(',');
156 }
157 v.write_json(out);
158 }
159 out.push(']');
160 }
161 Value::Object(m) => {
162 out.push('{');
163 for (i, (k, v)) in m.iter().enumerate() {
164 if i > 0 {
165 out.push(',');
166 }
167 write_json_string(k, out);
168 out.push(':');
169 v.write_json(out);
170 }
171 out.push('}');
172 }
173 }
174 }
175}
176
177fn format_f64(f: f64) -> String {
179 if f.is_finite() && f.fract() == 0.0 && f.abs() < 1e15 {
180 format!("{}", f as i64)
181 } else {
182 format!("{f}")
183 }
184}
185
186fn write_json_string(s: &str, out: &mut String) {
187 out.push('"');
188 for c in s.chars() {
189 match c {
190 '"' => out.push_str("\\\""),
191 '\\' => out.push_str("\\\\"),
192 '\n' => out.push_str("\\n"),
193 '\r' => out.push_str("\\r"),
194 '\t' => out.push_str("\\t"),
195 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
196 c => out.push(c),
197 }
198 }
199 out.push('"');
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 fn arr(v: &[Value]) -> Value {
207 Value::Array(v.to_vec())
208 }
209 fn obj(pairs: &[(&str, Value)]) -> Value {
210 Value::Object(
211 pairs
212 .iter()
213 .map(|(k, v)| (k.to_string(), v.clone()))
214 .collect(),
215 )
216 }
217
218 #[test]
219 fn total_order_across_types() {
220 let ladder = [
222 Value::Null,
223 Value::Bool(false),
224 Value::Bool(true),
225 Value::Int(-1),
226 Value::Float(2.5),
227 Value::Str("a".into()),
228 arr(&[Value::Int(1)]),
229 obj(&[("k", Value::Int(1))]),
230 ];
231 for i in 0..ladder.len() {
232 for j in 0..ladder.len() {
233 let want = i.cmp(&j);
234 if want != Ordering::Equal {
237 assert_eq!(
238 ladder[i].order(&ladder[j]),
239 want,
240 "{:?} vs {:?}",
241 ladder[i],
242 ladder[j]
243 );
244 }
245 }
246 }
247 }
248
249 #[test]
250 fn numbers_compare_across_int_and_float() {
251 assert!(Value::Int(1).value_eq(&Value::Float(1.0)));
252 assert_eq!(Value::Int(2).order(&Value::Float(2.5)), Ordering::Less);
253 assert_eq!(Value::Float(3.0).order(&Value::Int(3)), Ordering::Equal);
254 assert_eq!(Value::Int(1).as_f64(), Some(1.0));
255 assert_eq!(Value::Str("x".into()).as_f64(), None);
256 }
257
258 #[test]
259 fn arrays_and_objects_order_structurally() {
260 assert_eq!(
262 arr(&[Value::Int(1), Value::Int(2)]).order(&arr(&[Value::Int(1), Value::Int(3)])),
263 Ordering::Less
264 );
265 assert_eq!(
266 arr(&[Value::Int(1)]).order(&arr(&[Value::Int(1), Value::Int(0)])),
267 Ordering::Less
268 );
269 assert_eq!(
271 obj(&[("a", Value::Int(1))]).order(&obj(&[("b", Value::Int(1))])),
272 Ordering::Less
273 );
274 assert_eq!(
275 obj(&[("a", Value::Int(1))]).order(&obj(&[("a", Value::Int(2))])),
276 Ordering::Less
277 );
278 assert!(
280 obj(&[("a", Value::Int(1)), ("b", Value::Int(2))])
281 .value_eq(&obj(&[("b", Value::Int(2)), ("a", Value::Int(1))]))
282 );
283 }
284
285 #[test]
286 fn raw_string_and_truthiness() {
287 assert_eq!(Value::Float(1.0).to_raw_string(), "1");
288 assert_eq!(Value::Float(1.5).to_raw_string(), "1.5");
289 assert_eq!(Value::Null.to_raw_string(), "null");
290 assert_eq!(Value::Bool(true).to_raw_string(), "true");
291 assert_eq!(arr(&[Value::Int(1)]).to_raw_string(), "[1]");
292 assert_eq!(obj(&[("a", Value::Int(1))]).to_raw_string(), "{\"a\":1}");
293 assert!(Value::Int(0).is_truthy());
295 assert!(Value::Str("".into()).is_truthy());
296 assert!(!Value::Bool(false).is_truthy());
297 assert!(!Value::Null.is_truthy());
298 }
299
300 #[test]
301 fn json_encoding_escapes_and_floats() {
302 assert_eq!(
303 Value::Str("a\"b\\c\n\t\r".into()).to_json(),
304 "\"a\\\"b\\\\c\\n\\t\\r\""
305 );
306 assert_eq!(Value::Str("\u{0001}".into()).to_json(), "\"\\u0001\"");
308 assert_eq!(Value::Float(42.0).to_json(), "42");
310 assert_eq!(Value::Float(2.5).to_json(), "2.5");
311 assert_eq!(
312 obj(&[("n", Value::Null), ("xs", arr(&[Value::Bool(true)]))]).to_json(),
313 "{\"n\":null,\"xs\":[true]}"
314 );
315 assert_eq!(arr(&[]).to_json(), "[]");
316 }
317
318 #[test]
319 fn type_names() {
320 assert_eq!(Value::Null.type_name(), "null");
321 assert_eq!(Value::Bool(true).type_name(), "boolean");
322 assert_eq!(Value::Int(1).type_name(), "number");
323 assert_eq!(Value::Float(1.0).type_name(), "number");
324 assert_eq!(Value::Str("x".into()).type_name(), "string");
325 assert_eq!(arr(&[]).type_name(), "array");
326 assert_eq!(obj(&[]).type_name(), "object");
327 }
328}