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
318 .u
319 .arbitrary()
320 .unwrap_or(false)
321 .then(|| self.input_value_for_type(&ty))
322 .transpose()?;
323
324 if !exclude.contains(&name) {
325 input_values.push(InputValueDef {
326 description,
327 name,
328 ty,
329 default_value,
330 directives,
331 });
332 }
333 }
334
335 Ok(input_values)
336 }
337 pub fn input_value_def(
341 &mut self,
342 directive_location: DirectiveLocation,
343 ) -> ArbitraryResult<InputValueDef> {
344 let description = self
345 .u
346 .arbitrary()
347 .unwrap_or(false)
348 .then(|| self.description())
349 .transpose()?;
350 let name = self.name()?;
351 let ty = self.choose_ty(&self.list_existing_input_types())?;
352 let directives = self.directives(directive_location)?;
353 let default_value = self
354 .u
355 .arbitrary()
356 .unwrap_or(false)
357 .then(|| self.input_value_for_type(&ty))
358 .transpose()?;
359
360 Ok(InputValueDef {
361 description,
362 name,
363 ty,
364 default_value,
365 directives,
366 })
367 }
368
369 fn finite_f64(&mut self) -> arbitrary::Result<f64> {
370 loop {
371 let val: f64 = self.u.arbitrary()?;
372 if val.is_finite() {
373 return Ok(val);
374 }
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use crate::InputObjectTypeDef;
383 use arbitrary::Unstructured;
384 use indexmap::IndexMap;
385
386 #[test]
387 fn test_input_value_for_type() {
388 let data: Vec<u8> = (0..=5000usize).map(|n| (n % 255) as u8).collect();
389 let mut u = Unstructured::new(&data);
390 let mut document_builder = DocumentBuilder::new(&mut u);
391 let my_nested_type = InputObjectTypeDef {
392 description: None,
393 name: Name {
394 name: String::from("my_nested_object"),
395 },
396 directives: IndexMap::new(),
397 fields: vec![InputValueDef {
398 description: None,
399 name: Name {
400 name: String::from("value"),
401 },
402 ty: Ty::Named(Name {
403 name: String::from("String"),
404 }),
405 default_value: None,
406 directives: IndexMap::new(),
407 }],
408 extend: false,
409 };
410
411 let my_object_type = InputObjectTypeDef {
412 description: None,
413 name: Name {
414 name: String::from("my_object"),
415 },
416 directives: IndexMap::new(),
417 fields: vec![InputValueDef {
418 description: None,
419 name: Name {
420 name: String::from("first"),
421 },
422 ty: Ty::List(Box::new(Ty::Named(Name {
423 name: String::from("my_nested_object"),
424 }))),
425 default_value: None,
426 directives: IndexMap::new(),
427 }],
428 extend: false,
429 };
430 document_builder.input_object_type_defs.push(my_nested_type);
431 document_builder.input_object_type_defs.push(my_object_type);
432
433 let input_val = document_builder
434 .input_value_for_type(&Ty::List(Box::new(Ty::Named(Name {
435 name: String::from("my_object"),
436 }))))
437 .unwrap();
438
439 let input_val_str = apollo_compiler::ast::Value::from(input_val)
440 .serialize()
441 .no_indent()
442 .to_string();
443
444 assert_eq!(
445 input_val_str.as_str(),
446 "[{first: [{value: \"EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJ\"}, {value: \"MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJK\"}]}]"
447 );
448 }
449}