1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::DocumentBuilder;
6use apollo_compiler::ast;
7use apollo_compiler::Node;
8use arbitrary::Result;
9use indexmap::IndexMap;
10use indexmap::IndexSet;
11use std::hash::Hash;
12
13#[derive(Debug, Clone)]
20pub struct EnumTypeDef {
21 pub(crate) description: Option<Description>,
22 pub(crate) name: Name,
23 pub(crate) directives: IndexMap<Name, Directive>,
24 pub(crate) enum_values_def: IndexSet<EnumValueDefinition>,
25 pub(crate) extend: bool,
26}
27
28impl From<EnumTypeDef> for ast::Definition {
29 fn from(x: EnumTypeDef) -> Self {
30 if x.extend {
31 ast::EnumTypeExtension {
32 name: x.name.into(),
33 directives: Directive::to_ast(x.directives),
34 values: x
35 .enum_values_def
36 .into_iter()
37 .map(|x| Node::new(x.into()))
38 .collect(),
39 }
40 .into()
41 } else {
42 ast::EnumTypeDefinition {
43 description: x.description.map(Into::into),
44 name: x.name.into(),
45 directives: Directive::to_ast(x.directives),
46 values: x
47 .enum_values_def
48 .into_iter()
49 .map(|x| Node::new(x.into()))
50 .collect(),
51 }
52 .into()
53 }
54 }
55}
56
57impl TryFrom<apollo_parser::cst::EnumTypeDefinition> for EnumTypeDef {
58 type Error = crate::FromError;
59
60 fn try_from(
61 enum_def: apollo_parser::cst::EnumTypeDefinition,
62 ) -> std::result::Result<Self, Self::Error> {
63 Ok(Self {
64 description: enum_def
65 .description()
66 .and_then(|d| d.string_value())
67 .map(|s| Description::from(Into::<String>::into(s))),
68 name: enum_def.name().unwrap().into(),
69 directives: enum_def
70 .directives()
71 .map(Directive::convert_directives)
72 .transpose()?
73 .unwrap_or_default(),
74 enum_values_def: enum_def
75 .enum_values_definition()
76 .expect("must have enum values definition")
77 .enum_value_definitions()
78 .map(EnumValueDefinition::try_from)
79 .collect::<std::result::Result<_, _>>()?,
80 extend: false,
81 })
82 }
83}
84
85impl TryFrom<apollo_parser::cst::EnumTypeExtension> for EnumTypeDef {
86 type Error = crate::FromError;
87
88 fn try_from(
89 enum_def: apollo_parser::cst::EnumTypeExtension,
90 ) -> std::result::Result<Self, Self::Error> {
91 Ok(Self {
92 description: None,
93 name: enum_def.name().unwrap().into(),
94 directives: enum_def
95 .directives()
96 .map(Directive::convert_directives)
97 .transpose()?
98 .unwrap_or_default(),
99 enum_values_def: enum_def
100 .enum_values_definition()
101 .expect("must have enum values definition")
102 .enum_value_definitions()
103 .map(EnumValueDefinition::try_from)
104 .collect::<std::result::Result<_, _>>()?,
105 extend: true,
106 })
107 }
108}
109
110#[derive(Debug, Clone)]
117pub struct EnumValueDefinition {
118 pub(crate) description: Option<Description>,
119 pub(crate) value: Name,
120 pub(crate) directives: IndexMap<Name, Directive>,
121}
122
123impl From<EnumValueDefinition> for ast::EnumValueDefinition {
124 fn from(x: EnumValueDefinition) -> Self {
125 Self {
126 description: x.description.map(Into::into),
127 value: x.value.into(),
128 directives: Directive::to_ast(x.directives),
129 }
130 }
131}
132
133impl TryFrom<apollo_parser::cst::EnumValueDefinition> for EnumValueDefinition {
134 type Error = crate::FromError;
135
136 fn try_from(
137 enum_value_def: apollo_parser::cst::EnumValueDefinition,
138 ) -> std::result::Result<Self, Self::Error> {
139 Ok(Self {
140 description: enum_value_def.description().map(Description::from),
141 value: enum_value_def
142 .enum_value()
143 .expect("enum value def must have enum value")
144 .name()
145 .expect("enum value mus have a name")
146 .into(),
147 directives: enum_value_def
148 .directives()
149 .map(Directive::convert_directives)
150 .transpose()?
151 .unwrap_or_default(),
152 })
153 }
154}
155
156impl PartialEq for EnumValueDefinition {
157 fn eq(&self, other: &Self) -> bool {
158 self.value == other.value
159 }
160}
161
162impl Eq for EnumValueDefinition {}
163
164impl Hash for EnumValueDefinition {
165 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166 self.value.hash(state);
167 }
168}
169
170impl DocumentBuilder<'_> {
171 pub fn enum_type_definition(&mut self) -> Result<EnumTypeDef> {
173 let extend = !self.enum_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
174 let description = self
175 .u
176 .arbitrary()
177 .unwrap_or(false)
178 .then(|| self.description())
179 .transpose()?;
180 let name = if extend {
181 let available_enums: Vec<&Name> = self
182 .enum_type_defs
183 .iter()
184 .filter_map(|enm| if enm.extend { None } else { Some(&enm.name) })
185 .collect();
186 (*self.u.choose(&available_enums)?).clone()
187 } else {
188 self.type_name()?
189 };
190 let enum_values_def = self.enum_values_definition()?;
191 let directives = self.directives(DirectiveLocation::Enum)?;
192
193 Ok(EnumTypeDef {
194 description,
195 name,
196 enum_values_def,
197 directives,
198 extend,
199 })
200 }
201
202 pub fn choose_enum(&mut self) -> Result<&EnumTypeDef> {
204 self.u.choose(&self.enum_type_defs)
205 }
206
207 pub fn arbitrary_variant<'b>(&mut self, enum_: &'b EnumTypeDef) -> Result<&'b Name> {
209 let arbitrary_idx = self.u.int_in_range(0..=(enum_.enum_values_def.len() - 1))?;
210 Ok(enum_
211 .enum_values_def
212 .iter()
213 .nth(arbitrary_idx)
214 .map(|e| &e.value)
215 .expect("cannot get variant"))
216 }
217
218 pub fn enum_values_definition(&mut self) -> Result<IndexSet<EnumValueDefinition>> {
220 let mut enum_values_def = IndexSet::with_capacity(self.u.int_in_range(2..=10usize)?);
221 for i in 0..self.u.int_in_range(2..=10usize)? {
222 let description = self
223 .u
224 .arbitrary()
225 .unwrap_or(false)
226 .then(|| self.description())
227 .transpose()?;
228 let value = self.name_with_index(i)?;
229 let directives = self.directives(DirectiveLocation::EnumValue)?;
230
231 enum_values_def.insert(EnumValueDefinition {
232 description,
233 value,
234 directives,
235 });
236 }
237
238 Ok(enum_values_def)
239 }
240}