1use indexmap::IndexMap;
2
3use super::{Directive, directive::to_meta_directive_invocation};
4use crate::{
5 dynamic::SchemaError,
6 registry::{Deprecation, MetaEnumValue, MetaType, Registry},
7};
8
9#[derive(Debug)]
11pub struct EnumItem {
12 pub(crate) name: String,
13 pub(crate) description: Option<String>,
14 pub(crate) deprecation: Deprecation,
15 inaccessible: bool,
16 tags: Vec<String>,
17 pub(crate) directives: Vec<Directive>,
18}
19
20impl<T: Into<String>> From<T> for EnumItem {
21 #[inline]
22 fn from(name: T) -> Self {
23 EnumItem {
24 name: name.into(),
25 description: None,
26 deprecation: Deprecation::NoDeprecated,
27 inaccessible: false,
28 tags: Vec::new(),
29 directives: Vec::new(),
30 }
31 }
32}
33
34impl EnumItem {
35 #[inline]
37 pub fn new(name: impl Into<String>) -> Self {
38 name.into().into()
39 }
40
41 impl_set_description!();
42 impl_set_deprecation!();
43 impl_set_inaccessible!();
44 impl_set_tags!();
45 impl_directive!();
46}
47
48#[derive(Debug)]
50pub struct Enum {
51 pub(crate) name: String,
52 pub(crate) description: Option<String>,
53 pub(crate) enum_values: IndexMap<String, EnumItem>,
54 inaccessible: bool,
55 tags: Vec<String>,
56 pub(crate) directives: Vec<Directive>,
57 requires_scopes: Vec<String>,
58}
59
60impl Enum {
61 #[inline]
63 pub fn new(name: impl Into<String>) -> Self {
64 Self {
65 name: name.into(),
66 description: None,
67 enum_values: Default::default(),
68 inaccessible: false,
69 tags: Vec::new(),
70 directives: Vec::new(),
71 requires_scopes: Vec::new(),
72 }
73 }
74
75 impl_set_description!();
76 impl_directive!();
77
78 #[inline]
80 pub fn item(mut self, item: impl Into<EnumItem>) -> Self {
81 let item = item.into();
82 self.enum_values.insert(item.name.clone(), item);
83 self
84 }
85
86 pub fn items(mut self, items: impl IntoIterator<Item = impl Into<EnumItem>>) -> Self {
88 for item in items {
89 let item = item.into();
90 self.enum_values.insert(item.name.clone(), item);
91 }
92 self
93 }
94
95 impl_set_inaccessible!();
96 impl_set_tags!();
97
98 #[inline]
100 pub fn type_name(&self) -> &str {
101 &self.name
102 }
103
104 pub(crate) fn register(&self, registry: &mut Registry) -> Result<(), SchemaError> {
105 let mut enum_values = IndexMap::new();
106
107 for item in self.enum_values.values() {
108 enum_values.insert(
109 item.name.clone(),
110 MetaEnumValue {
111 name: item.name.as_str().into(),
112 description: item.description.clone(),
113 deprecation: item.deprecation.clone(),
114 visible: None,
115 inaccessible: item.inaccessible,
116 tags: item.tags.clone(),
117 directive_invocations: to_meta_directive_invocation(item.directives.clone()),
118 },
119 );
120 }
121
122 registry.types.insert(
123 self.name.clone(),
124 MetaType::Enum {
125 name: self.name.clone(),
126 description: self.description.clone(),
127 enum_values,
128 visible: None,
129 inaccessible: self.inaccessible,
130 tags: self.tags.clone(),
131 rust_typename: None,
132 directive_invocations: to_meta_directive_invocation(self.directives.clone()),
133 requires_scopes: self.requires_scopes.clone(),
134 },
135 );
136
137 Ok(())
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use crate::{Name, PathSegment, Pos, ServerError, Value, dynamic::*, value};
144
145 #[tokio::test]
146 async fn enum_type() {
147 let my_enum = Enum::new("MyEnum").item("A").item("B");
148
149 let query = Object::new("Query")
150 .field(Field::new(
151 "value",
152 TypeRef::named_nn(my_enum.type_name()),
153 |_| FieldFuture::new(async { Ok(Some(Value::from(Name::new("A")))) }),
154 ))
155 .field(
156 Field::new("value2", TypeRef::named_nn(my_enum.type_name()), |ctx| {
157 FieldFuture::new(async move {
158 Ok(Some(FieldValue::value(Name::new(
159 ctx.args.try_get("input")?.enum_name()?,
160 ))))
161 })
162 })
163 .argument(InputValue::new(
164 "input",
165 TypeRef::named_nn(my_enum.type_name()),
166 )),
167 )
168 .field(Field::new(
169 "errValue",
170 TypeRef::named_nn(my_enum.type_name()),
171 |_| FieldFuture::new(async { Ok(Some(Value::from(Name::new("C")))) }),
172 ));
173 let schema = Schema::build("Query", None, None)
174 .register(my_enum)
175 .register(query)
176 .finish()
177 .unwrap();
178
179 assert_eq!(
180 schema
181 .execute("{ value value2(input: B) }")
182 .await
183 .into_result()
184 .unwrap()
185 .data,
186 value!({
187 "value": "A",
188 "value2": "B"
189 })
190 );
191
192 assert_eq!(
193 schema
194 .execute("{ errValue }")
195 .await
196 .into_result()
197 .unwrap_err(),
198 vec![ServerError {
199 message: "internal: invalid item for enum \"MyEnum\"".to_owned(),
200 source: None,
201 locations: vec![Pos { column: 3, line: 1 }],
202 path: vec![PathSegment::Field("errValue".to_owned())],
203 extensions: None,
204 }]
205 );
206 }
207}