libgraphql_core/operation/
fragment_builder.rs1use crate::operation::{Fragment, FragmentRegistry, Selection, SelectionSetBuildError, SelectionSetBuilder};
2use crate::types::{GraphQLType, GraphQLTypeKind, NamedGraphQLTypeRef};
3use crate::{ast, loc, DirectiveAnnotation, DirectiveAnnotationBuilder};
4use crate::schema::Schema;
5use std::path::Path;
6use thiserror::Error;
7
8type Result<T> = std::result::Result<T, FragmentBuildError>;
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct FragmentBuilder<'schema: 'fragreg, 'fragreg> {
12 def_location: loc::SourceLocation,
13 directives: Vec<DirectiveAnnotation>,
14 name: Option<String>,
15 fragment_registry: &'fragreg FragmentRegistry<'schema>,
16 schema: &'schema Schema,
17 selection_set_builder: SelectionSetBuilder<'schema, 'fragreg>,
18 type_condition_ref: Option<NamedGraphQLTypeRef>,
19}
20
21impl<'schema: 'fragreg, 'fragreg> FragmentBuilder<'schema, 'fragreg> {
22 pub fn add_directive(
25 mut self,
26 annot: DirectiveAnnotation,
27 ) -> Result<Self> {
28 self.directives.push(annot);
30 Ok(self)
31 }
32
33 pub fn add_selection(
34 mut self,
35 selection: Selection<'schema>,
36 ) -> Result<Self> {
37 self.selection_set_builder =
38 self.selection_set_builder
39 .add_selection(selection)?;
40
41 Ok(self)
42 }
43
44 pub fn build(self) -> Result<Fragment<'schema>> {
45 let fragment_name = self.name.ok_or(
51 FragmentBuildError::NoFragmentNameSpecified {
52 fragment_def_src_location: self.def_location.to_owned(),
53 }
54 )?;
55
56 let type_condition_ref = self.type_condition_ref.ok_or(
57 FragmentBuildError::NoTypeConditionSpecified {
58 fragment_name: fragment_name.to_owned(),
59 fragment_src_location: self.def_location.to_owned(),
60 }
61 )?;
62
63 Ok(Fragment {
64 directives: self.directives,
65 def_location: self.def_location,
66 name: fragment_name,
67 schema: self.schema,
68 selection_set: self.selection_set_builder.build()?,
69 type_condition_ref,
70 })
71 }
72
73 pub fn from_ast(
74 schema: &'schema Schema,
75 fragment_registry: &'fragreg FragmentRegistry<'schema>,
76 ast: &ast::operation::FragmentDefinition,
77 file_path: Option<&Path>,
78 ) -> Result<Self> {
79 let fragdef_srcloc = loc::SourceLocation::from_execdoc_ast_position(
80 file_path,
81 &ast.position,
82 );
83
84 let directives = DirectiveAnnotationBuilder::from_ast(
85 &fragdef_srcloc,
86 &ast.directives,
87 );
88
89 let type_condition_type_name = match &ast.type_condition {
90 ast::operation::TypeCondition::On(type_name) => type_name,
91 };
92 let type_condition_type =
93 schema.all_types()
94 .get(type_condition_type_name)
95 .ok_or_else(||
96 FragmentBuildError::TypeConditionTypeDoesNotExistInSchema {
97 fragment_name: ast.name.to_string(),
98 fragment_src_location: fragdef_srcloc.to_owned(),
99 type_condition_type_name:
100 type_condition_type_name.to_owned(),
101 }
102 )?;
103
104 let selection_set_builder = SelectionSetBuilder::from_ast(
105 schema,
106 fragment_registry,
107 type_condition_type,
108 &ast.selection_set,
109 file_path,
110 )?;
111
112 Ok(Self {
113 def_location: fragdef_srcloc.to_owned(),
114 directives,
115 fragment_registry,
116 name: Some(ast.name.to_string()),
117 schema,
118 selection_set_builder,
119 type_condition_ref: Some(NamedGraphQLTypeRef::new(
120 type_condition_type_name,
121 fragdef_srcloc.to_owned(),
122 )),
123 })
124 }
125
126 pub fn set_name(mut self, name: impl Into<String>) -> Result<Self> {
127 let _ = self.name.insert(name.into());
128 Ok(self)
129 }
130
131 pub fn set_type_condition(
132 mut self,
133 graphql_type: &'schema GraphQLType,
134 ) -> Result<Self> {
135 match graphql_type {
136 GraphQLType::Interface(_)
137 | GraphQLType::Object(_)
138 | GraphQLType::Union(_)
139 => (),
140
141 _ => return Err(FragmentBuildError::InvalidFragmentTypeConditionTypeKind {
142 fragment_def_src_location: self.def_location,
143 invalid_type_name: graphql_type.name().to_string(),
144 invalid_type_kind: graphql_type.into(),
145 }),
146 };
147
148 let _ = self.type_condition_ref.insert(NamedGraphQLTypeRef::new(
149 graphql_type.name(),
150 self.def_location.to_owned(),
152 ));
153
154 Ok(self)
155 }
156}
157
158#[derive(Clone, Debug, Error)]
159pub enum FragmentBuildError {
160 #[error("Invalid fragment type condition type: `{invalid_type_kind:?}`")]
161 InvalidFragmentTypeConditionTypeKind {
162 fragment_def_src_location: loc::SourceLocation,
163 invalid_type_name: String,
164 invalid_type_kind: GraphQLTypeKind,
165 },
166
167 #[error("All fragment definitions must include a name")]
168 NoFragmentNameSpecified {
169 fragment_def_src_location: loc::SourceLocation,
170 },
171
172 #[error(
173 "Fragments must specify the type for which they apply to, but none \
174 was specified for the `{fragment_name}` fragment."
175 )]
176 NoTypeConditionSpecified {
177 fragment_name: String,
178 fragment_src_location: loc::SourceLocation,
179 },
180
181 #[error("Failure to build the selection set for this fragment: $0")]
182 SelectionSetBuildErrors(Vec<SelectionSetBuildError>),
183
184 #[error(
185 "The `{fragment_name}` fragment declares its type condition as \
186 `{type_condition_type_name}`, but this type is not defined in \
187 the schema.
188 ")]
189 TypeConditionTypeDoesNotExistInSchema {
190 fragment_name: String,
191 fragment_src_location: loc::SourceLocation,
192 type_condition_type_name: String,
193 },
194}
195impl std::convert::From<Vec<SelectionSetBuildError>> for FragmentBuildError {
196 fn from(value: Vec<SelectionSetBuildError>) -> Self {
197 Self::SelectionSetBuildErrors(value)
198 }
199}