apollo_encoder/
document.rs1use std::fmt;
2
3use crate::{
4 DirectiveDefinition, EnumDefinition, FragmentDefinition, InputObjectDefinition,
5 InterfaceDefinition, ObjectDefinition, OperationDefinition, ScalarDefinition, SchemaDefinition,
6 UnionDefinition,
7};
8
9#[derive(Debug, Clone, Default)]
77pub struct Document {
78 operation_definitions: Vec<OperationDefinition>,
79 fragment_definitions: Vec<FragmentDefinition>,
80 schema_definitions: Vec<SchemaDefinition>,
81 scalar_type_definitions: Vec<ScalarDefinition>,
83 object_type_definitions: Vec<ObjectDefinition>,
84 interface_type_definitions: Vec<InterfaceDefinition>,
85 union_type_definitions: Vec<UnionDefinition>,
86 enum_type_definitions: Vec<EnumDefinition>,
87 input_object_type_definitions: Vec<InputObjectDefinition>,
88 directive_definitions: Vec<DirectiveDefinition>,
90}
91
92impl Document {
93 pub fn new() -> Self {
95 Self::default()
96 }
97
98 pub fn operation(&mut self, operation_definition: OperationDefinition) {
100 self.operation_definitions.push(operation_definition);
101 }
102
103 pub fn fragment(&mut self, fragment_definition: FragmentDefinition) {
105 self.fragment_definitions.push(fragment_definition);
106 }
107
108 pub fn schema(&mut self, schema_definition: SchemaDefinition) {
110 self.schema_definitions.push(schema_definition);
111 }
112 pub fn scalar(&mut self, scalar_type_definition: ScalarDefinition) {
114 self.scalar_type_definitions.push(scalar_type_definition);
115 }
116 pub fn object(&mut self, object_type_definition: ObjectDefinition) {
118 self.object_type_definitions.push(object_type_definition);
119 }
120 pub fn interface(&mut self, interface_type_definition: InterfaceDefinition) {
122 self.interface_type_definitions
123 .push(interface_type_definition);
124 }
125 pub fn union(&mut self, union_type_definition: UnionDefinition) {
127 self.union_type_definitions.push(union_type_definition);
128 }
129 pub fn enum_(&mut self, enum_type_definition: EnumDefinition) {
131 self.enum_type_definitions.push(enum_type_definition);
132 }
133
134 pub fn input_object(&mut self, input_object_type_definition: InputObjectDefinition) {
136 self.input_object_type_definitions
137 .push(input_object_type_definition);
138 }
139 pub fn directive(&mut self, directive_definition: DirectiveDefinition) {
141 self.directive_definitions.push(directive_definition);
142 }
143}
144
145impl fmt::Display for Document {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 for operation_def in &self.operation_definitions {
148 write!(f, "{operation_def}")?;
149 }
150 for fragment_def in &self.fragment_definitions {
151 write!(f, "{fragment_def}")?;
152 }
153 for schema_def in &self.schema_definitions {
154 write!(f, "{schema_def}")?;
155 }
156 for scalar_type_def in &self.scalar_type_definitions {
157 write!(f, "{scalar_type_def}")?;
158 }
159 for object_type_def in &self.object_type_definitions {
160 write!(f, "{object_type_def}")?;
161 }
162 for interface_type_def in &self.interface_type_definitions {
163 write!(f, "{interface_type_def}")?;
164 }
165 for union_type_def in &self.union_type_definitions {
166 write!(f, "{union_type_def}")?;
167 }
168 for enum_type_def in &self.enum_type_definitions {
169 write!(f, "{enum_type_def}")?;
170 }
171 for input_object_type_def in &self.input_object_type_definitions {
172 write!(f, "{input_object_type_def}")?;
173 }
174 for directive_def in &self.directive_definitions {
175 write!(f, "{directive_def}")?;
176 }
177
178 Ok(())
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::{
185 Argument, Directive, EnumValue, Field, FragmentSpread, InlineFragment, OperationType,
186 Selection, SelectionSet, TypeCondition, Type_, Value, VariableDefinition,
187 };
188
189 use super::*;
190 use indoc::indoc;
191
192 #[test]
193 fn it_encodes_a_document_with_operation() {
194 let mut document = Document::new();
195 let selection_set = {
196 let sels = vec![
197 Selection::Field(Field::new(String::from("first"))),
198 Selection::Field(Field::new(String::from("second"))),
199 ];
200 let mut sel_set = SelectionSet::new();
201 sels.into_iter().for_each(|sel| sel_set.selection(sel));
202
203 sel_set
204 };
205 let var_def = VariableDefinition::new(
206 String::from("variable_def"),
207 Type_::List {
208 ty: Box::new(Type_::NamedType {
209 name: String::from("Int"),
210 }),
211 },
212 );
213 let mut new_op = OperationDefinition::new(OperationType::Query, selection_set);
214 let mut directive = Directive::new(String::from("testDirective"));
215 directive.arg(Argument::new(
216 String::from("first"),
217 Value::String("one".to_string()),
218 ));
219 new_op.variable_definition(var_def);
220 new_op.directive(directive);
221
222 document.operation(new_op);
223
224 assert_eq!(
225 document.to_string(),
226 indoc! { r#"
227 query($variable_def: [Int]) @testDirective(first: "one") {
228 first
229 second
230 }
231 "#}
232 );
233 }
234
235 #[test]
236 fn it_encodes_document_with_operation_and_fragments() {
237 let mut document = Document::new();
238
239 let mut droid_selection_set = SelectionSet::new();
240 let primary_function_field = Selection::Field(Field::new(String::from("primaryFunction")));
241 droid_selection_set.selection(primary_function_field);
242
243 let mut droid_inline_fragment = InlineFragment::new(droid_selection_set);
244 droid_inline_fragment.type_condition(Some(TypeCondition::new(String::from("Droid"))));
245
246 let comparison_fields_fragment_spread =
247 FragmentSpread::new(String::from("comparisonFields"));
248
249 let name_field = Field::new(String::from("name"));
250
251 let hero_selection_set = vec![
252 Selection::Field(name_field),
253 Selection::FragmentSpread(comparison_fields_fragment_spread),
254 Selection::InlineFragment(droid_inline_fragment),
255 ];
256
257 let mut hero_field = Field::new(String::from("hero"));
258 hero_field.selection_set(Some(SelectionSet::with_selections(hero_selection_set)));
259
260 let hero_for_episode_selection_set = vec![Selection::Field(hero_field)];
261 let mut hero_for_episode_operation = OperationDefinition::new(
262 OperationType::Query,
263 SelectionSet::with_selections(hero_for_episode_selection_set),
264 );
265 hero_for_episode_operation.name(Some(String::from("HeroForEpisode")));
266
267 document.operation(hero_for_episode_operation);
268
269 assert_eq!(
270 document.to_string(),
271 indoc! { r#"
272 query HeroForEpisode {
273 hero {
274 name
275 ...comparisonFields
276 ... on Droid {
277 primaryFunction
278 }
279 }
280 }
281 "#}
282 );
283 }
284
285 #[test]
286 fn it_encodes_a_document_with_type_system_definition() {
287 let mut document = Document::new();
288
289 let mut directive_def = DirectiveDefinition::new("provideTreat".to_string());
291 directive_def.description("Ensures cats get treats.".to_string());
292 directive_def.location("OBJECT".to_string());
293 directive_def.location("FIELD_DEFINITION".to_string());
294 directive_def.location("INPUT_FIELD_DEFINITION".to_string());
295 document.directive(directive_def);
296
297 let mut enum_ty_1 = EnumValue::new("CatTree".to_string());
299 enum_ty_1.description("Top bunk of a cat tree.".to_string());
300 let enum_ty_2 = EnumValue::new("Bed".to_string());
301 let mut enum_ty_3 = EnumValue::new("CardboardBox".to_string());
302 let mut directive = Directive::new(String::from("deprecated"));
303 directive.arg(Argument::new(
304 String::from("reason"),
305 Value::String("Box was recycled.".to_string()),
306 ));
307 enum_ty_3.directive(directive);
308
309 let mut enum_def = EnumDefinition::new("NapSpots".to_string());
310 enum_def.description("Favourite cat\nnap spots.".to_string());
311 enum_def.value(enum_ty_1);
312 enum_def.value(enum_ty_2);
313 enum_def.value(enum_ty_3);
314 document.enum_(enum_def);
315 let mut union_def = UnionDefinition::new("Pet".to_string());
317 union_def.description("A union of all pets represented within a household.".to_string());
318 union_def.member("Cat".to_string());
319 union_def.member("Dog".to_string());
320 document.union(union_def);
321
322 assert_eq!(
323 document.to_string(),
324 indoc! { r#"
325 "A union of all pets represented within a household."
326 union Pet = Cat | Dog
327 """
328 Favourite cat
329 nap spots.
330 """
331 enum NapSpots {
332 "Top bunk of a cat tree."
333 CatTree
334 Bed
335 CardboardBox @deprecated(reason: "Box was recycled.")
336 }
337 "Ensures cats get treats."
338 directive @provideTreat on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
339 "# }
340 );
341 }
342}