1use std::{
7 borrow::Cow,
8 collections::HashMap,
9 fmt::{Debug, Display},
10};
11
12use async_graphql::{
13 parser::types::Field, registry::Registry, resolver_utils::resolve_list, ContextSelectionSet, InputType,
14 InputValueError, InputValueResult, OutputType, Positioned, ServerResult, Value,
15};
16
17use crate::error::{err, Error, GenericErrorCode, MapToErr};
18
19#[derive(Debug, Clone, Default)]
21pub struct GraphQLMap<E: GraphQLMapEntry>(Vec<E>);
22
23pub trait GraphQLMapEntry {
25 type Key: Eq + std::hash::Hash;
26 type Item;
27
28 fn new(key: Self::Key, value: Self::Item) -> Self;
29 fn into_parts(self) -> (Self::Key, Self::Item);
30}
31
32#[macro_export]
65macro_rules! map_entry_for {
66 ($(#[$attr:meta])* $name:ident) => {
67 $crate::map_entry_for!{ $(#[$attr])* $name { key = String } }
68 };
69
70 ($(#[$attr:meta])* $name:ident { key = $key:ty }) => {
71 $crate::crates::paste::paste! {
72 $(#[$attr])*
73 pub struct [<$name Entry>] {
74 pub key: $key,
76 pub value: $name,
78 }
79 impl $crate::graphql::GraphQLMapEntry for [<$name Entry>] {
80 type Key = $key;
81 type Item = $name;
82
83 fn new(key: Self::Key, value: Self::Item) -> Self {
84 [<$name Entry>] { key, value }
85 }
86
87 fn into_parts(self) -> (Self::Key, Self::Item) {
88 (self.key, self.value)
89 }
90 }
91 }
92 };
93
94 ($(#[$attr:meta])* $name:ident { entry = $entry:ident }) => {
95 $crate::map_entry_for!{ $(#[$attr])* $name { entry = $entry, key = String } }
96 };
97
98 ($(#[$attr:meta])* $name:ident { key = $key:ty, entry = $entry:ident }) => {
99 $crate::map_entry_for!{ $(#[$attr])* $name { entry = $entry, key = $key } }
100 };
101
102 ($(#[$attr:meta])* $name:ident { entry = $entry:ident, key = $key:ty }) => {
103 $(#[$attr])*
104 pub struct $entry {
105 pub key: $key,
107 pub value: $name,
109 }
110 impl $crate::graphql::GraphQLMapEntry for $entry {
111 type Key = $key;
112 type Item = $name;
113
114 fn new(key: Self::Key, value: Self::Item) -> Self {
115 $entry { key, value }
116 }
117
118 fn into_parts(self) -> (Self::Key, Self::Item) {
119 (self.key, self.value)
120 }
121 }
122 };
123}
124
125impl<E, K, T> From<HashMap<K, T>> for GraphQLMap<E>
126where
127 E: GraphQLMapEntry,
128 K: Into<<E as GraphQLMapEntry>::Key>,
129 T: Into<<E as GraphQLMapEntry>::Item>,
130{
131 fn from(map: HashMap<K, T>) -> Self {
132 GraphQLMap(map.into_iter().map(|(k, v)| E::new(k.into(), v.into())).collect())
133 }
134}
135
136impl<E> GraphQLMap<E>
137where
138 E: GraphQLMapEntry,
139{
140 pub fn try_from<K, V>(map: HashMap<K, V>) -> Result<Self, Box<Error>>
142 where
143 K: Eq + std::hash::Hash + Display,
144 K: TryInto<<E as GraphQLMapEntry>::Key>,
145 <K as TryInto<<E as GraphQLMapEntry>::Key>>::Error: Display + Send + Sync + 'static,
146 V: TryInto<<E as GraphQLMapEntry>::Item>,
147 <V as TryInto<<E as GraphQLMapEntry>::Item>>::Error: Display + Send + Sync + 'static,
148 {
149 let mut vec = Vec::with_capacity(map.len());
150 for (key, value) in map.into_iter() {
151 let key = key.try_into().map_to_internal_err("Invalid map key")?;
152 let value = value.try_into().map_to_internal_err("Invalid map value")?;
153
154 vec.push(E::new(key, value));
155 }
156 Ok(GraphQLMap(vec))
157 }
158}
159
160impl<E, K, V> TryFrom<GraphQLMap<E>> for HashMap<K, V>
161where
162 E: GraphQLMapEntry,
163 K: Eq + std::hash::Hash + Display,
164 <E as GraphQLMapEntry>::Key: TryInto<K>,
165 <<E as GraphQLMapEntry>::Key as TryInto<K>>::Error: Display + Send + Sync + 'static,
166 <E as GraphQLMapEntry>::Item: TryInto<V>,
167 <<E as GraphQLMapEntry>::Item as TryInto<V>>::Error: Display + Send + Sync + 'static,
168{
169 type Error = Box<Error>;
170
171 fn try_from(value: GraphQLMap<E>) -> Result<Self, Self::Error> {
172 let mut map = HashMap::<K, V>::with_capacity(value.0.len());
173 for e in value.0.into_iter() {
174 let (key, value) = e.into_parts();
175
176 let key = key
177 .try_into()
178 .map_to_err_with(GenericErrorCode::BadRequest, "Invalid map key")?;
179 let value = value
180 .try_into()
181 .map_to_err_with(GenericErrorCode::BadRequest, "Invalid map value")?;
182
183 #[allow(clippy::map_entry)] if map.contains_key(&key) {
185 return Err(err!(GenericErrorCode::BadRequest, "Duplicated key: {}", key));
186 } else {
187 map.insert(key, value);
188 }
189 }
190 Ok(map)
191 }
192}
193
194impl<T: OutputType + GraphQLMapEntry> OutputType for GraphQLMap<T> {
195 fn type_name() -> Cow<'static, str> {
196 Cow::Owned(format!("[{}]", T::qualified_type_name()))
197 }
198
199 fn qualified_type_name() -> String {
200 format!("[{}]!", T::qualified_type_name())
201 }
202
203 fn create_type_info(registry: &mut Registry) -> String {
204 T::create_type_info(registry);
205 Self::qualified_type_name()
206 }
207
208 async fn resolve(&self, ctx: &ContextSelectionSet<'_>, field: &Positioned<Field>) -> ServerResult<Value> {
209 resolve_list(ctx, field, &self.0, Some(self.0.len())).await
210 }
211}
212impl<T: InputType + GraphQLMapEntry> InputType for GraphQLMap<T> {
213 type RawValueType = Self;
214
215 fn type_name() -> Cow<'static, str> {
216 Cow::Owned(format!("[{}]", T::qualified_type_name()))
217 }
218
219 fn qualified_type_name() -> String {
220 format!("[{}]!", T::qualified_type_name())
221 }
222
223 fn create_type_info(registry: &mut Registry) -> String {
224 T::create_type_info(registry);
225 Self::qualified_type_name()
226 }
227
228 fn parse(value: Option<Value>) -> InputValueResult<Self> {
229 match value.unwrap_or_default() {
230 Value::List(values) => {
231 let list: Vec<_> = values
232 .into_iter()
233 .map(|value| InputType::parse(Some(value)))
234 .collect::<Result<_, _>>()
235 .map_err(InputValueError::propagate)?;
236
237 Ok(GraphQLMap(list))
238 }
239 value => {
240 let list = vec![InputType::parse(Some(value)).map_err(InputValueError::propagate)?];
241 Ok(GraphQLMap(list))
242 }
243 }
244 }
245
246 fn to_value(&self) -> Value {
247 Value::List(self.0.iter().map(InputType::to_value).collect())
248 }
249
250 fn as_raw_value(&self) -> Option<&Self::RawValueType> {
251 Some(self)
252 }
253}