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 pub(crate) fn into_nullable(self) -> Self {
92 match self {
93 Ty::NonNull(inner) => *inner,
94 other => other,
95 }
96 }
97}
98
99impl DocumentBuilder<'_> {
100 pub fn ty(&mut self) -> ArbitraryResult<Ty> {
102 self.generate_ty(true)
103 }
104
105 pub fn choose_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
107 self.choose_ty_given_nullable(existing_types, true)
108 }
109
110 pub fn choose_named_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
112 let used_type_names: Vec<&Ty> = existing_types
113 .iter()
114 .chain(BUILTIN_SCALAR_NAMES.iter())
115 .collect();
116
117 Ok(self.u.choose(&used_type_names)?.to_owned().clone())
118 }
119
120 fn choose_ty_given_nullable(
121 &mut self,
122 existing_types: &[Ty],
123 is_nullable: bool,
124 ) -> ArbitraryResult<Ty> {
125 let ty: Ty = match self.u.int_in_range(0..=2usize)? {
126 0 => {
128 let used_type_names: Vec<&Ty> = existing_types
129 .iter()
130 .chain(BUILTIN_SCALAR_NAMES.iter())
131 .collect();
132
133 self.u.choose(&used_type_names)?.to_owned().clone()
134 }
135 1 => Ty::List(Box::new(
137 self.choose_ty_given_nullable(existing_types, true)?,
138 )),
139 2 => {
141 if is_nullable {
142 Ty::NonNull(Box::new(
143 self.choose_ty_given_nullable(existing_types, false)?,
144 ))
145 } else {
146 self.choose_ty_given_nullable(existing_types, is_nullable)?
147 }
148 }
149 _ => unreachable!(),
150 };
151
152 Ok(ty)
153 }
154
155 fn generate_ty(&mut self, is_nullable: bool) -> ArbitraryResult<Ty> {
156 let ty = match self.u.int_in_range(0..=2usize)? {
157 0 => Ty::Named(self.name()?),
159 1 => Ty::List(Box::new(self.generate_ty(true)?)),
161 2 => {
163 if is_nullable {
164 Ty::NonNull(Box::new(self.generate_ty(false)?))
165 } else {
166 self.generate_ty(is_nullable)?
167 }
168 }
169 _ => unreachable!(),
170 };
171
172 Ok(ty)
173 }
174
175 pub(crate) fn list_existing_types(&self) -> Vec<Ty> {
177 self.object_type_defs
178 .iter()
179 .map(|o| Ty::Named(o.name.clone()))
180 .chain(
181 self.enum_type_defs
182 .iter()
183 .map(|e| Ty::Named(e.name.clone())),
184 )
185 .collect()
186 }
187
188 pub(crate) fn list_existing_input_types(&self) -> Vec<Ty> {
193 self.scalar_type_defs
194 .iter()
195 .map(|s| Ty::Named(s.name.clone()))
196 .chain(
197 self.enum_type_defs
198 .iter()
199 .map(|e| Ty::Named(e.name.clone())),
200 )
201 .chain(
202 self.input_object_type_defs
203 .iter()
204 .map(|io| Ty::Named(io.name.clone())),
205 )
206 .collect()
207 }
208
209 pub(crate) fn list_existing_object_types(&self) -> Vec<Ty> {
211 self.object_type_defs
212 .iter()
213 .map(|o| Ty::Named(o.name.clone()))
214 .collect()
215 }
216
217 #[allow(dead_code)]
218 pub(crate) fn generate_value_for_type(&mut self, _ty: &Ty) -> InputValue {
219 todo!()
220 }
221}