use std::collections::BTreeMap;
use serde_json::{Map, Number, Value as Json};
use crate::value::Value;
#[must_use]
pub fn from_json(json: &Json) -> Value {
match json {
Json::Null => Value::Null,
Json::Bool(b) => Value::Bool(*b),
Json::Number(n) => number_to_value(n),
Json::String(s) => Value::Str(s.clone()),
Json::Array(items) => Value::List(items.iter().map(from_json).collect()),
Json::Object(map) => Value::Map(object_to_map(map)),
}
}
#[must_use]
pub fn from_json_owned(json: Json) -> Value {
match json {
Json::Null => Value::Null,
Json::Bool(b) => Value::Bool(b),
Json::Number(n) => number_to_value(&n),
Json::String(s) => Value::Str(s),
Json::Array(items) => Value::List(items.into_iter().map(from_json_owned).collect()),
Json::Object(map) => Value::Map(
map.into_iter()
.map(|(k, v)| (k, from_json_owned(v)))
.collect(),
),
}
}
fn number_to_value(n: &Number) -> Value {
if let Some(i) = n.as_i64() {
return Value::Int(i);
}
n.as_f64().map_or(Value::Null, Value::Float)
}
fn object_to_map(map: &Map<String, Json>) -> BTreeMap<String, Value> {
map.iter()
.map(|(key, value)| (key.clone(), from_json(value)))
.collect()
}
#[must_use]
pub fn to_json(value: &Value) -> Json {
match value {
Value::Null => Json::Null,
Value::Bool(b) => Json::Bool(*b),
Value::Int(i) => Json::Number((*i).into()),
Value::Float(f) => float_to_json(*f),
Value::Str(s)
| Value::DateTime(s)
| Value::Date(s)
| Value::Decimal(s)
| Value::Uuid(s) => Json::String(s.clone()),
Value::Bytes(b) => Json::String(String::from_utf8_lossy(b).into_owned()),
Value::List(items) => Json::Array(items.iter().map(to_json).collect()),
Value::Map(map) => Json::Object(map_to_object(map)),
}
}
fn float_to_json(f: f64) -> Json {
Number::from_f64(f).map_or(Json::Null, Json::Number)
}
fn map_to_object(map: &BTreeMap<String, Value>) -> Map<String, Json> {
map.iter()
.map(|(key, value)| (key.clone(), to_json(value)))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scalars_round_trip_through_json() {
let value = Value::Map(BTreeMap::from([
("n".to_owned(), Value::Int(7)),
("f".to_owned(), Value::Float(2.5)),
("s".to_owned(), Value::Str("hi".to_owned())),
("b".to_owned(), Value::Bool(true)),
("nil".to_owned(), Value::Null),
(
"xs".to_owned(),
Value::List(vec![Value::Int(1), Value::Int(2)]),
),
]));
assert_eq!(from_json(&to_json(&value)), value);
}
#[test]
fn typed_scalars_collapse_to_strings() {
assert_eq!(
to_json(&Value::Decimal("9.99".to_owned())),
Json::String("9.99".to_owned())
);
assert_eq!(
from_json(&to_json(&Value::Uuid("u".to_owned()))),
Value::Str("u".to_owned())
);
}
#[test]
fn big_int_falls_back_to_float() {
let json: Json = serde_json::from_str("9223372036854775808").unwrap();
assert!(matches!(from_json(&json), Value::Float(_)));
}
#[test]
fn owned_matches_borrowed() {
let json: Json =
serde_json::from_str(r#"{"a":1,"b":"x","c":[true,null,2.5],"d":{"k":"v"}}"#).unwrap();
assert_eq!(from_json_owned(json.clone()), from_json(&json));
}
}