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}