use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Arr(Vec<Value>),
Obj(Vec<(String, Value)>),
}
impl Value {
pub fn type_name(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Int(_) => "int",
Value::Float(_) => "float",
Value::Str(_) => "string",
Value::Arr(_) => "arr",
Value::Obj(_) => "obj",
}
}
pub fn obj_get(&self, key: &str) -> Option<&Value> {
if let Value::Obj(pairs) = self {
pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
} else {
None
}
}
}
pub fn deep_equals(a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::Null, Value::Null) => true,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::Int(x), Value::Int(y)) => x == y,
(Value::Float(x), Value::Float(y)) => x == y || (x.is_nan() && y.is_nan()),
(Value::Str(x), Value::Str(y)) => x == y,
(Value::Arr(x), Value::Arr(y)) => {
x.len() == y.len() && x.iter().zip(y).all(|(u, v)| deep_equals(u, v))
}
(Value::Obj(x), Value::Obj(y)) => {
if x.len() != y.len() {
return false;
}
let mx: BTreeMap<&str, &Value> = x.iter().map(|(k, v)| (k.as_str(), v)).collect();
let my: BTreeMap<&str, &Value> = y.iter().map(|(k, v)| (k.as_str(), v)).collect();
if mx.len() != my.len() {
return false; }
mx.len() == my.len()
&& mx
.iter()
.all(|(k, v)| my.get(k).map(|w| deep_equals(v, w)).unwrap_or(false))
}
_ => false,
}
}