apollo_smith/
operation.rs1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::selection_set::Selection;
6use crate::selection_set::SelectionSet;
7use crate::variable::VariableDef;
8use crate::DocumentBuilder;
9use apollo_compiler::ast;
10use apollo_compiler::Node;
11use arbitrary::Arbitrary;
12use arbitrary::Result as ArbitraryResult;
13use indexmap::IndexMap;
14
15#[derive(Debug, Clone)]
22pub struct OperationDef {
23 pub(crate) description: Option<Description>,
24 pub(crate) operation_type: OperationType,
25 pub(crate) name: Option<Name>,
26 pub(crate) variable_definitions: Vec<VariableDef>,
27 pub(crate) directives: IndexMap<Name, Directive>,
28 pub(crate) selection_set: SelectionSet,
29}
30
31impl From<OperationDef> for ast::Definition {
32 fn from(x: OperationDef) -> Self {
33 ast::OperationDefinition {
34 description: x.description.map(Into::into),
35 operation_type: x.operation_type.into(),
36 name: x.name.map(Into::into),
37 directives: Directive::to_ast(x.directives),
38 variables: x
39 .variable_definitions
40 .into_iter()
41 .map(|x| Node::new(x.into()))
42 .collect(),
43 selection_set: x.selection_set.into(),
44 }
45 .into()
46 }
47}
48
49impl From<OperationDef> for String {
50 fn from(op_def: OperationDef) -> Self {
51 ast::Definition::from(op_def).to_string()
52 }
53}
54
55impl TryFrom<apollo_parser::cst::OperationDefinition> for OperationDef {
56 type Error = crate::FromError;
57
58 fn try_from(
59 operation_def: apollo_parser::cst::OperationDefinition,
60 ) -> Result<Self, Self::Error> {
61 Ok(Self {
62 description: operation_def.description().map(Description::from),
63 name: operation_def.name().map(Name::from),
64 directives: operation_def
65 .directives()
66 .map(Directive::convert_directives)
67 .transpose()?
68 .unwrap_or_default(),
69 operation_type: operation_def
70 .operation_type()
71 .map(OperationType::from)
72 .unwrap_or(OperationType::Query),
73 variable_definitions: Vec::new(),
74 selection_set: operation_def.selection_set().unwrap().try_into()?,
75 })
76 }
77}
78
79#[derive(Debug, Arbitrary, Clone, Copy, PartialEq, Eq)]
86pub enum OperationType {
87 Query,
88 Mutation,
89 Subscription,
90}
91
92impl From<OperationType> for ast::OperationType {
93 fn from(op_type: OperationType) -> Self {
94 match op_type {
95 OperationType::Query => Self::Query,
96 OperationType::Mutation => Self::Mutation,
97 OperationType::Subscription => Self::Subscription,
98 }
99 }
100}
101
102impl From<apollo_parser::cst::OperationType> for OperationType {
103 fn from(op_type: apollo_parser::cst::OperationType) -> Self {
104 if op_type.query_token().is_some() {
105 Self::Query
106 } else if op_type.mutation_token().is_some() {
107 Self::Mutation
108 } else if op_type.subscription_token().is_some() {
109 Self::Subscription
110 } else {
111 Self::Query
112 }
113 }
114}
115
116impl DocumentBuilder<'_> {
117 pub fn operation_definition(&mut self) -> ArbitraryResult<Option<OperationDef>> {
119 self.operation_definition_in_document(false)
120 }
121
122 pub(crate) fn operation_definition_in_document(
135 &mut self,
136 require_named: bool,
137 ) -> ArbitraryResult<Option<OperationDef>> {
138 let schema = match self.schema_def.clone() {
139 Some(schema_def) => schema_def,
140 None => return Ok(None),
141 };
142
143 let want_name = require_named || self.u.arbitrary().unwrap_or(false);
144 let name = if want_name {
145 Some(self.type_name()?)
146 } else {
147 None
148 };
149
150 let available_operations = {
151 let mut ops = vec![];
152 if let Some(query) = &schema.query {
153 ops.push((OperationType::Query, query));
154 }
155 if let Some(mutation) = &schema.mutation {
156 ops.push((OperationType::Mutation, mutation));
157 }
158 if let Some(subscription) = &schema.subscription {
159 ops.push((OperationType::Subscription, subscription));
160 }
161
162 ops
163 };
164
165 let (operation_type, chosen_ty) = self.u.choose(&available_operations)?;
166 let directive_location = match operation_type {
167 OperationType::Query => DirectiveLocation::Query,
168 OperationType::Mutation => DirectiveLocation::Mutation,
169 OperationType::Subscription => DirectiveLocation::Subscription,
170 };
171 let directives = self.directives(directive_location)?;
172
173 self.stack_ty(chosen_ty);
175
176 let selection_set = if matches!(operation_type, OperationType::Subscription) {
177 SelectionSet {
182 selections: vec![Selection::Field(self.field(0)?)],
183 }
184 } else {
185 self.selection_set()?
186 };
187
188 self.stack.pop();
189 assert!(
190 self.stack.is_empty(),
191 "the stack must be empty at the end of an operation definition"
192 );
193
194 let variable_definitions = vec![];
196
197 Ok(Some(OperationDef {
198 description: None,
199 operation_type: *operation_type,
200 name,
201 variable_definitions,
202 directives,
203 selection_set,
204 }))
205 }
206}