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#[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 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 0 => InputValue::Int(self.u.arbitrary()?),
176 1 => InputValue::Float(self.finite_f64()?),
178 2 => InputValue::String(self.limited_string(40)?),
180 3 => InputValue::Boolean(self.u.arbitrary()?),
182 4 => InputValue::Null,
184 5 => {
186 if !self.enum_type_defs.is_empty() {
187 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 6 => {
196 InputValue::List(
198 (0..self.u.int_in_range(2..=4usize)?)
199 .map(|_| self.input_value(constness))
200 .collect::<ArbitraryResult<Vec<_>>>()?,
201 )
202 }
203 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 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 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 let directives = self.directives(DirectiveLocation::InputFieldDefinition)?;
294 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 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 let directives = self.directives(DirectiveLocation::InputFieldDefinition)?;
325 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}