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