fraiseql_core/schema/
graphql_value.rs1use std::fmt;
15
16use indexmap::IndexMap;
17use serde::{Deserialize, Serialize};
18
19use crate::error::{FraiseQLError, Result};
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33#[serde(untagged)]
34#[non_exhaustive]
35pub enum GraphQLValue {
36 Null,
38 Boolean(bool),
40 Int(i64),
42 Float(f64),
45 String(String),
49 List(Vec<GraphQLValue>),
51 Object(IndexMap<String, GraphQLValue>),
53}
54
55impl GraphQLValue {
56 #[must_use]
58 pub fn to_json(&self) -> serde_json::Value {
59 match self {
60 Self::Null => serde_json::Value::Null,
61 Self::Boolean(b) => serde_json::Value::Bool(*b),
62 Self::Int(i) => serde_json::json!(*i),
63 Self::Float(f) => serde_json::json!(*f),
64 Self::String(s) => serde_json::Value::String(s.clone()),
65 Self::List(v) => serde_json::Value::Array(v.iter().map(Self::to_json).collect()),
66 Self::Object(m) => {
67 serde_json::Value::Object(m.iter().map(|(k, v)| (k.clone(), v.to_json())).collect())
68 },
69 }
70 }
71
72 pub fn from_json(v: &serde_json::Value) -> Result<Self> {
84 match v {
85 serde_json::Value::Null => Ok(Self::Null),
86 serde_json::Value::Bool(b) => Ok(Self::Boolean(*b)),
87 serde_json::Value::Number(n) => {
88 if let Some(i) = n.as_i64() {
89 Ok(Self::Int(i))
90 } else if let Some(f) = n.as_f64() {
91 Ok(Self::Float(f))
92 } else {
93 Err(FraiseQLError::Validation {
94 message: format!("default value number out of range: {n}"),
95 path: None,
96 })
97 }
98 },
99 serde_json::Value::String(s) => Ok(Self::String(s.clone())),
100 serde_json::Value::Array(arr) => {
101 let items = arr.iter().map(Self::from_json).collect::<Result<Vec<_>>>()?;
102 Ok(Self::List(items))
103 },
104 serde_json::Value::Object(obj) => {
105 let map = obj
106 .iter()
107 .map(|(k, v)| Self::from_json(v).map(|gv| (k.clone(), gv)))
108 .collect::<Result<IndexMap<_, _>>>()?;
109 Ok(Self::Object(map))
110 },
111 }
112 }
113}
114
115impl fmt::Display for GraphQLValue {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 write!(f, "{}", self.to_json())
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn roundtrip_int() {
127 let v = GraphQLValue::Int(42);
128 assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
129 }
130
131 #[test]
132 fn roundtrip_float() {
133 let v = GraphQLValue::Float(1.5);
134 let rt = GraphQLValue::from_json(&v.to_json()).expect("roundtrip");
135 assert!(matches!(rt, GraphQLValue::Float(_)));
136 }
137
138 #[test]
139 fn roundtrip_string() {
140 let v = GraphQLValue::String("hello".to_string());
141 assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
142 }
143
144 #[test]
145 fn roundtrip_list() {
146 let v = GraphQLValue::List(vec![GraphQLValue::Int(1), GraphQLValue::Null]);
147 assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
148 }
149
150 #[test]
151 fn roundtrip_null() {
152 let v = GraphQLValue::Null;
153 assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
154 }
155
156 #[test]
157 fn roundtrip_boolean() {
158 let v = GraphQLValue::Boolean(true);
159 assert_eq!(GraphQLValue::from_json(&v.to_json()).expect("roundtrip"), v);
160 }
161
162 #[test]
163 fn json_null_parses_as_null() {
164 assert_eq!(
165 GraphQLValue::from_json(&serde_json::Value::Null).expect("parse"),
166 GraphQLValue::Null
167 );
168 }
169
170 #[test]
171 fn serde_roundtrip_via_json_string() {
172 let v = GraphQLValue::List(vec![GraphQLValue::Int(1), GraphQLValue::Null]);
173 let json_str = serde_json::to_string(&v).expect("serialize");
174 let back: GraphQLValue = serde_json::from_str(&json_str).expect("deserialize");
175 assert_eq!(back, v);
176 }
177}