1use crate::argument::Argument;
2use crate::argument::ArgumentsDef;
3use crate::description::Description;
4use crate::directive::Directive;
5use crate::directive::DirectiveLocation;
6use crate::name::Name;
7use crate::selection_set::SelectionSet;
8use crate::ty::Ty;
9use crate::DocumentBuilder;
10use apollo_compiler::ast;
11use apollo_compiler::Node;
12use arbitrary::Result as ArbitraryResult;
13use indexmap::IndexMap;
14use indexmap::IndexSet;
15
16#[derive(Debug, Clone)]
23pub struct FieldDef {
24 pub(crate) description: Option<Description>,
25 pub(crate) name: Name,
26 pub(crate) arguments_definition: Option<ArgumentsDef>,
27 pub(crate) ty: Ty,
28 pub(crate) directives: IndexMap<Name, Directive>,
29}
30
31impl From<FieldDef> for ast::FieldDefinition {
32 fn from(x: FieldDef) -> Self {
33 Self {
34 description: x.description.map(Into::into),
35 name: x.name.into(),
36 directives: Directive::to_ast(x.directives),
37 arguments: x.arguments_definition.map(Into::into).unwrap_or_default(),
38 ty: x.ty.into(),
39 }
40 }
41}
42
43impl TryFrom<apollo_parser::cst::FieldDefinition> for FieldDef {
44 type Error = crate::FromError;
45
46 fn try_from(field_def: apollo_parser::cst::FieldDefinition) -> Result<Self, Self::Error> {
47 Ok(Self {
48 description: field_def.description().map(Description::from),
49 name: field_def
50 .name()
51 .expect("field definition must have a name")
52 .into(),
53 arguments_definition: field_def
54 .arguments_definition()
55 .map(ArgumentsDef::try_from)
56 .transpose()?,
57 ty: field_def.ty().unwrap().into(),
58 directives: field_def
59 .directives()
60 .map(Directive::convert_directives)
61 .transpose()?
62 .unwrap_or_default(),
63 })
64 }
65}
66
67#[derive(Debug, Clone)]
74pub struct Field {
75 pub(crate) alias: Option<Name>,
76 pub(crate) name: Name,
77 pub(crate) args: Vec<Argument>,
78 pub(crate) directives: IndexMap<Name, Directive>,
79 pub(crate) selection_set: Option<SelectionSet>,
80}
81
82impl From<Field> for ast::Field {
83 fn from(x: Field) -> Self {
84 Self {
85 alias: x.alias.map(Into::into),
86 name: x.name.into(),
87 directives: Directive::to_ast(x.directives),
88 arguments: x.args.into_iter().map(|x| Node::new(x.into())).collect(),
89 selection_set: x.selection_set.map(Into::into).unwrap_or_default(),
90 }
91 }
92}
93
94impl TryFrom<apollo_parser::cst::Field> for Field {
95 type Error = crate::FromError;
96
97 fn try_from(field: apollo_parser::cst::Field) -> Result<Self, Self::Error> {
98 Ok(Self {
99 alias: field.alias().map(|alias| alias.name().unwrap().into()),
100 name: field.name().unwrap().into(),
101 args: field
102 .arguments()
103 .map(|arguments| {
104 arguments
105 .arguments()
106 .map(Argument::try_from)
107 .collect::<Result<_, _>>()
108 })
109 .transpose()?
110 .unwrap_or_default(),
111 directives: field
112 .directives()
113 .map(Directive::convert_directives)
114 .transpose()?
115 .unwrap_or_default(),
116 selection_set: field
117 .selection_set()
118 .map(SelectionSet::try_from)
119 .transpose()?,
120 })
121 }
122}
123
124impl DocumentBuilder<'_> {
125 pub fn fields_definition(&mut self, exclude: &[&Name]) -> ArbitraryResult<Vec<FieldDef>> {
127 let num_fields = self.u.int_in_range(2..=50usize)?;
128 let mut fields_names = IndexSet::with_capacity(num_fields);
129
130 for i in 0..num_fields {
131 let name = self.name_with_index(i)?;
132 if !exclude.contains(&&name) {
133 fields_names.insert(name);
134 }
135 }
136
137 let available_types: Vec<Ty> = self.list_existing_types();
139
140 fields_names
141 .into_iter()
142 .map(|field_name| {
143 Ok(FieldDef {
144 description: self
145 .u
146 .arbitrary()
147 .unwrap_or(false)
148 .then(|| self.description())
149 .transpose()?,
150 name: field_name,
151 arguments_definition: self
152 .u
153 .arbitrary()
154 .unwrap_or(false)
155 .then(|| self.arguments_definition())
156 .transpose()?,
157 ty: self.choose_ty(&available_types)?,
158 directives: self.directives(DirectiveLocation::FieldDefinition)?,
159 })
160 })
161 .collect()
162 }
163
164 pub fn field(&mut self, index: usize) -> ArbitraryResult<Field> {
166 let fields_defs = self
167 .stack
168 .last()
169 .expect("an object type must be added on the stack")
170 .fields_def();
171
172 let chosen_field_def = self.u.choose(fields_defs)?.clone();
173 let mut alias = self
174 .u
175 .arbitrary()
176 .unwrap_or(false)
177 .then(|| self.name_with_index(index))
178 .transpose()?;
179
180 let name = chosen_field_def.name.clone();
181 let args = match self.chosen_arguments.get(&name) {
183 Some(args) => args.clone(),
184 None => {
185 let args = chosen_field_def
186 .arguments_definition
187 .clone()
188 .map(|args_def| self.arguments_with_def(&args_def))
189 .unwrap_or_else(|| Ok(vec![]))?;
190 self.chosen_arguments.insert(name.clone(), args.clone());
191
192 args
193 }
194 };
195 let directives = self.directives(DirectiveLocation::Field)?;
196
197 let selection_set = if !chosen_field_def.ty.is_builtin() {
198 if self.stack_ty(&chosen_field_def.ty) {
200 let res = Some(self.selection_set()?);
201 self.stack.pop();
202 res
203 } else {
204 None
205 }
206 } else {
207 None
208 };
209
210 if let Some(alias_name) = alias.take() {
222 match self.chosen_aliases.get(&alias_name) {
223 None => {
224 self.chosen_aliases.insert(alias_name.clone(), name.clone());
225 alias = Some(alias_name);
226 }
227 Some(original_field_name) => {
228 if original_field_name == &name {
230 alias = Some(alias_name);
231 }
232 }
233 }
234 }
235
236 Ok(Field {
237 alias,
238 name,
239 args,
240 directives,
241 selection_set,
242 })
243 }
244}