apollo_smith/
enum_.rs

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/// Enums are special scalars that can only have a defined set of values.
14///
15/// *EnumTypeDefinition*:
16///     Description? **enum** Name Directives? EnumValuesDefinition?
17///
18/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Enums).
19#[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/// The __EnumValue type represents one of possible values of an enum.
111///
112/// *EnumValueDefinition*:
113///     Description? EnumValue Directives?
114///
115/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-The-__EnumValue-Type).
116#[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    /// Create an arbitrary `EnumTypeDef`
172    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    /// Choose an arbitrary `EnumTypeDef` in existings (already created) enum definitions
203    pub fn choose_enum(&mut self) -> Result<&EnumTypeDef> {
204        self.u.choose(&self.enum_type_defs)
205    }
206
207    /// Create an arbitrary variant `Name` given an enum
208    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    /// Create an arbitrary `EnumValueDefinition`
219    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}