1use crate::domain::error::WesleyError;
4use crate::domain::ir::*;
5use crate::domain::operation::{
6 OperationArgument, OperationDirectiveArgs, OperationType, SchemaOperation,
7};
8use crate::domain::schema_delta::{diff_schema_ir, SchemaDelta};
9use crate::ports::lowering::LoweringPort;
10use apollo_parser::{cst, Parser};
11use async_trait::async_trait;
12use indexmap::IndexMap;
13use std::collections::{BTreeMap, HashMap};
14
15pub struct ApolloLoweringAdapter {
17 _max_retries: usize,
18}
19
20impl ApolloLoweringAdapter {
21 pub fn new(max_retries: usize) -> Self {
23 Self {
24 _max_retries: max_retries,
25 }
26 }
27}
28
29#[async_trait]
30impl LoweringPort for ApolloLoweringAdapter {
31 async fn lower_sdl(&self, sdl: &str) -> Result<WesleyIR, WesleyError> {
32 self.parse_and_lower(sdl)
33 }
34}
35
36pub fn lower_schema_sdl(sdl: &str) -> Result<WesleyIR, WesleyError> {
38 ApolloLoweringAdapter::new(0).parse_and_lower(sdl)
39}
40
41pub fn diff_schema_sdl(old_sdl: &str, new_sdl: &str) -> Result<SchemaDelta, WesleyError> {
43 let adapter = ApolloLoweringAdapter::new(0);
44 let old_ir = adapter.parse_and_lower(old_sdl)?;
45 let new_ir = adapter.parse_and_lower(new_sdl)?;
46
47 Ok(diff_schema_ir(&old_ir, &new_ir))
48}
49
50pub fn list_schema_operations_sdl(schema_sdl: &str) -> Result<Vec<SchemaOperation>, WesleyError> {
52 let parser = Parser::new(schema_sdl);
53 let cst = parser.parse();
54
55 let errors = cst.errors().collect::<Vec<_>>();
56 if !errors.is_empty() {
57 let err = &errors[0];
58 return Err(WesleyError::ParseError {
59 message: err.message().to_string(),
60 line: None,
61 column: None,
62 });
63 }
64
65 let doc = cst.document();
66 let mut root_types = RootTypes::default();
67 for def in doc.definitions() {
68 match def {
69 cst::Definition::SchemaDefinition(schema) => {
70 update_root_types(schema.root_operation_type_definitions(), &mut root_types)?;
71 }
72 cst::Definition::SchemaExtension(schema) => {
73 update_root_types(schema.root_operation_type_definitions(), &mut root_types)?;
74 }
75 _ => {}
76 }
77 }
78
79 let mut operations = Vec::new();
80 for def in doc.definitions() {
81 match def {
82 cst::Definition::ObjectTypeDefinition(node) => {
83 collect_schema_operations_from_object(
84 node.name(),
85 node.fields_definition(),
86 &root_types,
87 &mut operations,
88 )?;
89 }
90 cst::Definition::ObjectTypeExtension(node) => {
91 collect_schema_operations_from_object(
92 node.name(),
93 node.fields_definition(),
94 &root_types,
95 &mut operations,
96 )?;
97 }
98 _ => {}
99 }
100 }
101
102 Ok(operations)
103}
104
105struct TypeAggregate {
107 name: String,
108 kind: TypeKind,
109 definitions: Vec<TypeDefinitionNode>,
110 extensions: Vec<TypeExtensionNode>,
111}
112
113enum TypeDefinitionNode {
114 Scalar(cst::ScalarTypeDefinition),
115 Object(cst::ObjectTypeDefinition),
116 Interface(cst::InterfaceTypeDefinition),
117 Union(cst::UnionTypeDefinition),
118 Enum(cst::EnumTypeDefinition),
119 InputObject(cst::InputObjectTypeDefinition),
120}
121
122impl TypeDefinitionNode {
123 fn name(&self) -> Option<cst::Name> {
124 match self {
125 TypeDefinitionNode::Scalar(node) => node.name(),
126 TypeDefinitionNode::Object(node) => node.name(),
127 TypeDefinitionNode::Interface(node) => node.name(),
128 TypeDefinitionNode::Union(node) => node.name(),
129 TypeDefinitionNode::Enum(node) => node.name(),
130 TypeDefinitionNode::InputObject(node) => node.name(),
131 }
132 }
133
134 fn description(&self) -> Option<cst::Description> {
135 match self {
136 TypeDefinitionNode::Scalar(node) => node.description(),
137 TypeDefinitionNode::Object(node) => node.description(),
138 TypeDefinitionNode::Interface(node) => node.description(),
139 TypeDefinitionNode::Union(node) => node.description(),
140 TypeDefinitionNode::Enum(node) => node.description(),
141 TypeDefinitionNode::InputObject(node) => node.description(),
142 }
143 }
144}
145
146enum TypeExtensionNode {
147 Scalar(cst::ScalarTypeExtension),
148 Object(cst::ObjectTypeExtension),
149 Interface(cst::InterfaceTypeExtension),
150 Union(cst::UnionTypeExtension),
151 Enum(cst::EnumTypeExtension),
152 InputObject(cst::InputObjectTypeExtension),
153}
154
155impl TypeExtensionNode {
156 fn name(&self) -> Option<cst::Name> {
157 match self {
158 TypeExtensionNode::Scalar(node) => node.name(),
159 TypeExtensionNode::Object(node) => node.name(),
160 TypeExtensionNode::Interface(node) => node.name(),
161 TypeExtensionNode::Union(node) => node.name(),
162 TypeExtensionNode::Enum(node) => node.name(),
163 TypeExtensionNode::InputObject(node) => node.name(),
164 }
165 }
166}
167
168impl ApolloLoweringAdapter {
169 fn parse_and_lower(&self, sdl: &str) -> Result<WesleyIR, WesleyError> {
170 let parser = Parser::new(sdl);
171 let cst = parser.parse();
172
173 let errors = cst.errors().collect::<Vec<_>>();
174 if !errors.is_empty() {
175 let err = &errors[0];
176 return Err(WesleyError::ParseError {
177 message: err.message().to_string(),
178 line: None,
179 column: None,
180 });
181 }
182
183 let doc = cst.document();
184 let mut aggregates: BTreeMap<String, TypeAggregate> = BTreeMap::new();
185
186 for def in doc.definitions() {
187 match def {
188 cst::Definition::ScalarTypeDefinition(node) => self.aggregate_definition(
189 TypeDefinitionNode::Scalar(node),
190 TypeKind::Scalar,
191 &mut aggregates,
192 )?,
193 cst::Definition::ObjectTypeDefinition(node) => self.aggregate_definition(
194 TypeDefinitionNode::Object(node),
195 TypeKind::Object,
196 &mut aggregates,
197 )?,
198 cst::Definition::InterfaceTypeDefinition(node) => self.aggregate_definition(
199 TypeDefinitionNode::Interface(node),
200 TypeKind::Interface,
201 &mut aggregates,
202 )?,
203 cst::Definition::UnionTypeDefinition(node) => self.aggregate_definition(
204 TypeDefinitionNode::Union(node),
205 TypeKind::Union,
206 &mut aggregates,
207 )?,
208 cst::Definition::EnumTypeDefinition(node) => self.aggregate_definition(
209 TypeDefinitionNode::Enum(node),
210 TypeKind::Enum,
211 &mut aggregates,
212 )?,
213 cst::Definition::InputObjectTypeDefinition(node) => self.aggregate_definition(
214 TypeDefinitionNode::InputObject(node),
215 TypeKind::InputObject,
216 &mut aggregates,
217 )?,
218 cst::Definition::ScalarTypeExtension(node) => self.aggregate_extension(
219 TypeExtensionNode::Scalar(node),
220 TypeKind::Scalar,
221 &mut aggregates,
222 )?,
223 cst::Definition::ObjectTypeExtension(node) => self.aggregate_extension(
224 TypeExtensionNode::Object(node),
225 TypeKind::Object,
226 &mut aggregates,
227 )?,
228 cst::Definition::InterfaceTypeExtension(node) => self.aggregate_extension(
229 TypeExtensionNode::Interface(node),
230 TypeKind::Interface,
231 &mut aggregates,
232 )?,
233 cst::Definition::UnionTypeExtension(node) => self.aggregate_extension(
234 TypeExtensionNode::Union(node),
235 TypeKind::Union,
236 &mut aggregates,
237 )?,
238 cst::Definition::EnumTypeExtension(node) => self.aggregate_extension(
239 TypeExtensionNode::Enum(node),
240 TypeKind::Enum,
241 &mut aggregates,
242 )?,
243 cst::Definition::InputObjectTypeExtension(node) => self.aggregate_extension(
244 TypeExtensionNode::InputObject(node),
245 TypeKind::InputObject,
246 &mut aggregates,
247 )?,
248 _ => {}
249 }
250 }
251
252 let mut types = Vec::new();
253 for agg in aggregates.values() {
254 types.push(self.build_type_from_aggregate(agg)?);
255 }
256
257 Ok(WesleyIR {
258 version: "1.0.0".to_string(),
259 metadata: None,
260 types,
261 })
262 }
263
264 fn aggregate_definition(
265 &self,
266 node: TypeDefinitionNode,
267 kind: TypeKind,
268 aggregates: &mut BTreeMap<String, TypeAggregate>,
269 ) -> Result<(), WesleyError> {
270 let name = type_node_name(node.name(), "Type definition missing name")?;
271 let agg = aggregate_for(aggregates, name, kind)?;
272 agg.definitions.push(node);
273 Ok(())
274 }
275
276 fn aggregate_extension(
277 &self,
278 node: TypeExtensionNode,
279 kind: TypeKind,
280 aggregates: &mut BTreeMap<String, TypeAggregate>,
281 ) -> Result<(), WesleyError> {
282 let name = type_node_name(node.name(), "Type extension missing name")?;
283 let agg = aggregate_for(aggregates, name, kind)?;
284 agg.extensions.push(node);
285 Ok(())
286 }
287
288 fn build_type_from_aggregate(
289 &self,
290 agg: &TypeAggregate,
291 ) -> Result<TypeDefinition, WesleyError> {
292 let mut directives = IndexMap::new();
293 let mut implements = Vec::new();
294 let mut fields = Vec::new();
295 let mut enum_values = Vec::new();
296 let mut union_members = Vec::new();
297 let mut description = None;
298
299 for def in &agg.definitions {
300 if description.is_none() {
301 description = description_from(def.description());
302 }
303 self.merge_definition(
304 def,
305 &mut directives,
306 &mut implements,
307 &mut fields,
308 &mut enum_values,
309 &mut union_members,
310 )?;
311 }
312
313 for ext in &agg.extensions {
314 self.merge_extension(
315 ext,
316 &mut directives,
317 &mut implements,
318 &mut fields,
319 &mut enum_values,
320 &mut union_members,
321 )?;
322 }
323
324 Ok(TypeDefinition {
325 name: agg.name.clone(),
326 kind: agg.kind,
327 description,
328 directives,
329 implements,
330 fields,
331 enum_values,
332 union_members,
333 })
334 }
335
336 fn merge_definition(
337 &self,
338 def: &TypeDefinitionNode,
339 directives: &mut IndexMap<String, serde_json::Value>,
340 implements: &mut Vec<String>,
341 fields: &mut Vec<Field>,
342 enum_values: &mut Vec<String>,
343 union_members: &mut Vec<String>,
344 ) -> Result<(), WesleyError> {
345 match def {
346 TypeDefinitionNode::Scalar(node) => {
347 if let Some(dirs) = node.directives() {
348 self.extract_directives(dirs, directives)?;
349 }
350 }
351 TypeDefinitionNode::Object(node) => {
352 if let Some(interfaces) = node.implements_interfaces() {
353 collect_implements(interfaces, implements)?;
354 }
355 if let Some(dirs) = node.directives() {
356 self.extract_directives(dirs, directives)?;
357 }
358 if let Some(fields_def) = node.fields_definition() {
359 self.collect_fields(fields_def, fields)?;
360 }
361 }
362 TypeDefinitionNode::Interface(node) => {
363 if let Some(interfaces) = node.implements_interfaces() {
364 collect_implements(interfaces, implements)?;
365 }
366 if let Some(dirs) = node.directives() {
367 self.extract_directives(dirs, directives)?;
368 }
369 if let Some(fields_def) = node.fields_definition() {
370 self.collect_fields(fields_def, fields)?;
371 }
372 }
373 TypeDefinitionNode::Union(node) => {
374 if let Some(dirs) = node.directives() {
375 self.extract_directives(dirs, directives)?;
376 }
377 if let Some(member_types) = node.union_member_types() {
378 collect_union_members(member_types, union_members)?;
379 }
380 }
381 TypeDefinitionNode::Enum(node) => {
382 if let Some(dirs) = node.directives() {
383 self.extract_directives(dirs, directives)?;
384 }
385 if let Some(values_def) = node.enum_values_definition() {
386 collect_enum_values(values_def, enum_values)?;
387 }
388 }
389 TypeDefinitionNode::InputObject(node) => {
390 if let Some(dirs) = node.directives() {
391 self.extract_directives(dirs, directives)?;
392 }
393 if let Some(fields_def) = node.input_fields_definition() {
394 self.collect_input_fields(fields_def, fields)?;
395 }
396 }
397 }
398
399 Ok(())
400 }
401
402 fn merge_extension(
403 &self,
404 ext: &TypeExtensionNode,
405 directives: &mut IndexMap<String, serde_json::Value>,
406 implements: &mut Vec<String>,
407 fields: &mut Vec<Field>,
408 enum_values: &mut Vec<String>,
409 union_members: &mut Vec<String>,
410 ) -> Result<(), WesleyError> {
411 match ext {
412 TypeExtensionNode::Scalar(node) => {
413 if let Some(dirs) = node.directives() {
414 self.extract_directives(dirs, directives)?;
415 }
416 }
417 TypeExtensionNode::Object(node) => {
418 if let Some(interfaces) = node.implements_interfaces() {
419 collect_implements(interfaces, implements)?;
420 }
421 if let Some(dirs) = node.directives() {
422 self.extract_directives(dirs, directives)?;
423 }
424 if let Some(fields_def) = node.fields_definition() {
425 self.collect_fields(fields_def, fields)?;
426 }
427 }
428 TypeExtensionNode::Interface(node) => {
429 if let Some(interfaces) = node.implements_interfaces() {
430 collect_implements(interfaces, implements)?;
431 }
432 if let Some(dirs) = node.directives() {
433 self.extract_directives(dirs, directives)?;
434 }
435 if let Some(fields_def) = node.fields_definition() {
436 self.collect_fields(fields_def, fields)?;
437 }
438 }
439 TypeExtensionNode::Union(node) => {
440 if let Some(dirs) = node.directives() {
441 self.extract_directives(dirs, directives)?;
442 }
443 if let Some(member_types) = node.union_member_types() {
444 collect_union_members(member_types, union_members)?;
445 }
446 }
447 TypeExtensionNode::Enum(node) => {
448 if let Some(dirs) = node.directives() {
449 self.extract_directives(dirs, directives)?;
450 }
451 if let Some(values_def) = node.enum_values_definition() {
452 collect_enum_values(values_def, enum_values)?;
453 }
454 }
455 TypeExtensionNode::InputObject(node) => {
456 if let Some(dirs) = node.directives() {
457 self.extract_directives(dirs, directives)?;
458 }
459 if let Some(fields_def) = node.input_fields_definition() {
460 self.collect_input_fields(fields_def, fields)?;
461 }
462 }
463 }
464
465 Ok(())
466 }
467
468 fn extract_directives(
469 &self,
470 dirs: cst::Directives,
471 map: &mut IndexMap<String, serde_json::Value>,
472 ) -> Result<(), WesleyError> {
473 for dir in dirs.directives() {
474 let dir_name = dir
475 .name()
476 .ok_or(WesleyError::LoweringError {
477 message: "Directive missing name".to_string(),
478 area: "directive".to_string(),
479 })?
480 .text()
481 .to_string();
482
483 let mut args_map = serde_json::Map::new();
484 if let Some(args) = dir.arguments() {
485 for arg in args.arguments() {
486 let arg_name = arg.name().map(|n| n.text().to_string()).unwrap_or_default();
487 if let Some(val) = arg.value() {
488 args_map.insert(arg_name, directive_value_to_json(val)?);
489 }
490 }
491 }
492
493 let val = if args_map.is_empty() {
494 serde_json::Value::Bool(true)
495 } else {
496 serde_json::Value::Object(args_map)
497 };
498
499 map.insert(dir_name, val);
500 }
501 Ok(())
502 }
503
504 fn collect_fields(
505 &self,
506 fields_def: cst::FieldsDefinition,
507 fields: &mut Vec<Field>,
508 ) -> Result<(), WesleyError> {
509 for field_def in fields_def.field_definitions() {
510 fields.push(self.build_field(field_def)?);
511 }
512
513 Ok(())
514 }
515
516 fn collect_input_fields(
517 &self,
518 fields_def: cst::InputFieldsDefinition,
519 fields: &mut Vec<Field>,
520 ) -> Result<(), WesleyError> {
521 for field_def in fields_def.input_value_definitions() {
522 fields.push(self.build_input_field(field_def)?);
523 }
524
525 Ok(())
526 }
527
528 fn build_field(&self, field_def: cst::FieldDefinition) -> Result<Field, WesleyError> {
529 let name = field_def
530 .name()
531 .ok_or(WesleyError::LoweringError {
532 message: "Field missing name".to_string(),
533 area: "field".to_string(),
534 })?
535 .text()
536 .to_string();
537
538 let type_node = field_def.ty().ok_or(WesleyError::LoweringError {
539 message: "Field missing type".to_string(),
540 area: "field".to_string(),
541 })?;
542
543 let mut field_directives = IndexMap::new();
544 if let Some(dirs) = field_def.directives() {
545 self.extract_directives(dirs, &mut field_directives)?;
546 }
547
548 Ok(Field {
549 name,
550 r#type: self.build_type_reference(type_node)?,
551 arguments: field_arguments_from_definition(field_def.arguments_definition())?,
552 directives: field_directives,
553 description: description_from(field_def.description()),
554 })
555 }
556
557 fn build_input_field(
558 &self,
559 field_def: cst::InputValueDefinition,
560 ) -> Result<Field, WesleyError> {
561 let name = field_def
562 .name()
563 .ok_or(WesleyError::LoweringError {
564 message: "Input field missing name".to_string(),
565 area: "field".to_string(),
566 })?
567 .text()
568 .to_string();
569
570 let type_node = field_def.ty().ok_or(WesleyError::LoweringError {
571 message: "Input field missing type".to_string(),
572 area: "field".to_string(),
573 })?;
574
575 let mut field_directives = IndexMap::new();
576 if let Some(dirs) = field_def.directives() {
577 self.extract_directives(dirs, &mut field_directives)?;
578 }
579
580 Ok(Field {
581 name,
582 r#type: self.build_type_reference(type_node)?,
583 arguments: Vec::new(),
584 directives: field_directives,
585 description: description_from(field_def.description()),
586 })
587 }
588
589 fn build_type_reference(&self, type_node: cst::Type) -> Result<TypeReference, WesleyError> {
590 type_reference_from_type(type_node, true)
591 }
592}
593
594fn aggregate_for(
595 aggregates: &mut BTreeMap<String, TypeAggregate>,
596 name: String,
597 kind: TypeKind,
598) -> Result<&mut TypeAggregate, WesleyError> {
599 use std::collections::btree_map::Entry;
600
601 match aggregates.entry(name.clone()) {
602 Entry::Vacant(entry) => Ok(entry.insert(TypeAggregate {
603 name,
604 kind,
605 definitions: Vec::new(),
606 extensions: Vec::new(),
607 })),
608 Entry::Occupied(entry) => {
609 let aggregate = entry.into_mut();
610 if aggregate.kind != kind {
611 return Err(lowering_error_value(
612 "type",
613 format!(
614 "Type '{}' is declared as both {:?} and {:?}",
615 aggregate.name, aggregate.kind, kind
616 ),
617 ));
618 }
619 Ok(aggregate)
620 }
621 }
622}
623
624fn type_node_name(name: Option<cst::Name>, message: &str) -> Result<String, WesleyError> {
625 name.map(|name| name.text().to_string())
626 .ok_or_else(|| lowering_error_value("type", message.to_string()))
627}
628
629fn description_from(description: Option<cst::Description>) -> Option<String> {
630 description
631 .and_then(|description| description.string_value())
632 .map(String::from)
633}
634
635fn collect_implements(
636 interfaces: cst::ImplementsInterfaces,
637 implements: &mut Vec<String>,
638) -> Result<(), WesleyError> {
639 for named_type in interfaces.named_types() {
640 let name = named_type_name_for_lowering(named_type, "Implemented interface missing name")?;
641 push_unique(implements, name);
642 }
643
644 Ok(())
645}
646
647fn collect_union_members(
648 member_types: cst::UnionMemberTypes,
649 union_members: &mut Vec<String>,
650) -> Result<(), WesleyError> {
651 for named_type in member_types.named_types() {
652 let name = named_type_name_for_lowering(named_type, "Union member missing name")?;
653 push_unique(union_members, name);
654 }
655
656 Ok(())
657}
658
659fn collect_enum_values(
660 values_def: cst::EnumValuesDefinition,
661 enum_values: &mut Vec<String>,
662) -> Result<(), WesleyError> {
663 for value_def in values_def.enum_value_definitions() {
664 let name = value_def
665 .enum_value()
666 .and_then(|enum_value| enum_value.name())
667 .map(|name| name.text().to_string())
668 .ok_or_else(|| lowering_error_value("enum", "Enum value missing name".to_string()))?;
669 push_unique(enum_values, name);
670 }
671
672 Ok(())
673}
674
675fn named_type_name_for_lowering(
676 named_type: cst::NamedType,
677 message: &str,
678) -> Result<String, WesleyError> {
679 named_type
680 .name()
681 .map(|name| name.text().to_string())
682 .ok_or_else(|| lowering_error_value("type", message.to_string()))
683}
684
685#[derive(Debug)]
686struct TypeReferenceShape {
687 base: String,
688 nullable: bool,
689 list_wrappers: Vec<TypeListWrapper>,
690 leaf_nullable: bool,
691}
692
693fn type_reference_from_type(
694 type_node: cst::Type,
695 nullable: bool,
696) -> Result<TypeReference, WesleyError> {
697 let shape = type_reference_shape_from_type(type_node, nullable)?;
698 let is_list = !shape.list_wrappers.is_empty();
699 let list_item_nullable = if is_list {
700 Some(
701 shape
702 .list_wrappers
703 .get(1)
704 .map(|wrapper| wrapper.nullable)
705 .unwrap_or(shape.leaf_nullable),
706 )
707 } else {
708 None
709 };
710 let has_nested_lists = shape.list_wrappers.len() > 1;
711
712 Ok(TypeReference {
713 base: shape.base,
714 nullable: shape.nullable,
715 is_list,
716 list_item_nullable,
717 list_wrappers: if has_nested_lists {
718 shape.list_wrappers
719 } else {
720 Vec::new()
721 },
722 leaf_nullable: if has_nested_lists {
723 Some(shape.leaf_nullable)
724 } else {
725 None
726 },
727 })
728}
729
730fn type_reference_shape_from_type(
731 type_node: cst::Type,
732 nullable: bool,
733) -> Result<TypeReferenceShape, WesleyError> {
734 match type_node {
735 cst::Type::NamedType(named_type) => Ok(TypeReferenceShape {
736 base: named_type_name_for_lowering(named_type, "Type reference missing name")?,
737 nullable,
738 list_wrappers: Vec::new(),
739 leaf_nullable: nullable,
740 }),
741 cst::Type::ListType(list_type) => {
742 let item_type = list_type.ty().ok_or_else(|| {
743 lowering_error_value("type", "List type missing item type".to_string())
744 })?;
745 let item_ref = type_reference_shape_from_type(item_type, true)?;
746 let mut list_wrappers = vec![TypeListWrapper { nullable }];
747 list_wrappers.extend(item_ref.list_wrappers);
748
749 Ok(TypeReferenceShape {
750 base: item_ref.base,
751 nullable,
752 list_wrappers,
753 leaf_nullable: item_ref.leaf_nullable,
754 })
755 }
756 cst::Type::NonNullType(non_null_type) => {
757 if let Some(named_type) = non_null_type.named_type() {
758 Ok(TypeReferenceShape {
759 base: named_type_name_for_lowering(
760 named_type,
761 "Non-null type reference missing name",
762 )?,
763 nullable: false,
764 list_wrappers: Vec::new(),
765 leaf_nullable: false,
766 })
767 } else if let Some(list_type) = non_null_type.list_type() {
768 let item_type = list_type.ty().ok_or_else(|| {
769 lowering_error_value("type", "Non-null list type missing item type".to_string())
770 })?;
771 let item_ref = type_reference_shape_from_type(item_type, true)?;
772 let mut list_wrappers = vec![TypeListWrapper { nullable: false }];
773 list_wrappers.extend(item_ref.list_wrappers);
774
775 Ok(TypeReferenceShape {
776 base: item_ref.base,
777 nullable: false,
778 list_wrappers,
779 leaf_nullable: item_ref.leaf_nullable,
780 })
781 } else {
782 Err(lowering_error_value(
783 "type",
784 "Non-null type missing inner type".to_string(),
785 ))
786 }
787 }
788 }
789}
790
791fn field_arguments_from_definition(
792 arguments_definition: Option<cst::ArgumentsDefinition>,
793) -> Result<Vec<FieldArgument>, WesleyError> {
794 let Some(arguments_definition) = arguments_definition else {
795 return Ok(Vec::new());
796 };
797
798 arguments_definition
799 .input_value_definitions()
800 .map(field_argument_from_input_value)
801 .collect()
802}
803
804fn field_argument_from_input_value(
805 input_value: cst::InputValueDefinition,
806) -> Result<FieldArgument, WesleyError> {
807 let name = input_value
808 .name()
809 .map(|name| name.text().to_string())
810 .ok_or_else(|| {
811 lowering_error_value("field argument", "Field argument missing name".into())
812 })?;
813 let type_node = input_value.ty().ok_or_else(|| {
814 lowering_error_value(
815 "field argument",
816 format!("Field argument '{name}' missing type"),
817 )
818 })?;
819 let default_value = input_value
820 .default_value()
821 .and_then(|default_value| default_value.value())
822 .map(directive_value_to_json)
823 .transpose()?;
824
825 let mut directives = IndexMap::new();
826 if let Some(dirs) = input_value.directives() {
827 ApolloLoweringAdapter::new(0).extract_directives(dirs, &mut directives)?;
828 }
829
830 Ok(FieldArgument {
831 name,
832 description: description_from(input_value.description()),
833 r#type: type_reference_from_type(type_node, true)?,
834 default_value,
835 directives,
836 })
837}
838
839fn directive_value_to_json(value: cst::Value) -> Result<serde_json::Value, WesleyError> {
840 match value {
841 cst::Value::StringValue(value) => Ok(serde_json::Value::String(String::from(value))),
842 cst::Value::FloatValue(value) => {
843 let raw = value
844 .float_token()
845 .map(|token| token.text().to_string())
846 .unwrap_or_default();
847 let parsed = raw.parse::<f64>().map_err(|err| {
848 lowering_error_value(
849 "directive",
850 format!("Invalid float directive argument '{raw}': {err}"),
851 )
852 })?;
853 serde_json::Number::from_f64(parsed)
854 .map(serde_json::Value::Number)
855 .ok_or_else(|| {
856 lowering_error_value(
857 "directive",
858 format!("Invalid finite float directive argument '{raw}'"),
859 )
860 })
861 }
862 cst::Value::IntValue(value) => {
863 let raw = value
864 .int_token()
865 .map(|token| token.text().to_string())
866 .unwrap_or_default();
867 raw.parse::<i64>()
868 .map(|parsed| serde_json::Value::Number(parsed.into()))
869 .map_err(|err| {
870 lowering_error_value(
871 "directive",
872 format!("Invalid integer directive argument '{raw}': {err}"),
873 )
874 })
875 }
876 cst::Value::BooleanValue(value) => Ok(serde_json::Value::Bool(
877 value.true_token().is_some() && value.false_token().is_none(),
878 )),
879 cst::Value::NullValue(_) => Ok(serde_json::Value::Null),
880 cst::Value::EnumValue(value) => {
881 let name = value
882 .name()
883 .map(|name| name.text().to_string())
884 .ok_or_else(|| {
885 lowering_error_value(
886 "directive",
887 "Enum directive value missing name".to_string(),
888 )
889 })?;
890 Ok(serde_json::Value::String(name))
891 }
892 cst::Value::ListValue(list) => {
893 let mut values = Vec::new();
894 for value in list.values() {
895 values.push(directive_value_to_json(value)?);
896 }
897 Ok(serde_json::Value::Array(values))
898 }
899 cst::Value::ObjectValue(object) => {
900 let mut map = serde_json::Map::new();
901 for field in object.object_fields() {
902 let name = field
903 .name()
904 .map(|name| name.text().to_string())
905 .ok_or_else(|| {
906 lowering_error_value(
907 "directive",
908 "Object directive value field missing name".to_string(),
909 )
910 })?;
911 let value = field.value().ok_or_else(|| {
912 lowering_error_value(
913 "directive",
914 format!("Object directive value field '{name}' missing value"),
915 )
916 })?;
917 map.insert(name, directive_value_to_json(value)?);
918 }
919 Ok(serde_json::Value::Object(map))
920 }
921 cst::Value::Variable(variable) => Err(lowering_error_value(
922 "directive",
923 format!(
924 "Directive argument values cannot be variables: {}",
925 variable.text()
926 ),
927 )),
928 }
929}
930
931fn lowering_error_value(area: &str, message: String) -> WesleyError {
932 WesleyError::LoweringError {
933 message,
934 area: area.to_string(),
935 }
936}
937
938pub fn resolve_operation_selections(operation_sdl: &str) -> Result<Vec<String>, WesleyError> {
940 let parsed = parse_operation_document(operation_sdl)?;
941 let op = parsed.only_operation()?;
942 let mut selections = Vec::new();
943
944 if let Some(selection_set) = op.selection_set() {
945 collect_selection_paths(
946 &selection_set,
947 "",
948 &parsed.fragments,
949 &mut Vec::new(),
950 &mut selections,
951 )?;
952 }
953
954 Ok(selections)
955}
956
957pub fn resolve_operation_selections_with_schema(
959 schema_sdl: &str,
960 operation_sdl: &str,
961) -> Result<Vec<String>, WesleyError> {
962 let adapter = ApolloLoweringAdapter::new(0);
963 let ir = adapter.parse_and_lower(schema_sdl)?;
964 let root_types = extract_root_types(schema_sdl)?;
965
966 let parsed = parse_operation_document(operation_sdl)?;
967 let op = parsed.only_operation()?;
968 let mut selections = Vec::new();
969
970 if let Some(selection_set) = op.selection_set() {
971 let root_type = root_types.root_for_operation(op)?;
972 let schema = SchemaIndex::new(&ir);
973 collect_schema_coordinates(
974 &selection_set,
975 root_type,
976 &schema,
977 &parsed.fragments,
978 &mut Vec::new(),
979 &mut selections,
980 )?;
981 }
982
983 Ok(selections)
984}
985
986pub fn extract_operation_directive_args(
988 operation_sdl: &str,
989 directive_name: &str,
990) -> Result<Vec<OperationDirectiveArgs>, WesleyError> {
991 let parsed = parse_operation_document(operation_sdl)?;
992 let op = parsed.only_operation()?;
993 let mut directives = Vec::new();
994
995 let Some(operation_directives) = op.directives() else {
996 return Ok(directives);
997 };
998
999 for directive in operation_directives.directives() {
1000 let name = required_name(directive.name(), "Directive missing name")?;
1001 if name != directive_name {
1002 continue;
1003 }
1004
1005 directives.push(OperationDirectiveArgs {
1006 directive_name: name,
1007 arguments: extract_directive_arguments(directive.arguments())?,
1008 });
1009 }
1010
1011 Ok(directives)
1012}
1013
1014fn extract_directive_arguments(
1015 arguments: Option<cst::Arguments>,
1016) -> Result<IndexMap<String, serde_json::Value>, WesleyError> {
1017 let mut values = IndexMap::new();
1018
1019 let Some(arguments) = arguments else {
1020 return Ok(values);
1021 };
1022
1023 for argument in arguments.arguments() {
1024 let name = required_name(argument.name(), "Directive argument missing name")?;
1025 let value = argument.value().ok_or_else(|| {
1026 operation_error_value(format!("Directive argument '{name}' missing value"))
1027 })?;
1028 values.insert(name, directive_value_to_json(value)?);
1029 }
1030
1031 Ok(values)
1032}
1033
1034struct ParsedOperationDocument {
1035 operations: Vec<cst::OperationDefinition>,
1036 fragments: BTreeMap<String, cst::FragmentDefinition>,
1037}
1038
1039impl ParsedOperationDocument {
1040 fn only_operation(&self) -> Result<&cst::OperationDefinition, WesleyError> {
1041 match self.operations.len() {
1042 0 => operation_error("No GraphQL operation found".to_string()),
1043 1 => Ok(&self.operations[0]),
1044 count => operation_error(format!(
1045 "Expected exactly one GraphQL operation, found {count}"
1046 )),
1047 }
1048 }
1049}
1050
1051fn parse_operation_document(operation_sdl: &str) -> Result<ParsedOperationDocument, WesleyError> {
1052 let parser = Parser::new(operation_sdl);
1053 let cst = parser.parse();
1054
1055 let errors = cst.errors().collect::<Vec<_>>();
1056 if !errors.is_empty() {
1057 let err = &errors[0];
1058 return Err(WesleyError::ParseError {
1059 message: err.message().to_string(),
1060 line: None,
1061 column: None,
1062 });
1063 }
1064
1065 let doc = cst.document();
1066 let mut operations = Vec::new();
1067 let mut fragments = BTreeMap::new();
1068
1069 for def in doc.definitions() {
1070 match def {
1071 cst::Definition::OperationDefinition(op) => {
1072 operations.push(op);
1073 }
1074 cst::Definition::FragmentDefinition(fragment) => {
1075 let name = fragment_name(&fragment)?;
1076 if fragments.insert(name.clone(), fragment).is_some() {
1077 return operation_error(format!("Duplicate fragment definition '{name}'"));
1078 }
1079 }
1080 _ => {}
1081 }
1082 }
1083
1084 Ok(ParsedOperationDocument {
1085 operations,
1086 fragments,
1087 })
1088}
1089
1090fn collect_selection_paths(
1091 selection_set: &cst::SelectionSet,
1092 prefix: &str,
1093 fragments: &BTreeMap<String, cst::FragmentDefinition>,
1094 active_fragments: &mut Vec<String>,
1095 actual_selections: &mut Vec<String>,
1096) -> Result<(), WesleyError> {
1097 for selection in selection_set.selections() {
1098 match selection {
1099 cst::Selection::Field(field) => {
1100 let field_name = required_name(field.name(), "Field selection missing name")?;
1101 let path = if prefix.is_empty() {
1102 field_name
1103 } else {
1104 format!("{prefix}.{field_name}")
1105 };
1106
1107 push_unique(actual_selections, path.clone());
1108
1109 if let Some(nested_selection_set) = field.selection_set() {
1110 collect_selection_paths(
1111 &nested_selection_set,
1112 &path,
1113 fragments,
1114 active_fragments,
1115 actual_selections,
1116 )?;
1117 }
1118 }
1119 cst::Selection::FragmentSpread(spread) => {
1120 let name = spread
1121 .fragment_name()
1122 .and_then(|fragment_name| fragment_name.name())
1123 .map(|name| name.text().to_string())
1124 .ok_or_else(|| {
1125 operation_error_value("Fragment spread missing name".to_string())
1126 })?;
1127
1128 if active_fragments.contains(&name) {
1129 return operation_error(format!(
1130 "Cyclic fragment spread detected for fragment '{name}'"
1131 ));
1132 }
1133
1134 let fragment = fragments.get(&name).ok_or_else(|| {
1135 operation_error_value(format!("Unknown fragment spread '{name}'"))
1136 })?;
1137
1138 active_fragments.push(name);
1139 if let Some(fragment_selection_set) = fragment.selection_set() {
1140 collect_selection_paths(
1141 &fragment_selection_set,
1142 prefix,
1143 fragments,
1144 active_fragments,
1145 actual_selections,
1146 )?;
1147 }
1148 active_fragments.pop();
1149 }
1150 cst::Selection::InlineFragment(fragment) => {
1151 if let Some(inline_selection_set) = fragment.selection_set() {
1152 collect_selection_paths(
1153 &inline_selection_set,
1154 prefix,
1155 fragments,
1156 active_fragments,
1157 actual_selections,
1158 )?;
1159 }
1160 }
1161 }
1162 }
1163
1164 Ok(())
1165}
1166
1167fn collect_schema_coordinates(
1168 selection_set: &cst::SelectionSet,
1169 parent_type: &str,
1170 schema: &SchemaIndex<'_>,
1171 fragments: &BTreeMap<String, cst::FragmentDefinition>,
1172 active_fragments: &mut Vec<String>,
1173 actual_selections: &mut Vec<String>,
1174) -> Result<(), WesleyError> {
1175 for selection in selection_set.selections() {
1176 match selection {
1177 cst::Selection::Field(field) => {
1178 let field_name = required_name(field.name(), "Field selection missing name")?;
1179 let schema_field = schema.field(parent_type, &field_name)?;
1180 let coordinate = format!("{parent_type}.{field_name}");
1181 push_unique(actual_selections, coordinate);
1182
1183 if let Some(nested_selection_set) = field.selection_set() {
1184 let nested_parent = schema_field.r#type.base.as_str();
1185 schema.require_type(nested_parent)?;
1186 collect_schema_coordinates(
1187 &nested_selection_set,
1188 nested_parent,
1189 schema,
1190 fragments,
1191 active_fragments,
1192 actual_selections,
1193 )?;
1194 }
1195 }
1196 cst::Selection::FragmentSpread(spread) => {
1197 let name = spread
1198 .fragment_name()
1199 .and_then(|fragment_name| fragment_name.name())
1200 .map(|name| name.text().to_string())
1201 .ok_or_else(|| {
1202 operation_error_value("Fragment spread missing name".to_string())
1203 })?;
1204
1205 if active_fragments.contains(&name) {
1206 return operation_error(format!(
1207 "Cyclic fragment spread detected for fragment '{name}'"
1208 ));
1209 }
1210
1211 let fragment = fragments.get(&name).ok_or_else(|| {
1212 operation_error_value(format!("Unknown fragment spread '{name}'"))
1213 })?;
1214 let fragment_parent = fragment_type_condition(fragment)?;
1215 schema.require_type(&fragment_parent)?;
1216
1217 active_fragments.push(name);
1218 if let Some(fragment_selection_set) = fragment.selection_set() {
1219 collect_schema_coordinates(
1220 &fragment_selection_set,
1221 &fragment_parent,
1222 schema,
1223 fragments,
1224 active_fragments,
1225 actual_selections,
1226 )?;
1227 }
1228 active_fragments.pop();
1229 }
1230 cst::Selection::InlineFragment(fragment) => {
1231 let inline_parent = if let Some(type_condition) = fragment.type_condition() {
1232 named_type_name(type_condition.named_type(), "Inline fragment missing type")?
1233 } else {
1234 parent_type.to_string()
1235 };
1236 schema.require_type(&inline_parent)?;
1237
1238 if let Some(inline_selection_set) = fragment.selection_set() {
1239 collect_schema_coordinates(
1240 &inline_selection_set,
1241 &inline_parent,
1242 schema,
1243 fragments,
1244 active_fragments,
1245 actual_selections,
1246 )?;
1247 }
1248 }
1249 }
1250 }
1251
1252 Ok(())
1253}
1254
1255struct SchemaIndex<'a> {
1256 types: HashMap<&'a str, &'a TypeDefinition>,
1257}
1258
1259impl<'a> SchemaIndex<'a> {
1260 fn new(ir: &'a WesleyIR) -> Self {
1261 let types = ir
1262 .types
1263 .iter()
1264 .map(|type_def| (type_def.name.as_str(), type_def))
1265 .collect::<HashMap<_, _>>();
1266 Self { types }
1267 }
1268
1269 fn require_type(&self, name: &str) -> Result<&'a TypeDefinition, WesleyError> {
1270 self.types
1271 .get(name)
1272 .copied()
1273 .ok_or_else(|| operation_error_value(format!("Unknown selection parent type '{name}'")))
1274 }
1275
1276 fn field(&self, parent_type: &str, field_name: &str) -> Result<&'a Field, WesleyError> {
1277 let type_def = self.require_type(parent_type)?;
1278 type_def
1279 .fields
1280 .iter()
1281 .find(|field| field.name == field_name)
1282 .ok_or_else(|| {
1283 operation_error_value(format!(
1284 "Type '{parent_type}' does not define selected field '{field_name}'"
1285 ))
1286 })
1287 }
1288}
1289
1290struct RootTypes {
1291 query: String,
1292 mutation: String,
1293 subscription: String,
1294}
1295
1296impl Default for RootTypes {
1297 fn default() -> Self {
1298 Self {
1299 query: "Query".to_string(),
1300 mutation: "Mutation".to_string(),
1301 subscription: "Subscription".to_string(),
1302 }
1303 }
1304}
1305
1306impl RootTypes {
1307 fn operation_types_for_type(&self, type_name: &str) -> Vec<OperationType> {
1308 let mut operation_types = Vec::new();
1309
1310 if self.query == type_name {
1311 operation_types.push(OperationType::Query);
1312 }
1313 if self.mutation == type_name {
1314 operation_types.push(OperationType::Mutation);
1315 }
1316 if self.subscription == type_name {
1317 operation_types.push(OperationType::Subscription);
1318 }
1319
1320 operation_types
1321 }
1322
1323 fn root_for_operation(&self, op: &cst::OperationDefinition) -> Result<&str, WesleyError> {
1324 let Some(operation_type) = op.operation_type() else {
1325 return Ok(self.query.as_str());
1326 };
1327
1328 if operation_type.query_token().is_some() {
1329 Ok(self.query.as_str())
1330 } else if operation_type.mutation_token().is_some() {
1331 Ok(self.mutation.as_str())
1332 } else if operation_type.subscription_token().is_some() {
1333 Ok(self.subscription.as_str())
1334 } else {
1335 operation_error("Unknown GraphQL operation type".to_string())
1336 }
1337 }
1338}
1339
1340fn extract_root_types(schema_sdl: &str) -> Result<RootTypes, WesleyError> {
1341 let parser = Parser::new(schema_sdl);
1342 let cst = parser.parse();
1343
1344 let errors = cst.errors().collect::<Vec<_>>();
1345 if !errors.is_empty() {
1346 let err = &errors[0];
1347 return Err(WesleyError::ParseError {
1348 message: err.message().to_string(),
1349 line: None,
1350 column: None,
1351 });
1352 }
1353
1354 let mut root_types = RootTypes::default();
1355
1356 for def in cst.document().definitions() {
1357 match def {
1358 cst::Definition::SchemaDefinition(schema) => {
1359 update_root_types(schema.root_operation_type_definitions(), &mut root_types)?;
1360 }
1361 cst::Definition::SchemaExtension(schema) => {
1362 update_root_types(schema.root_operation_type_definitions(), &mut root_types)?;
1363 }
1364 _ => {}
1365 }
1366 }
1367
1368 Ok(root_types)
1369}
1370
1371fn collect_schema_operations_from_object(
1372 name: Option<cst::Name>,
1373 fields_definition: Option<cst::FieldsDefinition>,
1374 root_types: &RootTypes,
1375 operations: &mut Vec<SchemaOperation>,
1376) -> Result<(), WesleyError> {
1377 let type_name = type_node_name(name, "Object type missing name")?;
1378 let operation_types = root_types.operation_types_for_type(&type_name);
1379 if operation_types.is_empty() {
1380 return Ok(());
1381 }
1382
1383 let Some(fields_definition) = fields_definition else {
1384 return Ok(());
1385 };
1386
1387 for field_def in fields_definition.field_definitions() {
1388 for operation_type in &operation_types {
1389 operations.push(schema_operation_from_field(
1390 *operation_type,
1391 &type_name,
1392 field_def.clone(),
1393 )?);
1394 }
1395 }
1396
1397 Ok(())
1398}
1399
1400fn schema_operation_from_field(
1401 operation_type: OperationType,
1402 root_type_name: &str,
1403 field_def: cst::FieldDefinition,
1404) -> Result<SchemaOperation, WesleyError> {
1405 let field_name = field_def
1406 .name()
1407 .map(|name| name.text().to_string())
1408 .ok_or_else(|| {
1409 lowering_error_value("schema operation", "Root field missing name".into())
1410 })?;
1411 let result_type = field_def.ty().ok_or_else(|| {
1412 lowering_error_value(
1413 "schema operation",
1414 format!("Root field '{field_name}' missing result type"),
1415 )
1416 })?;
1417
1418 let mut directives = IndexMap::new();
1419 if let Some(dirs) = field_def.directives() {
1420 ApolloLoweringAdapter::new(0).extract_directives(dirs, &mut directives)?;
1421 }
1422
1423 Ok(SchemaOperation {
1424 operation_type,
1425 root_type_name: root_type_name.to_string(),
1426 field_name,
1427 arguments: operation_arguments_from_definition(field_def.arguments_definition())?,
1428 result_type: type_reference_from_type(result_type, true)?,
1429 directives,
1430 })
1431}
1432
1433fn operation_arguments_from_definition(
1434 arguments_definition: Option<cst::ArgumentsDefinition>,
1435) -> Result<Vec<OperationArgument>, WesleyError> {
1436 let Some(arguments_definition) = arguments_definition else {
1437 return Ok(Vec::new());
1438 };
1439
1440 arguments_definition
1441 .input_value_definitions()
1442 .map(operation_argument_from_input_value)
1443 .collect()
1444}
1445
1446fn operation_argument_from_input_value(
1447 input_value: cst::InputValueDefinition,
1448) -> Result<OperationArgument, WesleyError> {
1449 let name = input_value
1450 .name()
1451 .map(|name| name.text().to_string())
1452 .ok_or_else(|| {
1453 lowering_error_value("schema operation", "Operation argument missing name".into())
1454 })?;
1455 let type_node = input_value.ty().ok_or_else(|| {
1456 lowering_error_value(
1457 "schema operation",
1458 format!("Operation argument '{name}' missing type"),
1459 )
1460 })?;
1461 let default_value = input_value
1462 .default_value()
1463 .and_then(|default_value| default_value.value())
1464 .map(directive_value_to_json)
1465 .transpose()?;
1466
1467 let mut directives = IndexMap::new();
1468 if let Some(dirs) = input_value.directives() {
1469 ApolloLoweringAdapter::new(0).extract_directives(dirs, &mut directives)?;
1470 }
1471
1472 Ok(OperationArgument {
1473 name,
1474 r#type: type_reference_from_type(type_node, true)?,
1475 default_value,
1476 directives,
1477 })
1478}
1479
1480fn update_root_types(
1481 root_defs: cst::CstChildren<cst::RootOperationTypeDefinition>,
1482 root_types: &mut RootTypes,
1483) -> Result<(), WesleyError> {
1484 for root_def in root_defs {
1485 let operation_type = root_def.operation_type().ok_or_else(|| {
1486 operation_error_value("Schema root operation missing operation type".to_string())
1487 })?;
1488 let named_type = named_type_name(
1489 root_def.named_type(),
1490 "Schema root operation missing named type",
1491 )?;
1492
1493 if operation_type.query_token().is_some() {
1494 root_types.query = named_type;
1495 } else if operation_type.mutation_token().is_some() {
1496 root_types.mutation = named_type;
1497 } else if operation_type.subscription_token().is_some() {
1498 root_types.subscription = named_type;
1499 }
1500 }
1501
1502 Ok(())
1503}
1504
1505fn push_unique(values: &mut Vec<String>, value: String) {
1506 if !values.contains(&value) {
1507 values.push(value);
1508 }
1509}
1510
1511fn fragment_name(fragment: &cst::FragmentDefinition) -> Result<String, WesleyError> {
1512 fragment
1513 .fragment_name()
1514 .and_then(|fragment_name| fragment_name.name())
1515 .map(|name| name.text().to_string())
1516 .ok_or_else(|| operation_error_value("Fragment definition missing name".to_string()))
1517}
1518
1519fn fragment_type_condition(fragment: &cst::FragmentDefinition) -> Result<String, WesleyError> {
1520 let type_condition = fragment.type_condition().ok_or_else(|| {
1521 operation_error_value("Fragment definition missing type condition".to_string())
1522 })?;
1523 named_type_name(
1524 type_condition.named_type(),
1525 "Fragment definition missing type condition",
1526 )
1527}
1528
1529fn named_type_name(name: Option<cst::NamedType>, message: &str) -> Result<String, WesleyError> {
1530 name.and_then(|named_type| named_type.name())
1531 .map(|name| name.text().to_string())
1532 .ok_or_else(|| operation_error_value(message.to_string()))
1533}
1534
1535fn required_name(name: Option<cst::Name>, message: &str) -> Result<String, WesleyError> {
1536 name.map(|name| name.text().to_string())
1537 .ok_or_else(|| operation_error_value(message.to_string()))
1538}
1539
1540fn operation_error<T>(message: String) -> Result<T, WesleyError> {
1541 Err(operation_error_value(message))
1542}
1543
1544fn operation_error_value(message: String) -> WesleyError {
1545 WesleyError::LoweringError {
1546 message,
1547 area: "operation".to_string(),
1548 }
1549}