1use apollo_compiler::ast::FieldDefinition;
4use apollo_compiler::ast::Type;
5use apollo_compiler::collections::IndexMap;
6use apollo_compiler::parser::SourceSpan;
7use apollo_compiler::schema::Component;
8use apollo_compiler::schema::EnumType;
9use apollo_compiler::schema::ExtendedType;
10use apollo_compiler::schema::InputObjectType;
11use apollo_compiler::schema::InterfaceType;
12use apollo_compiler::schema::ObjectType;
13use apollo_compiler::schema::UnionType;
14use apollo_compiler::Name;
15use apollo_compiler::Node;
16use apollo_compiler::Schema;
17use indexmap::IndexSet;
18
19use crate::location::Location;
20use crate::location::SourceId;
21use crate::name::Name as CrateName;
22use crate::name::Namespace;
23use crate::name::NotFinal;
24use crate::Shape;
25
26#[must_use]
29pub fn namespace_from_schema(schema: &Schema) -> Namespace<NotFinal> {
30 GraphQLSchemaWalker::new(schema).compute_namespace()
31}
32
33#[derive(Debug)]
34struct GraphQLSchemaWalker<'a> {
35 schema: &'a Schema,
36 visited: IndexSet<&'a str>,
37}
38
39impl<'a> GraphQLSchemaWalker<'a> {
40 fn new(schema: &'a Schema) -> Self {
41 Self {
42 schema,
43 visited: IndexSet::new(),
44 }
45 }
46
47 fn compute_namespace(mut self) -> Namespace<NotFinal> {
48 let mut namespace = Namespace::new();
49
50 namespace.insert("String", Shape::string([]));
52 namespace.insert("Int", Shape::int([]));
53 namespace.insert("Float", Shape::float([]));
54 namespace.insert("Boolean", Shape::bool([]));
55 namespace.insert("ID", Shape::one([Shape::string([]), Shape::int([])], []));
56
57 for (name, extended) in &self.schema.types {
58 if namespace.has(name.as_str()) {
59 debug_assert!(extended.is_built_in());
60 } else if !extended.is_built_in() {
61 namespace.insert(name.as_str(), self.shape_from_extended_type(extended));
62 }
63 }
64
65 namespace
66 }
67
68 fn shape_from_type(&mut self, ty: &Type) -> Shape {
69 let inner_name = ty.inner_named_type();
70 let inner_shape = if let Some(inner_type) = self.schema.types.get(inner_name) {
71 self.shape_from_extended_type(inner_type)
72 } else {
73 Shape::unknown(span(ty.inner_named_type().location()))
74 };
75
76 match ty {
77 Type::Named(_) => Self::nullable(inner_shape),
78 Type::NonNullNamed(_) => inner_shape,
79 Type::List(_) => Self::nullable(Shape::list(inner_shape, [])),
80 Type::NonNullList(_) => Shape::list(inner_shape, []),
81 }
82 }
83
84 fn nullable(shape: Shape) -> Shape {
85 let locations = shape.locations().cloned().collect::<Vec<_>>();
86 Shape::one([shape, Shape::null([])], locations)
87 }
88
89 fn shape_from_extended_type(&mut self, extended: &'a ExtendedType) -> Shape {
90 self.shape_from_extended_type_with_context(extended, false)
91 }
92
93 fn shape_from_extended_type_with_context(
94 &mut self,
95 extended: &'a ExtendedType,
96 in_abstract_context: bool,
97 ) -> Shape {
98 let type_name = extended.name().as_str();
99
100 if self.visited.contains(type_name) {
102 return Shape::name(type_name, span(extended.location()));
103 }
104
105 self.visited.insert(type_name);
107
108 let result = match extended {
109 ExtendedType::Scalar(node) => Shape::unknown(span(node.location())),
110
111 ExtendedType::Object(node) => {
112 let typename_shape = if in_abstract_context {
114 Shape::string_value(type_name, span(node.location()))
116 } else {
117 Shape::one(
119 [
120 Shape::string_value(type_name, span(node.location())),
121 Shape::none(),
122 ],
123 span(node.location()),
124 )
125 };
126
127 let fields = node
128 .fields
129 .iter()
130 .map(|(name, field)| (name.to_string(), self.shape_from_type(&field.ty)))
131 .chain(std::iter::once(("__typename".to_string(), typename_shape)))
132 .collect();
133
134 Shape::record(fields, span(node.location()))
135 }
136
137 ExtendedType::Interface(node) => {
138 let implementing_types: Vec<Shape> = self
141 .schema
142 .types
143 .iter()
144 .filter_map(|(_name, extended)| {
145 if let ExtendedType::Object(obj) = extended {
146 if obj.implements_interfaces.contains(&node.name) {
147 let mut impl_shape =
149 self.shape_from_extended_type_with_context(extended, true);
150
151 let concrete_type_name = extended.name().as_str();
153 let concrete_name =
154 CrateName::base(concrete_type_name, span(extended.location()));
155 impl_shape = impl_shape.with_name(&concrete_name);
156
157 Some(impl_shape)
158 } else {
159 None
160 }
161 } else {
162 None
163 }
164 })
165 .collect();
166
167 Shape::one(implementing_types, span(node.location()))
168 }
169
170 ExtendedType::Union(node) => Shape::one(
171 node.members.iter().filter_map(|member| {
172 self.schema.types.get(&member.name).map(|extended| {
173 let mut member_shape =
175 self.shape_from_extended_type_with_context(extended, true);
176
177 let concrete_type_name = extended.name().as_str();
179 let concrete_name =
180 CrateName::base(concrete_type_name, span(extended.location()));
181 member_shape = member_shape.with_name(&concrete_name);
182
183 member_shape
184 })
185 }),
186 span(node.location()),
187 ),
188
189 ExtendedType::Enum(node) => Shape::one(
190 node.values
191 .iter()
192 .map(|(name, _)| Shape::string_value(name.as_str(), span(name.location()))),
193 span(node.location()),
194 ),
195
196 ExtendedType::InputObject(node) => Shape::record(
197 node.fields
198 .iter()
199 .map(|(name, field)| (name.to_string(), self.shape_from_type(&field.ty)))
200 .collect(),
201 span(node.location()),
202 ),
203 };
204
205 self.visited.shift_remove(type_name);
207
208 result
209 }
210}
211
212#[must_use]
216pub fn shapes_for_schema(schema: &Schema) -> IndexMap<String, Shape> {
217 namespace_from_schema(schema).finalize().iter().collect()
218}
219
220#[must_use]
221pub fn shape_for_arguments(field_definition: &Component<FieldDefinition>) -> Shape {
222 let mut source_span = None;
224 for next_source_span in field_definition.arguments.iter().map(Node::location) {
225 source_span = SourceSpan::recompose(source_span, next_source_span);
226 }
227 Shape::record(
228 field_definition
229 .arguments
230 .iter()
231 .map(|arg| {
232 (
233 arg.name.to_string(),
234 from_type(arg.ty.as_ref(), span(arg.location())),
235 )
236 })
237 .collect(),
238 span(source_span),
239 )
240}
241
242impl From<&ExtendedType> for Shape {
243 fn from(ext: &ExtendedType) -> Self {
244 match ext {
245 ExtendedType::Scalar(scalar) => Shape::unknown(span(scalar.location())),
246 ExtendedType::Object(obj) => Self::from(obj),
247 ExtendedType::Interface(intf) => Self::from(intf),
248 ExtendedType::Union(union) => Self::from(union),
249 ExtendedType::Enum(enm) => Self::from(enm),
250 ExtendedType::InputObject(input) => Self::from(input),
251 }
252 }
253}
254
255impl From<&Name> for Shape {
256 fn from(name: &Name) -> Self {
257 let locations = span(name.location());
258 match name.as_str() {
259 "String" | "ID" => Self::string(locations),
260 "Int" => Self::int(locations),
261 "Float" => Self::float(locations),
262 "Boolean" => Self::bool(locations),
263 other => Self::name(other, locations),
264 }
265 }
266}
267
268impl From<&Node<ObjectType>> for Shape {
269 fn from(object_type: &Node<ObjectType>) -> Self {
270 record(&object_type.fields, span(object_type.location()))
271 }
272}
273
274fn record(fields: &IndexMap<Name, Component<FieldDefinition>>, locations: Vec<Location>) -> Shape {
275 let fields = fields
276 .iter()
277 .map(|(key, value)| (key.to_string(), Shape::from(value)))
278 .collect();
279 Shape::record(fields, locations)
280}
281
282fn nullable(shape: Shape, locations: Vec<Location>) -> Shape {
283 Shape::one([shape, Shape::null(locations.clone())], locations)
284}
285
286impl From<&Component<FieldDefinition>> for Shape {
287 fn from(field: &Component<FieldDefinition>) -> Self {
288 let locations = span(field.location());
289 from_type(&field.ty, locations)
290 }
291}
292
293fn from_type(ty: &Type, locations: Vec<Location>) -> Shape {
294 match ty {
295 Type::Named(name) => nullable(Shape::from(name), locations),
296 Type::NonNullNamed(name) => Shape::from(name),
297 Type::List(ty) => nullable(
298 Shape::list(from_type(ty.as_ref(), locations.clone()), locations.clone()),
299 locations,
300 ),
301 Type::NonNullList(ty) => Shape::list(from_type(ty.as_ref(), locations.clone()), locations),
302 }
303}
304
305impl From<&Node<InterfaceType>> for Shape {
306 fn from(interface_type: &Node<InterfaceType>) -> Self {
307 record(&interface_type.fields, span(interface_type.location()))
308 }
309}
310
311impl From<&Node<UnionType>> for Shape {
312 fn from(union_type: &Node<UnionType>) -> Self {
313 Self::one(
314 union_type
315 .members
316 .iter()
317 .map(|member| Self::from(&member.name)),
318 span(union_type.location()),
319 )
320 }
321}
322
323impl From<&Node<EnumType>> for Shape {
324 fn from(enum_type: &Node<EnumType>) -> Self {
325 Shape::one(
326 enum_type
327 .values
328 .iter()
329 .map(|(name, _)| Shape::string_value(name.as_str(), span(name.location()))),
330 span(enum_type.location()),
331 )
332 }
333}
334
335impl From<&Node<InputObjectType>> for Shape {
336 fn from(input_object_type: &Node<InputObjectType>) -> Self {
337 Self::record(
338 input_object_type
339 .fields
340 .iter()
341 .map(|(name, input)| {
342 (
343 name.to_string(),
344 from_type(input.ty.as_ref(), span(input.location())),
345 )
346 })
347 .collect(),
348 span(input_object_type.location()),
349 )
350 }
351}
352
353fn span(source_span: Option<SourceSpan>) -> Vec<Location> {
354 source_span.into_iter().map(Location::from).collect()
355}
356
357impl From<SourceSpan> for Location {
358 fn from(span: SourceSpan) -> Self {
359 Location {
360 source_id: SourceId::GraphQL(span.file_id()),
361 span: span.offset()..span.end_offset(),
362 }
363 }
364}