1mod graphql_parser_conversion;
2mod json_conversion;
3
4#[cfg(test)]
5mod tests;
6
7use crate::query::UsedTypes;
8use crate::type_qualifiers::GraphqlTypeQualifier;
9use std::collections::HashMap;
10
11pub(crate) const DEFAULT_SCALARS: &[&str] = &["ID", "String", "Int", "Float", "Boolean"];
12
13#[derive(Debug, PartialEq, Clone)]
14struct StoredObjectField {
15 name: String,
16 object: ObjectId,
17}
18
19#[derive(Debug, PartialEq, Clone)]
20pub(crate) struct StoredObject {
21 pub(crate) name: String,
22 pub(crate) fields: Vec<StoredFieldId>,
23 pub(crate) implements_interfaces: Vec<InterfaceId>,
24}
25
26#[derive(Debug, PartialEq, Clone)]
27pub(crate) struct StoredField {
28 pub(crate) name: String,
29 pub(crate) r#type: StoredFieldType,
30 pub(crate) parent: StoredFieldParent,
31 pub(crate) deprecation: Option<Option<String>>,
33}
34
35impl StoredField {
36 pub(crate) fn deprecation(&self) -> Option<Option<&str>> {
37 self.deprecation.as_ref().map(|inner| inner.as_deref())
38 }
39}
40
41#[derive(Debug, PartialEq, Clone)]
42pub(crate) enum StoredFieldParent {
43 Object(ObjectId),
44 Interface(InterfaceId),
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
48pub(crate) struct ObjectId(u32);
49
50#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
51pub(crate) struct ObjectFieldId(usize);
52
53#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
54pub(crate) struct InterfaceId(usize);
55
56#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
57pub(crate) struct ScalarId(usize);
58
59#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
60pub(crate) struct UnionId(usize);
61
62#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
63pub(crate) struct EnumId(usize);
64
65#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
66pub(crate) struct InputId(u32);
67
68#[derive(Debug, Clone, Copy, PartialEq)]
69pub(crate) struct StoredFieldId(usize);
70
71#[derive(Debug, Clone, Copy, PartialEq)]
72struct InputFieldId(usize);
73
74#[derive(Debug, Clone, PartialEq)]
75pub(crate) struct StoredInterface {
76 name: String,
77 fields: Vec<StoredFieldId>,
78}
79
80#[derive(Debug, Clone, PartialEq)]
81pub(crate) struct StoredFieldType {
82 pub(crate) id: TypeId,
83 pub(crate) qualifiers: Vec<GraphqlTypeQualifier>,
88}
89
90#[derive(Debug, Clone, PartialEq)]
91pub(crate) struct StoredUnion {
92 pub(crate) name: String,
93 pub(crate) variants: Vec<TypeId>,
94}
95
96#[derive(Debug, Clone, PartialEq)]
97pub(crate) struct StoredScalar {
98 pub(crate) name: String,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
102pub(crate) enum TypeId {
103 Object(ObjectId),
104 Scalar(ScalarId),
105 Interface(InterfaceId),
106 Union(UnionId),
107 Enum(EnumId),
108 Input(InputId),
109}
110
111impl TypeId {
112 fn r#enum(id: usize) -> Self {
113 TypeId::Enum(EnumId(id))
114 }
115
116 fn interface(id: usize) -> Self {
117 TypeId::Interface(InterfaceId(id))
118 }
119
120 fn union(id: usize) -> Self {
121 TypeId::Union(UnionId(id))
122 }
123
124 fn object(id: u32) -> Self {
125 TypeId::Object(ObjectId(id))
126 }
127
128 fn input(id: u32) -> Self {
129 TypeId::Input(InputId(id))
130 }
131
132 fn as_interface_id(&self) -> Option<InterfaceId> {
133 match self {
134 TypeId::Interface(id) => Some(*id),
135 _ => None,
136 }
137 }
138
139 fn as_object_id(&self) -> Option<ObjectId> {
140 match self {
141 TypeId::Object(id) => Some(*id),
142 _ => None,
143 }
144 }
145
146 pub(crate) fn as_input_id(&self) -> Option<InputId> {
147 match self {
148 TypeId::Input(id) => Some(*id),
149 _ => None,
150 }
151 }
152
153 pub(crate) fn as_scalar_id(&self) -> Option<ScalarId> {
154 match self {
155 TypeId::Scalar(id) => Some(*id),
156 _ => None,
157 }
158 }
159
160 pub(crate) fn as_enum_id(&self) -> Option<EnumId> {
161 match self {
162 TypeId::Enum(id) => Some(*id),
163 _ => None,
164 }
165 }
166
167 pub(crate) fn name<'a>(&self, schema: &'a Schema) -> &'a str {
168 match self {
169 TypeId::Object(obj) => schema.get_object(*obj).name.as_str(),
170 TypeId::Scalar(s) => schema.get_scalar(*s).name.as_str(),
171 TypeId::Interface(s) => schema.get_interface(*s).name.as_str(),
172 TypeId::Union(s) => schema.get_union(*s).name.as_str(),
173 TypeId::Enum(s) => schema.get_enum(*s).name.as_str(),
174 TypeId::Input(s) => schema.get_input(*s).name.as_str(),
175 }
176 }
177}
178
179#[derive(Debug, Clone, PartialEq)]
180pub(crate) struct StoredEnum {
181 pub(crate) name: String,
182 pub(crate) variants: Vec<String>,
183}
184
185#[derive(Debug, Clone, PartialEq)]
186pub(crate) struct StoredInputFieldType {
187 pub(crate) id: TypeId,
188 pub(crate) qualifiers: Vec<GraphqlTypeQualifier>,
189}
190
191impl StoredInputFieldType {
192 pub(crate) fn is_indirected(&self) -> bool {
196 self.qualifiers
197 .iter()
198 .any(|qualifier| qualifier == &GraphqlTypeQualifier::List)
199 }
200
201 pub(crate) fn is_optional(&self) -> bool {
202 self.qualifiers
203 .get(0)
204 .map(|qualifier| !qualifier.is_required())
205 .unwrap_or(true)
206 }
207}
208
209#[derive(Debug, Clone, PartialEq)]
210pub(crate) struct StoredInputType {
211 pub(crate) name: String,
212 pub(crate) fields: Vec<(String, StoredInputFieldType)>,
213}
214
215#[derive(Debug, Clone, PartialEq)]
217pub(crate) struct Schema {
218 stored_objects: Vec<StoredObject>,
219 stored_fields: Vec<StoredField>,
220 stored_interfaces: Vec<StoredInterface>,
221 stored_unions: Vec<StoredUnion>,
222 stored_scalars: Vec<StoredScalar>,
223 stored_enums: Vec<StoredEnum>,
224 stored_inputs: Vec<StoredInputType>,
225 names: HashMap<String, TypeId>,
226
227 pub(crate) query_type: Option<ObjectId>,
228 pub(crate) mutation_type: Option<ObjectId>,
229 pub(crate) subscription_type: Option<ObjectId>,
230}
231
232impl Schema {
233 pub(crate) fn new() -> Schema {
234 let mut schema = Schema {
235 stored_objects: Vec::new(),
236 stored_interfaces: Vec::new(),
237 stored_fields: Vec::new(),
238 stored_unions: Vec::new(),
239 stored_scalars: Vec::with_capacity(DEFAULT_SCALARS.len()),
240 stored_enums: Vec::new(),
241 stored_inputs: Vec::new(),
242 names: HashMap::new(),
243 query_type: None,
244 mutation_type: None,
245 subscription_type: None,
246 };
247
248 schema.push_default_scalars();
249
250 schema
251 }
252
253 fn push_default_scalars(&mut self) {
254 for scalar in DEFAULT_SCALARS {
255 let id = self.push_scalar(StoredScalar {
256 name: (*scalar).to_owned(),
257 });
258
259 self.names.insert((*scalar).to_owned(), TypeId::Scalar(id));
260 }
261 }
262
263 fn push_object(&mut self, object: StoredObject) -> ObjectId {
264 let id = ObjectId(self.stored_objects.len() as u32);
265 self.stored_objects.push(object);
266
267 id
268 }
269
270 fn push_interface(&mut self, interface: StoredInterface) -> InterfaceId {
271 let id = InterfaceId(self.stored_interfaces.len());
272
273 self.stored_interfaces.push(interface);
274
275 id
276 }
277
278 fn push_scalar(&mut self, scalar: StoredScalar) -> ScalarId {
279 let id = ScalarId(self.stored_scalars.len());
280
281 self.stored_scalars.push(scalar);
282
283 id
284 }
285
286 fn push_enum(&mut self, enm: StoredEnum) -> EnumId {
287 let id = EnumId(self.stored_enums.len());
288
289 self.stored_enums.push(enm);
290
291 id
292 }
293
294 fn push_field(&mut self, field: StoredField) -> StoredFieldId {
295 let id = StoredFieldId(self.stored_fields.len());
296
297 self.stored_fields.push(field);
298
299 id
300 }
301
302 pub(crate) fn query_type(&self) -> ObjectId {
303 self.query_type
304 .expect("Query operation type must be defined")
305 }
306
307 pub(crate) fn mutation_type(&self) -> Option<ObjectId> {
308 self.mutation_type
309 }
310
311 pub(crate) fn subscription_type(&self) -> Option<ObjectId> {
312 self.subscription_type
313 }
314
315 pub(crate) fn get_interface(&self, interface_id: InterfaceId) -> &StoredInterface {
316 self.stored_interfaces.get(interface_id.0).unwrap()
317 }
318
319 pub(crate) fn get_input(&self, input_id: InputId) -> &StoredInputType {
320 self.stored_inputs.get(input_id.0 as usize).unwrap()
321 }
322
323 pub(crate) fn get_object(&self, object_id: ObjectId) -> &StoredObject {
324 self.stored_objects
325 .get(object_id.0 as usize)
326 .expect("Schema::get_object")
327 }
328
329 pub(crate) fn get_field(&self, field_id: StoredFieldId) -> &StoredField {
330 self.stored_fields.get(field_id.0).unwrap()
331 }
332
333 pub(crate) fn get_enum(&self, enum_id: EnumId) -> &StoredEnum {
334 self.stored_enums.get(enum_id.0).unwrap()
335 }
336
337 pub(crate) fn get_scalar(&self, scalar_id: ScalarId) -> &StoredScalar {
338 self.stored_scalars.get(scalar_id.0).unwrap()
339 }
340
341 pub(crate) fn get_union(&self, union_id: UnionId) -> &StoredUnion {
342 self.stored_unions
343 .get(union_id.0)
344 .expect("Schema::get_union")
345 }
346
347 fn find_interface(&self, interface_name: &str) -> InterfaceId {
348 self.find_type_id(interface_name).as_interface_id().unwrap()
349 }
350
351 pub(crate) fn find_type(&self, type_name: &str) -> Option<TypeId> {
352 self.names.get(type_name).copied()
353 }
354
355 pub(crate) fn objects(&self) -> impl Iterator<Item = (ObjectId, &StoredObject)> {
356 self.stored_objects
357 .iter()
358 .enumerate()
359 .map(|(idx, obj)| (ObjectId(idx as u32), obj))
360 }
361
362 pub(crate) fn inputs(&self) -> impl Iterator<Item = (InputId, &StoredInputType)> {
363 self.stored_inputs
364 .iter()
365 .enumerate()
366 .map(|(idx, obj)| (InputId(idx as u32), obj))
367 }
368
369 fn find_type_id(&self, type_name: &str) -> TypeId {
370 match self.names.get(type_name) {
371 Some(id) => *id,
372 None => {
373 panic!(
374 "graphql-client-codegen internal error: failed to resolve TypeId for `{}°.",
375 type_name
376 );
377 }
378 }
379 }
380}
381
382impl StoredInputType {
383 pub(crate) fn used_input_ids_recursive(&self, used_types: &mut UsedTypes, schema: &Schema) {
384 for type_id in self.fields.iter().map(|(_name, ty)| ty.id) {
385 match type_id {
386 TypeId::Input(input_id) => {
387 if used_types.types.contains(&type_id) {
388 continue;
389 } else {
390 used_types.types.insert(type_id);
391 let input = schema.get_input(input_id);
392 input.used_input_ids_recursive(used_types, schema);
393 }
394 }
395 TypeId::Enum(_) | TypeId::Scalar(_) => {
396 used_types.types.insert(type_id);
397 }
398 _ => (),
399 }
400 }
401 }
402
403 fn contains_type_without_indirection(&self, input_id: InputId, schema: &Schema) -> bool {
404 self.fields.iter().any(|(_name, field_type)| {
406 if field_type.is_indirected() {
408 return false;
409 }
410
411 let field_input_id = field_type.id.as_input_id();
412
413 if let Some(field_input_id) = field_input_id {
414 if field_input_id == input_id {
415 return true;
416 }
417
418 let input = schema.get_input(field_input_id);
419
420 input.contains_type_without_indirection(input_id, schema)
422 } else {
423 false
425 }
426 })
427 }
428}
429
430pub(crate) fn input_is_recursive_without_indirection(input_id: InputId, schema: &Schema) -> bool {
431 let input = schema.get_input(input_id);
432 input.contains_type_without_indirection(input_id, schema)
433}
434
435impl std::convert::From<gurkle_parser::schema::Document> for Schema {
436 fn from(ast: gurkle_parser::schema::Document) -> Schema {
437 graphql_parser_conversion::build_schema(ast)
438 }
439}
440
441impl std::convert::From<crate::introspection_response::IntrospectionResponse> for Schema {
442 fn from(src: crate::introspection_response::IntrospectionResponse) -> Self {
443 json_conversion::build_schema(src)
444 }
445}
446
447pub(crate) fn resolve_field_type(
448 schema: &Schema,
449 inner: &gurkle_parser::schema::Type,
450) -> StoredFieldType {
451 use crate::type_qualifiers::graphql_parser_depth;
452 use gurkle_parser::schema::Type::*;
453
454 let qualifiers_depth = graphql_parser_depth(inner);
455 let mut qualifiers = Vec::with_capacity(qualifiers_depth);
456
457 let mut inner = inner;
458
459 loop {
460 match inner {
461 ListType(new_inner) => {
462 qualifiers.push(GraphqlTypeQualifier::List);
463 inner = new_inner;
464 }
465 NonNullType(new_inner) => {
466 qualifiers.push(GraphqlTypeQualifier::Required);
467 inner = new_inner;
468 }
469 NamedType(name) => {
470 return StoredFieldType {
471 id: schema.find_type_id(name),
472 qualifiers,
473 }
474 }
475 }
476 }
477}
478
479pub(crate) trait ObjectLike {
480 fn name(&self) -> &str;
481
482 fn get_field_by_name<'a>(
483 &'a self,
484 name: &str,
485 schema: &'a Schema,
486 ) -> Option<(StoredFieldId, &'a StoredField)>;
487}
488
489impl ObjectLike for StoredObject {
490 fn name(&self) -> &str {
491 &self.name
492 }
493
494 fn get_field_by_name<'a>(
495 &'a self,
496 name: &str,
497 schema: &'a Schema,
498 ) -> Option<(StoredFieldId, &'a StoredField)> {
499 self.fields
500 .iter()
501 .map(|field_id| (*field_id, schema.get_field(*field_id)))
502 .find(|(_, f)| f.name == name)
503 }
504}
505
506impl ObjectLike for StoredInterface {
507 fn name(&self) -> &str {
508 &self.name
509 }
510
511 fn get_field_by_name<'a>(
512 &'a self,
513 name: &str,
514 schema: &'a Schema,
515 ) -> Option<(StoredFieldId, &'a StoredField)> {
516 self.fields
517 .iter()
518 .map(|field_id| (*field_id, schema.get_field(*field_id)))
519 .find(|(_, field)| field.name == name)
520 }
521}