1use bumpalo::collections::Vec;
2use hashbrown::HashMap;
3use serde_json::{json, map::Map as JSMap, Value as JSValue};
4
5use super::ValueFromNode;
6use crate::ast::*;
7use crate::error::{Error, Result};
8
9pub fn ast_variables_from_value<'a>(
14 ctx: &'a ASTContext,
15 input: &JSValue,
16 var_defs: &'a VariableDefinitions<'a>,
17) -> Result<Variables<'a>> {
18 let mut vars = HashMap::new_in(&ctx.arena);
19 if var_defs.is_empty() {
20 Ok(vars)
21 } else if let JSValue::Object(obj) = input {
22 for var_def in var_defs.children.iter() {
23 let value = match obj.get(var_def.variable.name) {
24 Some(value) => ctx.alloc(ast_from_value(ctx, value, ctx.alloc(var_def.of_type))?),
25 None => match (var_def.default_value.clone(), var_def.of_type) {
26 (Value::List(_), Type::ListType(_)) => &var_def.default_value,
27 (Value::Null, Type::ListType(_)) => &var_def.default_value,
28 (default_value, Type::ListType(_)) => {
29 let mut builder: Vec<'_, _> = Vec::new_in(&ctx.arena);
30 builder.push(default_value);
31 ctx.alloc(Value::List(ListValue { children: builder }))
32 }
33 _ => &var_def.default_value,
34 },
35 };
36 vars.insert(var_def.variable.name, value.to_owned());
37 }
38 Ok(vars)
39 } else {
40 Err(Error::new(
41 "Variables expected but received non-object value",
42 None,
43 ))
44 }
45}
46
47pub fn ast_from_value<'a>(
49 ctx: &'a ASTContext,
50 value: &JSValue,
51 of_type: &'a Type<'a>,
52) -> Result<Value<'a>> {
53 match (of_type, value) {
54 (Type::ListType(of_type), JSValue::Array(list)) => {
55 let new_list = list.iter().map(|value| ast_from_value(ctx, value, of_type));
56
57 let mut new_list_children = Vec::new_in(&ctx.arena);
58 for item in new_list {
59 new_list_children.push(item?);
60 }
61
62 return Ok(Value::List(ListValue {
63 children: new_list_children,
64 }));
65 }
66 (Type::ListType(of_type), value) => {
67 if matches!(value, JSValue::Null) {
68 return Ok(Value::Null);
69 }
70
71 let child = ast_from_value(ctx, value, of_type)?;
72 let mut new_list_children = Vec::new_in(&ctx.arena);
73 new_list_children.push(child);
74 return Ok(Value::List(ListValue {
75 children: new_list_children,
76 }));
77 }
78
79 (Type::NonNullType(_), JSValue::Null) => {
80 Err(Error::new("Received null for non-nullable type", None))
81 }
82
83 (_, JSValue::Null) => Ok(Value::Null),
84
85 (Type::NonNullType(of_type), value) => ast_from_value(ctx, value, of_type),
86
87 (Type::NamedType(NamedType { name: "Boolean" }), JSValue::Bool(x)) => {
88 Ok(Value::Boolean((*x).into()))
89 }
90
91 (Type::NamedType(NamedType { name: "Boolean" }), JSValue::Number(num)) => {
92 Ok(Value::Boolean((num.as_u64().unwrap_or(0) != 0).into()))
93 }
94
95 (Type::NamedType(NamedType { name: "Int" }), JSValue::Number(num)) => num
96 .as_i64()
97 .map(|x| {
98 Value::Int(IntValue {
99 value: ctx.alloc_str(&x.to_string()),
100 })
101 })
102 .ok_or_else(|| Error::new("Received Float for Int type", None)),
103
104 (Type::NamedType(NamedType { name: "Float" }), JSValue::Number(num)) => {
105 let num = num.as_f64().unwrap_or(0.0);
106 if num.is_finite() {
107 Ok(Value::Float(FloatValue {
108 value: ctx.alloc_str(&num.to_string()),
109 }))
110 } else {
111 Err(Error::new("Received non-finite Float for Float type", None))
112 }
113 }
114
115 (
116 Type::NamedType(NamedType {
117 name: "ID" | "String",
118 }),
119 JSValue::String(str),
120 ) => Ok(Value::String(ctx.alloc_str(str).into())),
121
122 (
123 Type::NamedType(NamedType {
124 name: "ID" | "String",
125 }),
126 JSValue::Number(num),
127 ) => Ok(Value::String(ctx.alloc_string(num.to_string()).into())),
128
129 (Type::NamedType(NamedType { name: _ }), value) => Ok(ast_from_value_untyped(ctx, value)),
130 }
131}
132
133pub fn ast_from_value_untyped<'a>(ctx: &'a ASTContext, value: &JSValue) -> Value<'a> {
135 match value {
136 JSValue::Array(list) => {
137 let new_list = list.iter().map(|value| ast_from_value_untyped(ctx, value));
138
139 let mut new_list_children = Vec::new_in(&ctx.arena);
140 for item in new_list {
141 new_list_children.push(item);
142 }
143
144 return Value::List(ListValue {
145 children: new_list_children,
146 });
147 }
148 JSValue::Object(map) => {
149 let new_object_children_iter = map.iter().map(|(key, value)| ObjectField {
150 name: ctx.alloc_str(key),
151 value: ast_from_value_untyped(ctx, value),
152 });
153 let mut new_object_children = Vec::new_in(&ctx.arena);
154 for item in new_object_children_iter {
155 new_object_children.push(item);
156 }
157 return Value::Object(ObjectValue {
158 children: new_object_children,
159 });
160 }
161 JSValue::Number(num) => num
162 .as_i64()
163 .map(|x| {
164 Value::Int(IntValue {
165 value: ctx.alloc_str(&x.to_string()),
166 })
167 })
168 .unwrap_or_else(|| {
169 let float = num.as_f64().filter(|x| x.is_finite()).unwrap_or(0.0);
170 Value::Float(FloatValue {
171 value: ctx.alloc_str(&float.to_string()),
172 })
173 }),
174 JSValue::Bool(x) => Value::Boolean((*x).into()),
175 JSValue::String(str) => Value::String(ctx.alloc_str(str).into()),
176 JSValue::Null => Value::Null,
177 }
178}
179
180pub fn value_from_ast_variables<'a>(variable: &'a Variables<'a>) -> JSMap<String, JSValue> {
182 let mut map = JSMap::new();
183 for (key, value) in variable.iter() {
184 map.insert(key.to_string(), value.clone().to_json(None));
185 }
186 map
187}
188
189pub fn value_from_ast<'a>(
191 value: &Value<'a>,
192 of_type: &'a Type<'a>,
193 variables: Option<&'a Variables<'a>>,
194) -> Result<JSValue> {
195 match (of_type, value) {
196 (of_type, Value::Variable(var)) => variables
197 .and_then(|vars| vars.get(var.name))
198 .ok_or_else(|| Error::new("Invalid variable reference when casting to value", None))
199 .and_then(|value| value_from_ast(value, of_type, None)),
200
201 (Type::ListType(of_type), Value::List(list)) => {
202 let new_list_children_iter = list
203 .children
204 .iter()
205 .map(|value| value_from_ast(value, of_type, variables));
206 let mut new_children = vec![];
207 for item in new_list_children_iter {
208 new_children.push(item?);
209 }
210 Ok(JSValue::Array(new_children))
211 }
212
213 (Type::ListType(of_type), value) => {
214 let child = value_from_ast(value, of_type, variables)?;
215 Ok(JSValue::Array(vec![child]))
216 }
217
218 (Type::NonNullType(_), Value::Null) => {
219 Err(Error::new("Received null for non-nullable type", None))
220 }
221
222 (_, Value::Null) => Ok(JSValue::Null),
223
224 (Type::NonNullType(of_type), value) => value_from_ast(value, of_type, variables),
225
226 (Type::NamedType(NamedType { name: "Boolean" }), Value::Boolean(x)) => {
227 Ok(JSValue::Bool(x.value))
228 }
229 (Type::NamedType(NamedType { name: "Boolean" }), Value::Int(x)) => {
230 let res = x.value.parse::<i32>();
231 match res {
232 Ok(int) => Ok(JSValue::Bool(int != 0)),
233 Err(_) => Err(Error::new(
234 format!("Got invalid Int {} expected Boolean type.", x.value),
235 None,
236 )),
237 }
238 }
239
240 (Type::NamedType(NamedType { name: "Int" }), Value::Int(x)) => {
241 let res = x.value.parse::<i32>();
242 match res {
243 Ok(int) => Ok(JSValue::Number(int.into())),
244 Err(_) => Err(Error::new(format!("Got invalid Int {}.", x.value), None)),
245 }
246 }
247 (Type::NamedType(NamedType { name: "Float" }), Value::Float(x)) => Ok(json!(x.value)),
248
249 (
250 Type::NamedType(NamedType {
251 name: "ID" | "String",
252 }),
253 Value::Int(num),
254 ) => Ok(JSValue::String(num.value.to_string())),
255
256 (
257 Type::NamedType(NamedType {
258 name: "ID" | "String",
259 }),
260 Value::String(str),
261 ) => Ok(JSValue::String(str.value.into())),
262
263 (Type::NamedType(NamedType { name: _ }), value) => Ok(value.to_owned().to_json(variables)),
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::{ast_variables_from_value, DefaultIn};
270 use crate::ast::{
271 ASTContext, Directives, NamedType, Type, Value, Variable, VariableDefinition,
272 VariableDefinitions,
273 };
274 use bumpalo::collections::Vec;
275 use serde_json::{json, Value as JsValue};
276
277 #[test]
278 fn nullable_list() {
279 let ctx = ASTContext::new();
280 let input = json!({
281 "list": JsValue::Null,
282 });
283
284 let var = vec![VariableDefinition {
285 variable: Variable { name: "list" },
286 of_type: Type::ListType(&Type::NonNullType(&Type::NamedType(NamedType {
287 name: "Int",
288 }))),
289 default_value: Value::Null,
290 directives: Directives::default_in(&ctx.arena)
291 }];
292
293 let var_defs = VariableDefinitions {
294 children: Vec::from_iter_in(var, &ctx.arena),
295 };
296 let _ = ast_variables_from_value(&ctx, &input, &var_defs).unwrap();
297 }
298
299 #[test]
300 fn object_list() {
301 let ctx = ASTContext::new();
302 let input = json!({
303 "orderBys": [{
304 "equals": {
305 "value": 5
306 }
307 }],
308 });
309
310 let var = vec![VariableDefinition {
311 variable: Variable { name: "orderBys" },
312 of_type: Type::ListType(&Type::NamedType(NamedType {
313 name: "orderByInput",
314 })),
315 default_value: Value::Null,
316 directives: Directives::default_in(&ctx.arena)
317 }];
318
319 let var_defs = VariableDefinitions {
320 children: Vec::from_iter_in(var, &ctx.arena),
321 };
322 let _ = ast_variables_from_value(&ctx, &input, &var_defs).unwrap();
323 }
324}