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