1use crate::directive::Directive;
2use crate::directive::DirectiveLocation;
3use crate::name::Name;
4use crate::selection_set::SelectionSet;
5use crate::ty::Ty;
6use crate::DocumentBuilder;
7use apollo_compiler::ast;
8use arbitrary::Result as ArbitraryResult;
9use indexmap::IndexMap;
10
11#[derive(Debug, Clone)]
18pub struct FragmentDef {
19 pub(crate) name: Name,
20 pub(crate) type_condition: TypeCondition,
21 pub(crate) directives: IndexMap<Name, Directive>,
22 pub(crate) selection_set: SelectionSet,
23}
24
25impl From<FragmentDef> for ast::Definition {
26 fn from(x: FragmentDef) -> Self {
27 ast::FragmentDefinition {
28 name: x.name.into(),
29 type_condition: x.type_condition.name.into(),
30 directives: Directive::to_ast(x.directives),
31 selection_set: x.selection_set.into(),
32 }
33 .into()
34 }
35}
36
37impl TryFrom<apollo_parser::cst::FragmentDefinition> for FragmentDef {
38 type Error = crate::FromError;
39
40 fn try_from(fragment_def: apollo_parser::cst::FragmentDefinition) -> Result<Self, Self::Error> {
41 Ok(Self {
42 name: fragment_def.fragment_name().unwrap().name().unwrap().into(),
43 directives: fragment_def
44 .directives()
45 .map(Directive::convert_directives)
46 .transpose()?
47 .unwrap_or_default(),
48 type_condition: fragment_def.type_condition().unwrap().into(),
49 selection_set: fragment_def.selection_set().unwrap().try_into()?,
50 })
51 }
52}
53
54#[derive(Debug, Clone)]
61pub struct FragmentSpread {
62 pub(crate) name: Name,
63 pub(crate) directives: IndexMap<Name, Directive>,
64}
65
66impl From<FragmentSpread> for ast::FragmentSpread {
67 fn from(x: FragmentSpread) -> Self {
68 Self {
69 fragment_name: x.name.into(),
70 directives: Directive::to_ast(x.directives),
71 }
72 }
73}
74
75impl TryFrom<apollo_parser::cst::FragmentSpread> for FragmentSpread {
76 type Error = crate::FromError;
77
78 fn try_from(fragment_spread: apollo_parser::cst::FragmentSpread) -> Result<Self, Self::Error> {
79 Ok(Self {
80 name: fragment_spread
81 .fragment_name()
82 .unwrap()
83 .name()
84 .unwrap()
85 .into(),
86 directives: fragment_spread
87 .directives()
88 .map(Directive::convert_directives)
89 .transpose()?
90 .unwrap_or_default(),
91 })
92 }
93}
94
95#[derive(Debug, Clone)]
102pub struct InlineFragment {
103 pub(crate) type_condition: Option<TypeCondition>,
104 pub(crate) directives: IndexMap<Name, Directive>,
105 pub(crate) selection_set: SelectionSet,
106}
107
108impl From<InlineFragment> for ast::InlineFragment {
109 fn from(x: InlineFragment) -> Self {
110 Self {
111 type_condition: x.type_condition.map(|t| t.name.into()),
112 directives: Directive::to_ast(x.directives),
113 selection_set: x.selection_set.into(),
114 }
115 }
116}
117
118impl TryFrom<apollo_parser::cst::InlineFragment> for InlineFragment {
119 type Error = crate::FromError;
120
121 fn try_from(inline_fragment: apollo_parser::cst::InlineFragment) -> Result<Self, Self::Error> {
122 Ok(Self {
123 directives: inline_fragment
124 .directives()
125 .map(Directive::convert_directives)
126 .transpose()?
127 .unwrap_or_default(),
128 selection_set: inline_fragment.selection_set().unwrap().try_into()?,
129 type_condition: inline_fragment.type_condition().map(TypeCondition::from),
130 })
131 }
132}
133
134#[derive(Debug, Clone)]
141pub struct TypeCondition {
142 name: Name,
143}
144
145impl From<apollo_parser::cst::TypeCondition> for TypeCondition {
146 fn from(type_condition: apollo_parser::cst::TypeCondition) -> Self {
147 Self {
148 name: type_condition.named_type().unwrap().name().unwrap().into(),
149 }
150 }
151}
152
153impl DocumentBuilder<'_> {
154 pub fn fragment_definition(&mut self) -> ArbitraryResult<FragmentDef> {
156 let selected_object_type_name = self.u.choose(&self.object_type_defs)?.name.clone();
158 let _ = self.stack_ty(&Ty::Named(selected_object_type_name));
159 let name = self.type_name()?;
160 let directives = self.directives(DirectiveLocation::FragmentDefinition)?;
161 let selection_set = self.selection_set()?;
162 let type_condition = self.type_condition()?;
163 self.stack.pop();
164
165 Ok(FragmentDef {
166 name,
167 type_condition,
168 directives,
169 selection_set,
170 })
171 }
172
173 pub fn fragment_spread(
175 &mut self,
176 excludes: &mut Vec<Name>,
177 ) -> ArbitraryResult<Option<FragmentSpread>> {
178 let available_fragment: Vec<&FragmentDef> = self
179 .fragment_defs
180 .iter()
181 .filter(|f| excludes.contains(&f.name))
182 .collect();
183
184 let name = if available_fragment.is_empty() {
185 return Ok(None);
186 } else {
187 self.u.choose(&available_fragment)?.name.clone()
188 };
189 let directives = self.directives(DirectiveLocation::FragmentSpread)?;
190 excludes.push(name.clone());
191
192 Ok(Some(FragmentSpread { name, directives }))
193 }
194
195 pub fn inline_fragment(&mut self) -> ArbitraryResult<InlineFragment> {
197 let type_condition = self
198 .u
199 .arbitrary()
200 .unwrap_or(false)
201 .then(|| self.type_condition())
202 .transpose()?;
203 let selection_set = self.selection_set()?;
204 let directives = self.directives(DirectiveLocation::InlineFragment)?;
205
206 Ok(InlineFragment {
207 type_condition,
208 directives,
209 selection_set,
210 })
211 }
212
213 pub fn type_condition(&mut self) -> ArbitraryResult<TypeCondition> {
215 let last_element = self.stack.last();
216 match last_element {
217 Some(last_element) => Ok(TypeCondition {
218 name: last_element.name().clone(),
219 }),
220 None => {
221 let named_types: Vec<Ty> = self
222 .list_existing_object_types()
223 .into_iter()
224 .filter(Ty::is_named)
225 .collect();
226
227 Ok(TypeCondition {
228 name: self.choose_named_ty(&named_types)?.name().clone(),
229 })
230 }
231 }
232 }
233}