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