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