juniper/types/
utilities.rs1use std::collections::HashSet;
2
3use itertools::Itertools as _;
4
5use crate::{
6 ast::InputValue,
7 schema::{
8 meta::{Argument, EnumMeta, InputObjectMeta, MetaType},
9 model::{SchemaType, TypeType},
10 },
11 value::ScalarValue,
12};
13
14pub(crate) mod error {
16 use std::fmt::Display;
17
18 pub(crate) fn non_null(arg_type: impl Display) -> String {
19 format!("\"null\" specified for not nullable type \"{arg_type}\"")
20 }
21
22 pub(crate) fn enum_value(arg_value: impl Display, arg_type: impl Display) -> String {
23 format!("Invalid value \"{arg_value}\" for enum \"{arg_type}\"")
24 }
25
26 pub(crate) fn type_value(arg_value: impl Display, arg_type: impl Display) -> String {
27 format!("Invalid value \"{arg_value}\" for type \"{arg_type}\"")
28 }
29
30 pub(crate) fn parser(arg_type: impl Display, msg: impl Display) -> String {
31 format!("Parser error for \"{arg_type}\": {msg}")
32 }
33
34 pub(crate) fn not_input_object(arg_type: impl Display) -> String {
35 format!("\"{arg_type}\" is not an input object")
36 }
37
38 pub(crate) fn field(
39 arg_type: impl Display,
40 field_name: impl Display,
41 error_message: impl Display,
42 ) -> String {
43 format!("Error on \"{arg_type}\" field \"{field_name}\": {error_message}")
44 }
45
46 pub(crate) fn missing_fields(arg_type: impl Display, missing_fields: impl Display) -> String {
47 format!("\"{arg_type}\" is missing fields: {missing_fields}")
48 }
49
50 pub(crate) fn unknown_field(arg_type: impl Display, field_name: impl Display) -> String {
51 format!("Field \"{field_name}\" does not exist on type \"{arg_type}\"")
52 }
53
54 pub(crate) fn invalid_list_length(
55 arg_value: impl Display,
56 actual: usize,
57 expected: usize,
58 ) -> String {
59 format!("Expected list of length {expected}, but \"{arg_value}\" has length {actual}")
60 }
61}
62
63fn validate_object_field<S>(
66 schema: &SchemaType<S>,
67 object_type: &TypeType<S>,
68 object_fields: &[Argument<S>],
69 field_value: &InputValue<S>,
70 field_key: &str,
71) -> Option<String>
72where
73 S: ScalarValue,
74{
75 let field_type = object_fields
76 .iter()
77 .filter(|f| f.name == field_key)
78 .map(|f| schema.make_type(&f.arg_type))
79 .next();
80
81 if let Some(field_arg_type) = field_type {
82 let error_message = validate_literal_value(schema, &field_arg_type, field_value);
83
84 error_message.map(|m| error::field(object_type, field_key, m))
85 } else {
86 Some(error::unknown_field(object_type, field_key))
87 }
88}
89
90pub fn validate_literal_value<S>(
92 schema: &SchemaType<S>,
93 arg_type: &TypeType<S>,
94 arg_value: &InputValue<S>,
95) -> Option<String>
96where
97 S: ScalarValue,
98{
99 match *arg_type {
100 TypeType::NonNull(ref inner) => {
101 if arg_value.is_null() {
102 Some(error::non_null(arg_type))
103 } else {
104 validate_literal_value(schema, inner, arg_value)
105 }
106 }
107 TypeType::List(ref inner, expected_size) => match *arg_value {
108 InputValue::Null | InputValue::Variable(_) => None,
109 InputValue::List(ref items) => {
110 if let Some(expected) = expected_size {
111 if items.len() != expected {
112 return Some(error::invalid_list_length(arg_value, items.len(), expected));
113 }
114 }
115 items
116 .iter()
117 .find_map(|i| validate_literal_value(schema, inner, &i.item))
118 }
119 ref v => {
120 if let Some(expected) = expected_size {
121 if expected != 1 {
122 return Some(error::invalid_list_length(arg_value, 1, expected));
123 }
124 }
125 validate_literal_value(schema, inner, v)
126 }
127 },
128 TypeType::Concrete(t) => {
129 if let (&InputValue::Scalar(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
132 (arg_value, arg_type.to_concrete())
133 {
134 return Some(error::enum_value(arg_value, arg_type));
135 }
136
137 match *arg_value {
138 InputValue::Null | InputValue::Variable(_) => None,
139 ref v @ InputValue::Scalar(_) | ref v @ InputValue::Enum(_) => {
140 if let Some(parse_fn) = t.input_value_parse_fn() {
141 if parse_fn(v).is_ok() {
142 None
143 } else {
144 Some(error::type_value(arg_value, arg_type))
145 }
146 } else {
147 Some(error::parser(arg_type, "no parser present"))
148 }
149 }
150 InputValue::List(_) => Some("Input lists are not literals".to_owned()),
151 InputValue::Object(ref obj) => {
152 if let MetaType::InputObject(InputObjectMeta {
153 ref input_fields, ..
154 }) = *t
155 {
156 let mut remaining_required_fields = input_fields
157 .iter()
158 .filter_map(|f| {
159 (f.arg_type.is_non_null() && f.default_value.is_none())
160 .then_some(f.name.as_str())
161 })
162 .collect::<HashSet<_>>();
163
164 let error_message = obj.iter().find_map(|(key, value)| {
165 remaining_required_fields.remove(key.item.as_str());
166 validate_object_field(
167 schema,
168 arg_type,
169 input_fields,
170 &value.item,
171 &key.item,
172 )
173 });
174
175 if error_message.is_some() {
176 return error_message;
177 }
178
179 if remaining_required_fields.is_empty() {
180 None
181 } else {
182 let missing_fields = remaining_required_fields
183 .into_iter()
184 .format_with(", ", |s, f| f(&format_args!("\"{s}\"")));
185 Some(error::missing_fields(arg_type, missing_fields))
186 }
187 } else {
188 Some(error::not_input_object(arg_type))
189 }
190 }
191 }
192 }
193 }
194}