apollo_smith/
input_value.rs

1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::ty::Ty;
6use crate::DocumentBuilder;
7use apollo_compiler::ast;
8use apollo_compiler::Node;
9use arbitrary::Result as ArbitraryResult;
10use indexmap::IndexMap;
11
12#[derive(Debug, Clone, Copy)]
13pub enum Constness {
14    Const,
15    NonConst,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum InputValue {
20    Variable(Name),
21    Int(i32),
22    Float(f64),
23    String(String),
24    Boolean(bool),
25    Null,
26    Enum(Name),
27    List(Vec<InputValue>),
28    Object(Vec<(Name, InputValue)>),
29}
30
31impl From<InputValue> for ast::Value {
32    fn from(input_value: InputValue) -> Self {
33        match input_value {
34            InputValue::Variable(v) => Self::Variable(v.into()),
35            InputValue::Int(i) => Self::Int(i.into()),
36            InputValue::Float(f) => Self::Float(f.into()),
37            InputValue::String(s) => Self::String(s),
38            InputValue::Boolean(b) => Self::Boolean(b),
39            InputValue::Null => Self::Null,
40            InputValue::Enum(enm) => Self::Enum(enm.into()),
41            InputValue::List(l) => Self::List(l.into_iter().map(|v| Node::new(v.into())).collect()),
42            InputValue::Object(o) => Self::Object(
43                o.into_iter()
44                    .map(|(n, i)| (n.into(), Node::new(i.into())))
45                    .collect(),
46            ),
47        }
48    }
49}
50
51impl TryFrom<apollo_parser::cst::DefaultValue> for InputValue {
52    type Error = crate::FromError;
53
54    fn try_from(default_val: apollo_parser::cst::DefaultValue) -> Result<Self, Self::Error> {
55        default_val.value().unwrap().try_into()
56    }
57}
58
59impl TryFrom<apollo_parser::cst::Value> for InputValue {
60    type Error = crate::FromError;
61
62    fn try_from(value: apollo_parser::cst::Value) -> Result<Self, Self::Error> {
63        let smith_value = match value {
64            apollo_parser::cst::Value::Variable(variable) => {
65                Self::Variable(variable.name().unwrap().into())
66            }
67            apollo_parser::cst::Value::StringValue(val) => Self::String(val.into()),
68            apollo_parser::cst::Value::FloatValue(val) => Self::Float(val.try_into()?),
69            apollo_parser::cst::Value::IntValue(val) => Self::Int(val.try_into()?),
70            apollo_parser::cst::Value::BooleanValue(val) => Self::Boolean(val.try_into()?),
71            apollo_parser::cst::Value::NullValue(_val) => Self::Null,
72            apollo_parser::cst::Value::EnumValue(val) => Self::Enum(val.name().unwrap().into()),
73            apollo_parser::cst::Value::ListValue(val) => Self::List(
74                val.values()
75                    .map(Self::try_from)
76                    .collect::<Result<Vec<_>, _>>()?,
77            ),
78            apollo_parser::cst::Value::ObjectValue(val) => Self::Object(
79                val.object_fields()
80                    .map(|of| Ok((of.name().unwrap().into(), of.value().unwrap().try_into()?)))
81                    .collect::<Result<Vec<_>, crate::FromError>>()?,
82            ),
83        };
84        Ok(smith_value)
85    }
86}
87
88impl From<InputValue> for String {
89    fn from(input_val: InputValue) -> Self {
90        match input_val {
91            InputValue::Variable(v) => format!("${}", String::from(v)),
92            InputValue::Int(i) => format!("{i}"),
93            InputValue::Float(f) => format!("{f}"),
94            InputValue::String(s) => s,
95            InputValue::Boolean(b) => format!("{b}"),
96            InputValue::Null => String::from("null"),
97            InputValue::Enum(val) => val.into(),
98            InputValue::List(list) => format!(
99                "[{}]",
100                list.into_iter()
101                    .map(String::from)
102                    .collect::<Vec<String>>()
103                    .join(", ")
104            ),
105            InputValue::Object(obj) => format!(
106                "{{ {} }}",
107                obj.into_iter()
108                    .map(|(k, v)| format!("{}: {}", String::from(k), String::from(v)))
109                    .collect::<Vec<String>>()
110                    .join(", ")
111            ),
112        }
113    }
114}
115
116/// The __InputValueDef type represents field and directive arguments.
117///
118/// *InputValueDefinition*:
119///     Description? Name **:** Type DefaultValue? Directives?
120///
121/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-The-__InputValue-Type).
122#[derive(Debug, Clone, PartialEq)]
123pub struct InputValueDef {
124    pub(crate) description: Option<Description>,
125    pub(crate) name: Name,
126    pub(crate) ty: Ty,
127    pub(crate) default_value: Option<InputValue>,
128    pub(crate) directives: IndexMap<Name, Directive>,
129}
130
131impl From<InputValueDef> for ast::InputValueDefinition {
132    fn from(x: InputValueDef) -> Self {
133        Self {
134            description: x.description.map(Into::into),
135            name: x.name.into(),
136            ty: Node::new(x.ty.into()),
137            default_value: x.default_value.map(|x| Node::new(x.into())),
138            directives: Directive::to_ast(x.directives),
139        }
140    }
141}
142
143impl TryFrom<apollo_parser::cst::InputValueDefinition> for InputValueDef {
144    type Error = crate::FromError;
145
146    fn try_from(
147        input_val_def: apollo_parser::cst::InputValueDefinition,
148    ) -> Result<Self, Self::Error> {
149        Ok(Self {
150            description: input_val_def.description().map(Description::from),
151            name: input_val_def.name().unwrap().into(),
152            ty: input_val_def.ty().unwrap().into(),
153            default_value: input_val_def
154                .default_value()
155                .map(InputValue::try_from)
156                .transpose()?,
157            directives: input_val_def
158                .directives()
159                .map(Directive::convert_directives)
160                .transpose()?
161                .unwrap_or_default(),
162        })
163    }
164}
165
166impl DocumentBuilder<'_> {
167    /// Create an arbitrary `InputValue`
168    pub fn input_value(&mut self, constness: Constness) -> ArbitraryResult<InputValue> {
169        let index = match constness {
170            Constness::Const => self.u.int_in_range(0..=7usize)?,
171            Constness::NonConst => self.u.int_in_range(0..=8usize)?,
172        };
173        let val = match index {
174            // Int
175            0 => InputValue::Int(self.u.arbitrary()?),
176            // Float
177            1 => InputValue::Float(self.finite_f64()?),
178            // String
179            2 => InputValue::String(self.limited_string(40)?),
180            // Boolean
181            3 => InputValue::Boolean(self.u.arbitrary()?),
182            // Null
183            4 => InputValue::Null,
184            // Enum
185            5 => {
186                if !self.enum_type_defs.is_empty() {
187                    // TODO get rid of this clone
188                    let enum_choosed = self.choose_enum()?.clone();
189                    InputValue::Enum(self.arbitrary_variant(&enum_choosed)?.clone())
190                } else {
191                    self.input_value(constness)?
192                }
193            }
194            // List
195            6 => {
196                // FIXME: it's semantically wrong it should always be the same type inside
197                InputValue::List(
198                    (0..self.u.int_in_range(2..=4usize)?)
199                        .map(|_| self.input_value(constness))
200                        .collect::<ArbitraryResult<Vec<_>>>()?,
201                )
202            }
203            // Object
204            7 => InputValue::Object(
205                (0..self.u.int_in_range(2..=4usize)?)
206                    .map(|_| Ok((self.name()?, self.input_value(constness)?)))
207                    .collect::<ArbitraryResult<Vec<_>>>()?,
208            ),
209            // Variable TODO: only generate valid variable name (existing variables)
210            8 => InputValue::Variable(self.name()?),
211            _ => unreachable!(),
212        };
213
214        Ok(val)
215    }
216
217    pub fn input_value_for_type(&mut self, ty: &Ty) -> ArbitraryResult<InputValue> {
218        let gen_val = |doc_builder: &mut DocumentBuilder<'_>| -> ArbitraryResult<InputValue> {
219            if ty.is_builtin() {
220                match ty.name().name.as_str() {
221                    "String" => Ok(InputValue::String(doc_builder.limited_string(1000)?)),
222                    "Int" => Ok(InputValue::Int(doc_builder.u.arbitrary()?)),
223                    "Float" => Ok(InputValue::Float(doc_builder.finite_f64()?)),
224                    "Boolean" => Ok(InputValue::Boolean(doc_builder.u.arbitrary()?)),
225                    "ID" => Ok(InputValue::Int(doc_builder.u.arbitrary()?)),
226                    other => {
227                        unreachable!("{} is not a builtin", other);
228                    }
229                }
230            } else if let Some(enum_) = doc_builder
231                .enum_type_defs
232                .iter()
233                .find(|e| &e.name == ty.name())
234                .cloned()
235            {
236                Ok(InputValue::Enum(
237                    doc_builder.arbitrary_variant(&enum_)?.clone(),
238                ))
239            } else if let Some(object_ty) = doc_builder
240                .object_type_defs
241                .iter()
242                .find(|o| &o.name == ty.name())
243                .cloned()
244            {
245                Ok(InputValue::Object(
246                    object_ty
247                        .fields_def
248                        .iter()
249                        .map(|field_def| {
250                            Ok((
251                                field_def.name.clone(),
252                                doc_builder.input_value_for_type(&field_def.ty)?,
253                            ))
254                        })
255                        .collect::<ArbitraryResult<Vec<_>>>()?,
256                ))
257            } else {
258                todo!()
259            }
260        };
261
262        let val = match ty {
263            Ty::Named(_) => gen_val(self)?,
264            Ty::List(_) => {
265                let nb_elt = self.u.int_in_range(1..=25usize)?;
266                InputValue::List(
267                    (0..nb_elt)
268                        .map(|_| gen_val(self))
269                        .collect::<ArbitraryResult<Vec<InputValue>>>()?,
270                )
271            }
272            Ty::NonNull(_) => gen_val(self)?,
273        };
274
275        Ok(val)
276    }
277
278    /// Create an arbitrary list of `InputValueDef`
279    pub fn input_values_def(&mut self) -> ArbitraryResult<Vec<InputValueDef>> {
280        let arbitrary_iv_num = self.u.int_in_range(2..=5usize)?;
281        let mut input_values = Vec::with_capacity(arbitrary_iv_num - 1);
282
283        for i in 0..arbitrary_iv_num {
284            let description = self
285                .u
286                .arbitrary()
287                .unwrap_or(false)
288                .then(|| self.description())
289                .transpose()?;
290            let name = self.name_with_index(i)?;
291            let ty = self.choose_ty(&self.list_existing_types())?;
292            // TODO: incorrect because input_values_def is called from different locations
293            let directives = self.directives(DirectiveLocation::InputFieldDefinition)?;
294            // TODO: FIXME: it's not correct I need to generate default value corresponding to the ty above
295            let default_value = self
296                .u
297                .arbitrary()
298                .unwrap_or(false)
299                .then(|| self.input_value(Constness::Const))
300                .transpose()?;
301
302            input_values.push(InputValueDef {
303                description,
304                name,
305                ty,
306                default_value,
307                directives,
308            });
309        }
310
311        Ok(input_values)
312    }
313    /// Create an arbitrary `InputValueDef`
314    pub fn input_value_def(&mut self) -> ArbitraryResult<InputValueDef> {
315        let description = self
316            .u
317            .arbitrary()
318            .unwrap_or(false)
319            .then(|| self.description())
320            .transpose()?;
321        let name = self.name()?;
322        let ty = self.choose_ty(&self.list_existing_types())?;
323        // TODO: incorrect because input_values_def is called from different locations
324        let directives = self.directives(DirectiveLocation::InputFieldDefinition)?;
325        // TODO: FIXME: it's not correct I need to generate default value corresponding to the ty above
326        let default_value = self
327            .u
328            .arbitrary()
329            .unwrap_or(false)
330            .then(|| self.input_value(Constness::Const))
331            .transpose()?;
332
333        Ok(InputValueDef {
334            description,
335            name,
336            ty,
337            default_value,
338            directives,
339        })
340    }
341
342    fn finite_f64(&mut self) -> arbitrary::Result<f64> {
343        loop {
344            let val: f64 = self.u.arbitrary()?;
345            if val.is_finite() {
346                return Ok(val);
347            }
348        }
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355    use crate::field::FieldDef;
356    use crate::ObjectTypeDef;
357    use arbitrary::Unstructured;
358    use indexmap::IndexMap;
359    use indexmap::IndexSet;
360
361    #[test]
362    fn test_input_value_for_type() {
363        let data: Vec<u8> = (0..=5000usize).map(|n| (n % 255) as u8).collect();
364        let mut u = Unstructured::new(&data);
365        let mut document_builder = DocumentBuilder {
366            u: &mut u,
367            input_object_type_defs: Vec::new(),
368            object_type_defs: Vec::new(),
369            interface_type_defs: Vec::new(),
370            union_type_defs: Vec::new(),
371            enum_type_defs: Vec::new(),
372            scalar_type_defs: Vec::new(),
373            schema_def: None,
374            directive_defs: Vec::new(),
375            operation_defs: Vec::new(),
376            fragment_defs: Vec::new(),
377            stack: Vec::new(),
378            chosen_arguments: IndexMap::new(),
379            chosen_aliases: IndexMap::new(),
380        };
381        let my_nested_type = ObjectTypeDef {
382            description: None,
383            name: Name {
384                name: String::from("my_nested_object"),
385            },
386            implements_interfaces: IndexSet::new(),
387            directives: IndexMap::new(),
388            fields_def: vec![FieldDef {
389                description: None,
390                name: Name {
391                    name: String::from("value"),
392                },
393                arguments_definition: None,
394                ty: Ty::Named(Name {
395                    name: String::from("String"),
396                }),
397                directives: IndexMap::new(),
398            }],
399            extend: false,
400        };
401
402        let my_object_type = ObjectTypeDef {
403            description: None,
404            name: Name {
405                name: String::from("my_object"),
406            },
407            implements_interfaces: IndexSet::new(),
408            directives: IndexMap::new(),
409            fields_def: vec![FieldDef {
410                description: None,
411                name: Name {
412                    name: String::from("first"),
413                },
414                arguments_definition: None,
415                ty: Ty::List(Box::new(Ty::Named(Name {
416                    name: String::from("my_nested_object"),
417                }))),
418                directives: IndexMap::new(),
419            }],
420            extend: false,
421        };
422        document_builder.object_type_defs.push(my_nested_type);
423        document_builder.object_type_defs.push(my_object_type);
424
425        let my_type_to_find = Ty::List(Box::new(Ty::Named(Name {
426            name: String::from("my_object"),
427        })));
428        document_builder.object_type_defs.iter().find(|o| {
429            let res = &o.name == my_type_to_find.name();
430
431            res
432        });
433
434        let input_val = document_builder
435            .input_value_for_type(&Ty::List(Box::new(Ty::Named(Name {
436                name: String::from("my_object"),
437            }))))
438            .unwrap();
439
440        let input_val_str = apollo_compiler::ast::Value::from(input_val)
441            .serialize()
442            .no_indent()
443            .to_string();
444
445        assert_eq!(
446            input_val_str.as_str(),
447            "[{first: [{value: \"womkigecaYWUSQOMKIGECA86420zxvtcaYWUSQOMKIGECA86420zxvtrpnljhfdbKIGECA86420zxvtrpnljhfdbZXVTRPN9420zxvtrpnljhfdbZXVTRPNLJHFDB97rpnljhfdbZXVTRPNLJHFDB97531_ywugbZXVTRPNLJHFDB97531_ywusqomkigeNLJHFDB97531_ywusqomkigecaYWUSQOM531_ywusqomkigecaYWUSQOMKIGECA8vqomkigecaYWUSQOMKIGECA86420zxvtcaYWUSQOMKIGECA86420zxvtrpnljhfdbKIGECA86420zxvtrpnljhfdbZXVTRPN9420zxvtrpnljhfdbZXVTRPNLJHFDB97rpnljhfdbZXVTRPNLJHFDB97531_ywugbZXVTRPNLJHFDB97531_ywusqomkigeNLJHFDB97531_ywusqomkigecaYWUSQOM531_ywusqomkigecaYWUSQOMKIGECA8vqomkig\"}, {value: \"C6420zxvtrpnljhfdbZXVTRPN9420zxvtrpnljhfdbZXVTRPNLJHFDB97rpnljhfdbZXVTRPNLJHFDB97531_ywugbZXVTRPNLJHFDB97531kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}]}]"
448        );
449    }
450}