1use crate::input_value::InputValue;
2use crate::name::Name;
3use crate::DocumentBuilder;
4use apollo_compiler::ast;
5use arbitrary::Result as ArbitraryResult;
6use once_cell::sync::Lazy;
7
8static BUILTIN_SCALAR_NAMES: Lazy<[Ty; 5]> = Lazy::new(|| {
9 [
10 Ty::Named(Name::new(String::from("Int"))),
11 Ty::Named(Name::new(String::from("Float"))),
12 Ty::Named(Name::new(String::from("String"))),
13 Ty::Named(Name::new(String::from("Boolean"))),
14 Ty::Named(Name::new(String::from("ID"))),
15 ]
16});
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub enum Ty {
24 Named(Name),
26 List(Box<Ty>),
28 NonNull(Box<Ty>),
30}
31
32impl From<Ty> for ast::Type {
33 fn from(val: Ty) -> Self {
34 match val {
35 Ty::Named(name) => Self::Named(name.into()),
36 Ty::List(ty) => Self::from(*ty).list(),
37 Ty::NonNull(ty) => Self::from(*ty).non_null(),
38 }
39 }
40}
41
42impl From<apollo_parser::cst::Type> for Ty {
43 fn from(ty: apollo_parser::cst::Type) -> Self {
44 match ty {
45 apollo_parser::cst::Type::NamedType(named_ty) => named_ty.into(),
46 apollo_parser::cst::Type::ListType(list_type) => {
47 Self::List(Box::new(list_type.ty().unwrap().into()))
48 }
49 apollo_parser::cst::Type::NonNullType(non_null) => {
50 if let Some(named_ty) = non_null.named_type() {
51 Self::NonNull(Box::new(named_ty.into()))
52 } else if let Some(list_type) = non_null.list_type() {
53 Self::NonNull(Box::new(Self::List(Box::new(
54 list_type.ty().unwrap().into(),
55 ))))
56 } else {
57 panic!("a non null type must have a type")
58 }
59 }
60 }
61 }
62}
63
64impl From<apollo_parser::cst::NamedType> for Ty {
65 fn from(ty: apollo_parser::cst::NamedType) -> Self {
66 Self::Named(ty.name().unwrap().into())
67 }
68}
69
70impl Ty {
71 pub(crate) fn name(&self) -> &Name {
72 match self {
73 Ty::Named(name) => name,
74 Ty::List(list) => list.name(),
75 Ty::NonNull(non_null) => non_null.name(),
76 }
77 }
78
79 pub fn is_named(&self) -> bool {
83 matches!(self, Self::Named(..))
84 }
85
86 pub(crate) fn is_builtin(&self) -> bool {
87 BUILTIN_SCALAR_NAMES.contains(&Ty::Named(self.name().clone()))
88 }
89}
90
91impl DocumentBuilder<'_> {
92 pub fn ty(&mut self) -> ArbitraryResult<Ty> {
94 self.generate_ty(true)
95 }
96
97 pub fn choose_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
99 self.choose_ty_given_nullable(existing_types, true)
100 }
101
102 pub fn choose_named_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
104 let used_type_names: Vec<&Ty> = existing_types
105 .iter()
106 .chain(BUILTIN_SCALAR_NAMES.iter())
107 .collect();
108
109 Ok(self.u.choose(&used_type_names)?.to_owned().clone())
110 }
111
112 fn choose_ty_given_nullable(
113 &mut self,
114 existing_types: &[Ty],
115 is_nullable: bool,
116 ) -> ArbitraryResult<Ty> {
117 let ty: Ty = match self.u.int_in_range(0..=2usize)? {
118 0 => {
120 let used_type_names: Vec<&Ty> = existing_types
121 .iter()
122 .chain(BUILTIN_SCALAR_NAMES.iter())
123 .collect();
124
125 self.u.choose(&used_type_names)?.to_owned().clone()
126 }
127 1 => Ty::List(Box::new(
129 self.choose_ty_given_nullable(existing_types, true)?,
130 )),
131 2 => {
133 if is_nullable {
134 Ty::NonNull(Box::new(
135 self.choose_ty_given_nullable(existing_types, false)?,
136 ))
137 } else {
138 self.choose_ty_given_nullable(existing_types, is_nullable)?
139 }
140 }
141 _ => unreachable!(),
142 };
143
144 Ok(ty)
145 }
146
147 fn generate_ty(&mut self, is_nullable: bool) -> ArbitraryResult<Ty> {
148 let ty = match self.u.int_in_range(0..=2usize)? {
149 0 => Ty::Named(self.name()?),
151 1 => Ty::List(Box::new(self.generate_ty(true)?)),
153 2 => {
155 if is_nullable {
156 Ty::NonNull(Box::new(self.generate_ty(false)?))
157 } else {
158 self.generate_ty(is_nullable)?
159 }
160 }
161 _ => unreachable!(),
162 };
163
164 Ok(ty)
165 }
166
167 pub(crate) fn list_existing_types(&self) -> Vec<Ty> {
169 self.object_type_defs
170 .iter()
171 .map(|o| Ty::Named(o.name.clone()))
172 .chain(
173 self.enum_type_defs
174 .iter()
175 .map(|e| Ty::Named(e.name.clone())),
176 )
177 .collect()
178 }
179
180 pub(crate) fn list_existing_object_types(&self) -> Vec<Ty> {
182 self.object_type_defs
183 .iter()
184 .map(|o| Ty::Named(o.name.clone()))
185 .collect()
186 }
187
188 #[allow(dead_code)]
189 pub(crate) fn generate_value_for_type(&mut self, _ty: &Ty) -> InputValue {
190 todo!()
191 }
192}