1use crate::location::{Location, SourceId};
4use crate::Shape;
5use apollo_compiler::ast::{FieldDefinition, Type};
6use apollo_compiler::collections::IndexMap;
7use apollo_compiler::parser::SourceSpan;
8use apollo_compiler::schema::{
9 Component, EnumType, ExtendedType, InputObjectType, InterfaceType, ObjectType, UnionType,
10};
11use apollo_compiler::{Name, Node, Schema};
12
13#[must_use]
17pub fn shapes_for_schema(schema: &Schema) -> IndexMap<&str, Shape> {
18 schema
19 .types
20 .iter()
21 .filter(|&(_name, ty)| !ty.is_built_in())
22 .map(|(name, ty)| (name.as_str(), Shape::from(ty)))
23 .collect()
24}
25
26#[must_use]
27pub fn shape_for_arguments(field_definition: &Component<FieldDefinition>) -> Shape {
28 let mut source_span = None;
30 for next_source_span in field_definition.arguments.iter().map(Node::location) {
31 source_span = SourceSpan::recompose(source_span, next_source_span);
32 }
33 Shape::record(
34 field_definition
35 .arguments
36 .iter()
37 .map(|arg| {
38 (
39 arg.name.to_string(),
40 from_type(arg.ty.as_ref(), span(arg.location())),
41 )
42 })
43 .collect(),
44 span(source_span),
45 )
46}
47
48impl From<&ExtendedType> for Shape {
49 fn from(ext: &ExtendedType) -> Self {
50 match ext {
51 ExtendedType::Scalar(scalar) => Shape::unknown(span(scalar.location())),
52 ExtendedType::Object(obj) => Self::from(obj),
53 ExtendedType::Interface(intf) => Self::from(intf),
54 ExtendedType::Union(union) => Self::from(union),
55 ExtendedType::Enum(enm) => Self::from(enm),
56 ExtendedType::InputObject(input) => Self::from(input),
57 }
58 }
59}
60
61impl From<&Name> for Shape {
62 fn from(name: &Name) -> Self {
63 let locations = span(name.location());
64 match name.as_str() {
65 "String" | "ID" => Self::string(locations),
66 "Int" => Self::int(locations),
67 "Float" => Self::float(locations),
68 "Boolean" => Self::bool(locations),
69 other => Self::name(other, locations),
70 }
71 }
72}
73
74impl From<&Node<ObjectType>> for Shape {
75 fn from(object_type: &Node<ObjectType>) -> Self {
76 record(&object_type.fields, span(object_type.location()))
77 }
78}
79
80fn record(fields: &IndexMap<Name, Component<FieldDefinition>>, locations: Vec<Location>) -> Shape {
81 let fields = fields
82 .iter()
83 .map(|(key, value)| (key.to_string(), Shape::from(value)))
84 .collect();
85 Shape::record(fields, locations)
86}
87
88fn nullable(shape: Shape, locations: Vec<Location>) -> Shape {
89 Shape::one([shape, Shape::null(locations.clone())], locations)
90}
91
92impl From<&Component<FieldDefinition>> for Shape {
93 fn from(field: &Component<FieldDefinition>) -> Self {
94 let locations = span(field.location());
95 from_type(&field.ty, locations)
96 }
97}
98
99fn from_type(ty: &Type, locations: Vec<Location>) -> Shape {
100 match ty {
101 Type::Named(name) => nullable(Shape::from(name), locations),
102 Type::NonNullNamed(name) => Shape::from(name),
103 Type::List(ty) => nullable(
104 Shape::list(from_type(ty.as_ref(), locations.clone()), locations.clone()),
105 locations,
106 ),
107 Type::NonNullList(ty) => Shape::list(from_type(ty.as_ref(), locations.clone()), locations),
108 }
109}
110
111impl From<&Node<InterfaceType>> for Shape {
112 fn from(interface_type: &Node<InterfaceType>) -> Self {
113 record(&interface_type.fields, span(interface_type.location()))
114 }
115}
116
117impl From<&Node<UnionType>> for Shape {
118 fn from(union_type: &Node<UnionType>) -> Self {
119 Self::one(
120 union_type
121 .members
122 .iter()
123 .map(|member| Self::from(&member.name)),
124 span(union_type.location()),
125 )
126 }
127}
128
129impl From<&Node<EnumType>> for Shape {
130 fn from(enum_type: &Node<EnumType>) -> Self {
131 Shape::one(
132 enum_type
133 .values
134 .iter()
135 .map(|(name, _)| Shape::string_value(name.as_str(), span(name.location()))),
136 span(enum_type.location()),
137 )
138 }
139}
140
141impl From<&Node<InputObjectType>> for Shape {
142 fn from(input_object_type: &Node<InputObjectType>) -> Self {
143 Self::record(
144 input_object_type
145 .fields
146 .iter()
147 .map(|(name, input)| {
148 (
149 name.to_string(),
150 from_type(input.ty.as_ref(), span(input.location())),
151 )
152 })
153 .collect(),
154 span(input_object_type.location()),
155 )
156 }
157}
158
159fn span(source_span: Option<SourceSpan>) -> Vec<Location> {
160 source_span.into_iter().map(Location::from).collect()
161}
162
163impl From<SourceSpan> for Location {
164 fn from(span: SourceSpan) -> Self {
165 Location {
166 source_id: SourceId::GraphQL(span.file_id()),
167 span: span.offset()..span.end_offset(),
168 }
169 }
170}