use crate::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Map(BTreeMap<String, Value>),
Timestamp(i64),
Uuid([u8; 16]),
Uri(String),
Identifier(String),
}
pub type ValueRef<'a> = &'a Value;
impl Value {
pub fn from_json(v: &serde_json::Value) -> Result<Self> {
Ok(match v {
serde_json::Value::Null => Self::Null,
serde_json::Value::Bool(b) => Self::Bool(*b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Self::Integer(i)
} else if let Some(u) = n.as_u64() {
if u > i64::MAX as u64 {
return Err(Error::Semantic("integer out of i64 range".into()));
}
Self::Integer(u as i64)
} else if let Some(f) = n.as_f64() {
Self::Float(f)
} else {
return Err(Error::Semantic("unsupported number".into()));
}
}
serde_json::Value::String(s) => Self::String(s.clone()),
serde_json::Value::Array(a) => {
let mut out = Vec::with_capacity(a.len());
for x in a {
out.push(Self::from_json(x)?);
}
Self::Array(out)
}
serde_json::Value::Object(o) => {
let mut map = BTreeMap::new();
for (k, val) in o {
map.insert(k.clone(), Self::from_json(val)?);
}
Self::Map(map)
}
})
}
pub fn to_json(&self) -> serde_json::Value {
match self {
Self::Null => serde_json::Value::Null,
Self::Bool(b) => serde_json::Value::Bool(*b),
Self::Integer(i) => serde_json::json!(*i),
Self::Float(f) => serde_json::json!(*f),
Self::String(s) | Self::Uri(s) | Self::Identifier(s) => {
serde_json::Value::String(s.clone())
}
Self::Bytes(b) => serde_json::Value::String(hex::encode(b)),
Self::Array(a) => serde_json::Value::Array(a.iter().map(Self::to_json).collect()),
Self::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)
}
Self::Timestamp(t) => serde_json::json!(*t),
Self::Uuid(u) => serde_json::Value::String(hex::encode(u)),
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
match self {
Self::Map(m) => m.get(key),
_ => None,
}
}
pub fn index(&self, i: usize) -> Option<&Value> {
match self {
Self::Array(a) => a.get(i),
_ => None,
}
}
}
pub fn canonical_encode(value: &Value) -> Result<Vec<u8>> {
super::tokens::encode_tokens(value)
}