use std::fmt;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use crate::error::{FraiseQLError, Result};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum GraphQLValue {
Null,
Boolean(bool),
Int(i64),
Float(f64),
String(String),
List(Vec<GraphQLValue>),
Object(IndexMap<String, GraphQLValue>),
}
impl GraphQLValue {
#[must_use]
pub fn to_json(&self) -> serde_json::Value {
match self {
Self::Null => serde_json::Value::Null,
Self::Boolean(b) => serde_json::Value::Bool(*b),
Self::Int(i) => serde_json::json!(*i),
Self::Float(f) => serde_json::json!(*f),
Self::String(s) => serde_json::Value::String(s.clone()),
Self::List(v) => serde_json::Value::Array(v.iter().map(Self::to_json).collect()),
Self::Object(m) => {
serde_json::Value::Object(m.iter().map(|(k, v)| (k.clone(), v.to_json())).collect())
},
}
}
pub fn from_json(v: &serde_json::Value) -> Result<Self> {
match v {
serde_json::Value::Null => Ok(Self::Null),
serde_json::Value::Bool(b) => Ok(Self::Boolean(*b)),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Ok(Self::Int(i))
} else if let Some(f) = n.as_f64() {
Ok(Self::Float(f))
} else {
Err(FraiseQLError::Validation {
message: format!("default value number out of range: {n}"),
path: None,
})
}
},
serde_json::Value::String(s) => Ok(Self::String(s.clone())),
serde_json::Value::Array(arr) => {
let items = arr.iter().map(Self::from_json).collect::<Result<Vec<_>>>()?;
Ok(Self::List(items))
},
serde_json::Value::Object(obj) => {
let map = obj
.iter()
.map(|(k, v)| Self::from_json(v).map(|gv| (k.clone(), gv)))
.collect::<Result<IndexMap<_, _>>>()?;
Ok(Self::Object(map))
},
}
}
}
impl fmt::Display for GraphQLValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_json())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_int() {
let v = GraphQLValue::Int(42);
assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
}
#[test]
fn roundtrip_float() {
let v = GraphQLValue::Float(1.5);
let rt = GraphQLValue::from_json(&v.to_json()).expect("roundtrip");
assert!(matches!(rt, GraphQLValue::Float(_)));
}
#[test]
fn roundtrip_string() {
let v = GraphQLValue::String("hello".to_string());
assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
}
#[test]
fn roundtrip_list() {
let v = GraphQLValue::List(vec![GraphQLValue::Int(1), GraphQLValue::Null]);
assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
}
#[test]
fn roundtrip_null() {
let v = GraphQLValue::Null;
assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
}
#[test]
fn roundtrip_boolean() {
let v = GraphQLValue::Boolean(true);
assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
}
#[test]
fn json_null_parses_as_null() {
assert_eq!(
GraphQLValue::from_json(&serde_json::Value::Null).expect("parse"),
GraphQLValue::Null
);
}
#[test]
fn serde_roundtrip_via_json_string() {
let v = GraphQLValue::List(vec![GraphQLValue::Int(1), GraphQLValue::Null]);
let json_str = serde_json::to_string(&v).expect("serialize");
let back: GraphQLValue = serde_json::from_str(&json_str).expect("deserialize");
assert_eq!(back, v);
}
}