apollo_compiler/validation/
value.rs1use crate::ast;
2use crate::coordinate::TypeAttributeCoordinate;
3use crate::schema;
4use crate::validation::diagnostics::DiagnosticData;
5use crate::validation::DiagnosticList;
6use crate::Node;
7
8fn unsupported_type(
9 diagnostics: &mut DiagnosticList,
10 value: &Node<ast::Value>,
11 declared_type: &Node<ast::Type>,
12) {
13 diagnostics.push(
14 value.location(),
15 DiagnosticData::UnsupportedValueType {
16 ty: declared_type.clone(),
17 value: value.clone(),
18 definition_location: declared_type.location(),
19 },
20 )
21}
22
23pub(crate) fn validate_values(
24 diagnostics: &mut DiagnosticList,
25 schema: &crate::Schema,
26 ty: &Node<ast::Type>,
27 argument: &Node<ast::Argument>,
28 var_defs: &[Node<ast::VariableDefinition>],
29) {
30 value_of_correct_type(diagnostics, schema, ty, &argument.value, var_defs);
31}
32
33pub(crate) fn value_of_correct_type(
34 diagnostics: &mut DiagnosticList,
35 schema: &crate::Schema,
36 ty: &Node<ast::Type>,
37 arg_value: &Node<ast::Value>,
38 var_defs: &[Node<ast::VariableDefinition>],
39) {
40 let Some(type_definition) = schema.types.get(ty.inner_named_type()) else {
41 return;
42 };
43
44 match &**arg_value {
45 ast::Value::Int(int) => match &type_definition {
54 schema::ExtendedType::Scalar(scalar) if !scalar.is_built_in() => {}
56 schema::ExtendedType::Scalar(scalar) => match scalar.name.as_str() {
57 "ID" => {}
59 "Int" => {
60 if int.try_to_i32().is_err() {
61 diagnostics.push(
62 arg_value.location(),
63 DiagnosticData::IntCoercionError {
64 value: int.as_str().to_owned(),
65 },
66 )
67 }
68 }
69 "Float" => {
70 if int.try_to_f64().is_err() {
71 diagnostics.push(
72 arg_value.location(),
73 DiagnosticData::FloatCoercionError {
74 value: int.as_str().to_owned(),
75 },
76 )
77 }
78 }
79 _ => unsupported_type(diagnostics, arg_value, ty),
80 },
81 _ => unsupported_type(diagnostics, arg_value, ty),
82 },
83 ast::Value::Float(float) => match &type_definition {
88 schema::ExtendedType::Scalar(scalar) if !scalar.is_built_in() => {}
90 schema::ExtendedType::Scalar(scalar) if scalar.name == "Float" => {
91 if float.try_to_f64().is_err() {
92 diagnostics.push(
93 arg_value.location(),
94 DiagnosticData::FloatCoercionError {
95 value: float.as_str().to_owned(),
96 },
97 )
98 }
99 }
100 _ => unsupported_type(diagnostics, arg_value, ty),
101 },
102 ast::Value::String(_) => match &type_definition {
108 schema::ExtendedType::Scalar(scalar) => {
109 if scalar.is_built_in() && !matches!(scalar.name.as_str(), "String" | "ID") {
114 unsupported_type(diagnostics, arg_value, ty);
115 }
116 }
117 _ => unsupported_type(diagnostics, arg_value, ty),
118 },
119 ast::Value::Boolean(_) => match &type_definition {
123 schema::ExtendedType::Scalar(scalar) => {
124 if scalar.is_built_in() && scalar.name.as_str() != "Boolean" {
125 unsupported_type(diagnostics, arg_value, ty);
126 }
127 }
128 _ => unsupported_type(diagnostics, arg_value, ty),
129 },
130 ast::Value::Null => {
131 if ty.is_non_null() {
132 unsupported_type(diagnostics, arg_value, ty);
133 }
134 }
135 ast::Value::Variable(var_name) => {
136 if let Some(var_def) = var_defs.iter().find(|v| v.name == *var_name) {
137 match &type_definition {
138 schema::ExtendedType::Scalar(_)
139 | schema::ExtendedType::Enum(_)
140 | schema::ExtendedType::InputObject(_) => {
141 if var_def.ty.inner_named_type() != ty.inner_named_type() {
145 unsupported_type(diagnostics, arg_value, ty);
146 }
147 }
148 _ => unsupported_type(diagnostics, arg_value, ty),
149 }
150 } else {
151 diagnostics.push(
152 arg_value.location(),
153 DiagnosticData::UndefinedVariable {
154 name: var_name.clone(),
155 },
156 );
157 }
158 }
159 ast::Value::Enum(value) => match &type_definition {
160 schema::ExtendedType::Scalar(scalar) if !scalar.is_built_in() => {
161 }
163 schema::ExtendedType::Enum(enum_) => {
164 if !enum_.values.contains_key(value) {
165 diagnostics.push(
166 value.location(),
167 DiagnosticData::UndefinedEnumValue {
168 value: value.clone(),
169 definition: enum_.name.clone(),
170 definition_location: enum_.location(),
171 },
172 );
173 }
174 }
175 _ => unsupported_type(diagnostics, arg_value, ty),
176 },
177 ast::Value::List(li) => {
186 let accepts_list = ty.is_list()
187 || matches!(type_definition, schema::ExtendedType::Scalar(scalar) if !scalar.is_built_in());
189 if !accepts_list {
190 unsupported_type(diagnostics, arg_value, ty)
191 } else {
192 let item_type = ty.same_location(ty.item_type().clone());
193 if type_definition.is_input_type() {
194 for v in li {
195 value_of_correct_type(diagnostics, schema, &item_type, v, var_defs);
196 }
197 } else {
198 unsupported_type(diagnostics, arg_value, &item_type);
199 }
200 }
201 }
202 ast::Value::Object(obj) => match &type_definition {
203 schema::ExtendedType::Scalar(scalar) if !scalar.is_built_in() => {}
204 schema::ExtendedType::InputObject(input_obj) => {
205 let undefined_field = obj
206 .iter()
207 .find(|(name, ..)| !input_obj.fields.contains_key(name));
208
209 if let Some((name, value)) = undefined_field {
212 diagnostics.push(
213 value.location(),
214 DiagnosticData::UndefinedInputValue {
215 value: name.clone(),
216 definition: input_obj.name.clone(),
217 definition_location: input_obj.location(),
218 },
219 );
220 }
221
222 input_obj.fields.iter().for_each(|(input_name, f)| {
223 let ty = &f.ty;
224 let is_missing = !obj.iter().any(|(value_name, ..)| input_name == value_name);
225 let is_null = obj
226 .iter()
227 .any(|(name, value)| input_name == name && value.is_null());
228
229 if (ty.is_non_null() && f.default_value.is_none()) && (is_missing || is_null) {
234 diagnostics.push(
235 arg_value.location(),
236 DiagnosticData::RequiredField {
237 name: input_name.clone(),
238 coordinate: TypeAttributeCoordinate {
239 ty: input_obj.name.clone(),
240 attribute: input_name.clone(),
241 },
242 expected_type: ty.clone(),
243 definition_location: f.location(),
244 },
245 );
246 }
247
248 let used_val = obj.iter().find(|(obj_name, ..)| obj_name == input_name);
249
250 if let Some((_, v)) = used_val {
251 value_of_correct_type(diagnostics, schema, ty, v, var_defs);
252 }
253 })
254 }
255 _ => unsupported_type(diagnostics, arg_value, ty),
256 },
257 }
258}