use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
List(Vec<Value>),
Map(BTreeMap<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::List(_) => "list",
Value::Map(_) => "map",
}
}
pub fn matches_type(&self, ty: &str) -> bool {
match (self, ty) {
(Value::Null, _) => true,
(Value::Bool(_), "bool") => true,
(Value::Int(_), "int") => true,
(Value::Float(_), "float") => true,
(Value::Str(_), "string") => true,
(Value::List(_), "list") => true,
(Value::Map(_), "map") => true,
_ => false,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn truthy(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::Null => false,
Value::Int(i) => *i != 0,
Value::Float(f) => *f != 0.0,
Value::Str(s) => !s.is_empty(),
Value::List(l) => !l.is_empty(),
Value::Map(m) => !m.is_empty(),
}
}
pub fn as_str_value(&self) -> Option<&str> {
match self {
Value::Str(s) => Some(s.as_str()),
_ => None,
}
}
pub fn coerce_to(&self, ty: &str) -> Option<Value> {
match (self, ty) {
(Value::Null, _) => Some(Value::Null),
(Value::Bool(b), "bool") => Some(Value::Bool(*b)),
(Value::Int(i), "int") => Some(Value::Int(*i)),
(Value::Float(f), "float") => Some(Value::Float(*f)),
(Value::Str(s), "string") => Some(Value::Str(s.clone())),
(Value::List(l), "list") => Some(Value::List(l.clone())),
(Value::Map(m), "map") => Some(Value::Map(m.clone())),
(Value::Int(i), "float") => Some(Value::Float(*i as f64)),
_ => None,
}
}
}
impl serde::Serialize for Value {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.to_json().serialize(s)
}
}
impl<'de> serde::Deserialize<'de> for Value {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
serde_json::Value::deserialize(d).map(|j| Value::from_json(&j))
}
}
impl Value {
pub fn from_yaml(v: &serde_yaml::Value) -> Value {
use serde_yaml::Value::*;
match v {
Null => Value::Null,
Bool(b) => Value::Bool(*b),
Number(n) => {
if let Some(i) = n.as_i64() {
Value::Int(i)
} else if let Some(u) = n.as_u64() {
Value::Int(u as i64)
} else {
Value::Float(n.as_f64().unwrap_or(f64::NAN))
}
}
String(s) => Value::Str(s.clone()),
Sequence(seq) => Value::List(seq.iter().map(Value::from_yaml).collect()),
Mapping(m) => {
let mut map = BTreeMap::new();
for (k, v) in m {
let key = match k {
String(s) => s.clone(),
Bool(b) => b.to_string(),
Number(n) => n.to_string(),
Null => "null".to_string(),
_ => continue,
};
map.insert(key, Value::from_yaml(v));
}
Value::Map(map)
}
Tagged(t) => Value::from_yaml(&t.value),
}
}
pub fn to_json(&self) -> serde_json::Value {
match self {
Value::Null => serde_json::Value::Null,
Value::Bool(b) => serde_json::Value::Bool(*b),
Value::Int(i) => serde_json::Value::Number((*i as i64).into()),
Value::Float(f) => serde_json::Number::from_f64(*f)
.map(serde_json::Value::Number)
.unwrap_or(serde_json::Value::Null),
Value::Str(s) => serde_json::Value::String(s.clone()),
Value::List(l) => serde_json::Value::Array(l.iter().map(Value::to_json).collect()),
Value::Map(m) => {
let mut o = serde_json::Map::new();
for (k, v) in m {
o.insert(k.clone(), v.to_json());
}
serde_json::Value::Object(o)
}
}
}
pub fn from_json(v: &serde_json::Value) -> Value {
match v {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(b) => Value::Bool(*b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Value::Int(i)
} else if let Some(u) = n.as_u64() {
Value::Int(u as i64)
} else {
Value::Float(n.as_f64().unwrap_or(f64::NAN))
}
}
serde_json::Value::String(s) => Value::Str(s.clone()),
serde_json::Value::Array(a) => {
Value::List(a.iter().map(Value::from_json).collect())
}
serde_json::Value::Object(o) => {
let mut m = BTreeMap::new();
for (k, v) in o {
m.insert(k.clone(), Value::from_json(v));
}
Value::Map(m)
}
}
}
}
pub fn map1(k: &str, v: Value) -> Value {
let mut m = BTreeMap::new();
m.insert(k.to_string(), v);
Value::Map(m)
}