use crate::expr::FORBIDDEN_OBJECT_KEY;
use crate::value::Value;
use serde_json::Value as J;
#[derive(Debug)]
pub struct DecodeError {
pub msg: String,
pub code: Option<&'static str>,
}
impl DecodeError {
fn new(msg: impl Into<String>) -> Self {
DecodeError {
msg: msg.into(),
code: None,
}
}
fn coded(code: &'static str, msg: impl Into<String>) -> Self {
DecodeError {
msg: msg.into(),
code: Some(code),
}
}
}
impl std::fmt::Display for DecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "decode error: {}", self.msg)
}
}
impl std::error::Error for DecodeError {}
pub fn decode_value(x: &J) -> Result<Value, DecodeError> {
match x {
J::Null => Ok(Value::Null),
J::Bool(b) => Ok(Value::Bool(*b)),
J::String(s) => Ok(Value::Str(s.clone())),
J::Number(n) => {
if let Some(i) = n.as_i64() {
if n.is_i64() || n.is_u64() {
return Ok(Value::Int(i));
}
Ok(Value::Int(i))
} else if n.is_u64() {
Err(DecodeError::new(format!("integer {n} exceeds i64")))
} else {
let f = n
.as_f64()
.ok_or_else(|| DecodeError::new(format!("bad number {n}")))?;
Ok(Value::Float(f))
}
}
J::Array(a) => {
let mut out = Vec::with_capacity(a.len());
for e in a {
out.push(decode_value(e)?);
}
Ok(Value::Arr(out))
}
J::Object(o) => {
if o.len() == 1 {
if let Some(J::String(s)) = o.get("int") {
let i: i64 = s
.parse()
.map_err(|_| DecodeError::new(format!("int literal '{s}' not i64")))?;
return Ok(Value::Int(i));
}
if let Some(J::Number(n)) = o.get("float") {
let f = n
.as_f64()
.ok_or_else(|| DecodeError::new(format!("bad float {n}")))?;
return Ok(Value::Float(f));
}
if o.contains_key("nan") {
return Ok(Value::Float(f64::NAN));
}
if let Some(J::Number(n)) = o.get("inf") {
let sign = n.as_f64().unwrap_or(1.0);
return Ok(Value::Float(if sign < 0.0 {
f64::NEG_INFINITY
} else {
f64::INFINITY
}));
}
}
let mut out = Vec::with_capacity(o.len());
for (k, v) in o {
if k == FORBIDDEN_OBJECT_KEY {
return Err(DecodeError::coded(
"FORBIDDEN_KEY",
format!("object key \"{FORBIDDEN_OBJECT_KEY}\" is forbidden (fail-closed)"),
));
}
out.push((k.clone(), decode_value(v)?));
}
Ok(Value::Obj(out))
}
}
}
pub fn encode_value(v: &Value) -> J {
match v {
Value::Null => J::Null,
Value::Bool(b) => J::Bool(*b),
Value::Str(s) => J::String(s.clone()),
Value::Int(i) => {
const SAFE: i64 = 9_007_199_254_740_991; if *i >= -SAFE && *i <= SAFE {
J::Number((*i).into())
} else {
let mut m = serde_json::Map::new();
m.insert("int".into(), J::String(i.to_string()));
J::Object(m)
}
}
Value::Float(f) => {
if f.fract() == 0.0 && f.is_finite() {
let mut m = serde_json::Map::new();
m.insert(
"float".into(),
serde_json::Number::from_f64(*f)
.map(J::Number)
.unwrap_or(J::Null),
);
J::Object(m)
} else {
serde_json::Number::from_f64(*f)
.map(J::Number)
.unwrap_or(J::Null)
}
}
Value::Arr(a) => J::Array(a.iter().map(encode_value).collect()),
Value::Obj(o) => {
let mut m = serde_json::Map::new();
for (k, val) in o {
m.insert(k.clone(), encode_value(val));
}
J::Object(m)
}
}
}