async_graphql/resolver_utils/
container.rs1use std::{future::Future, pin::Pin, sync::Arc};
2
3use futures_util::{FutureExt, StreamExt as _, stream::FuturesUnordered};
4use indexmap::IndexMap;
5
6use crate::{
7 Context, ContextBase, ContextSelectionSet, Error, Name, OutputType, ServerError, ServerResult,
8 Value, extensions::ResolveInfo, parser::types::Selection,
9};
10
11#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
16pub trait ContainerType: OutputType {
17 #[doc(hidden)]
19 fn is_empty() -> bool {
20 false
21 }
22
23 #[cfg(feature = "boxed-trait")]
28 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>>;
29
30 #[cfg(not(feature = "boxed-trait"))]
35 fn resolve_field(
36 &self,
37 ctx: &Context<'_>,
38 ) -> impl Future<Output = ServerResult<Option<Value>>> + Send;
39
40 fn collect_all_fields<'a>(
46 &'a self,
47 ctx: &ContextSelectionSet<'a>,
48 fields: &mut Fields<'a>,
49 ) -> ServerResult<()>
50 where
51 Self: Send + Sync,
52 {
53 fields.add_set(ctx, self)
54 }
55
56 #[cfg(feature = "boxed-trait")]
60 async fn find_entity(&self, _: &Context<'_>, _: &Value) -> ServerResult<Option<Value>> {
61 Ok(None)
62 }
63
64 #[cfg(not(feature = "boxed-trait"))]
68 fn find_entity(
69 &self,
70 _: &Context<'_>,
71 _params: &Value,
72 ) -> impl Future<Output = ServerResult<Option<Value>>> + Send {
73 async { Ok(None) }
74 }
75}
76
77#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
78impl<T: ContainerType + ?Sized> ContainerType for &T {
79 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
80 T::resolve_field(*self, ctx).await
81 }
82
83 async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
84 T::find_entity(*self, ctx, params).await
85 }
86}
87
88#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
89impl<T: ContainerType + ?Sized> ContainerType for Arc<T> {
90 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
91 T::resolve_field(self, ctx).await
92 }
93
94 async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
95 T::find_entity(self, ctx, params).await
96 }
97}
98
99#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
100impl<T: ContainerType + ?Sized> ContainerType for Box<T> {
101 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
102 T::resolve_field(self, ctx).await
103 }
104
105 async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
106 T::find_entity(self, ctx, params).await
107 }
108}
109
110#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
111impl<T: ContainerType, E: Into<Error> + Send + Sync + Clone> ContainerType for Result<T, E> {
112 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
113 match self {
114 Ok(value) => T::resolve_field(value, ctx).await,
115 Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
116 }
117 }
118
119 async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
120 match self {
121 Ok(value) => T::find_entity(value, ctx, params).await,
122 Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
123 }
124 }
125}
126
127pub async fn resolve_container<'a, T: ContainerType + ?Sized>(
129 ctx: &ContextSelectionSet<'a>,
130 root: &'a T,
131) -> ServerResult<Value> {
132 resolve_container_inner(ctx, root, true).await
133}
134
135pub async fn resolve_container_serial<'a, T: ContainerType + ?Sized>(
137 ctx: &ContextSelectionSet<'a>,
138 root: &'a T,
139) -> ServerResult<Value> {
140 resolve_container_inner(ctx, root, false).await
141}
142
143fn insert_value(target: &mut IndexMap<Name, Value>, name: Name, value: Value) {
144 if let Some(prev_value) = target.get_mut(&name) {
145 if let Value::Object(target_map) = prev_value {
146 if let Value::Object(obj) = value {
147 for (key, value) in obj.into_iter() {
148 insert_value(target_map, key, value);
149 }
150 }
151 } else if let Value::List(target_list) = prev_value
152 && let Value::List(list) = value
153 {
154 for (idx, value) in list.into_iter().enumerate() {
155 if let Some(Value::Object(target_map)) = target_list.get_mut(idx)
156 && let Value::Object(obj) = value
157 {
158 for (key, value) in obj.into_iter() {
159 insert_value(target_map, key, value);
160 }
161 }
162 }
163 }
164 } else {
165 target.insert(name, value);
166 }
167}
168
169async fn resolve_container_inner<'a, T: ContainerType + ?Sized>(
170 ctx: &ContextSelectionSet<'a>,
171 root: &'a T,
172 parallel: bool,
173) -> ServerResult<Value> {
174 let mut fields = Fields(Vec::new());
175 fields.add_set(ctx, root)?;
176
177 Ok(do_resolve_container(ctx, parallel, fields.0).await)
178}
179
180pub(crate) async fn do_resolve_container<'a>(
181 ctx: &ContextSelectionSet<'a>,
182 parallel: bool,
183 futures: Vec<BoxFieldFuture<'a>>,
184) -> Value {
185 let mut results = IndexMap::new();
186 let mut handle = |res| match res {
187 Ok((name, value)) => insert_value(&mut results, name, value),
188 Err(e) => ctx.add_error(e),
189 };
190
191 if parallel {
192 let mut set = FuturesUnordered::new();
193 set.extend(futures);
194 while let Some(field) = set.next().await {
195 handle(field);
196 }
197 } else {
198 for fut in futures {
199 handle(fut.await);
200 }
201 }
202
203 if results.is_empty() {
204 Value::Null
205 } else {
206 Value::Object(results)
207 }
208}
209
210type BoxFieldFuture<'a> = Pin<Box<dyn Future<Output = ServerResult<(Name, Value)>> + 'a + Send>>;
211
212pub struct Fields<'a>(Vec<BoxFieldFuture<'a>>);
214
215impl<'a> Fields<'a> {
216 pub fn add_set<T: ContainerType + ?Sized>(
219 &mut self,
220 ctx: &ContextSelectionSet<'a>,
221 root: &'a T,
222 ) -> ServerResult<()> {
223 for selection in &ctx.item.node.items {
224 match &selection.node {
225 Selection::Field(field) => {
226 if field.node.name.node == "__typename" {
227 let ctx_field = ctx.with_field(field);
229 let field_name = ctx_field.item.node.response_key().node.clone();
230 let typename = root.introspection_type_name().into_owned();
231
232 self.0.push(Box::pin(async move {
233 Ok((field_name, Value::String(typename)))
234 }));
235 continue;
236 }
237
238 let resolve_fut = Box::pin({
239 let ctx = ctx.clone();
240 async move {
241 let ctx_field = ctx.with_field(field);
242 let field_name = ctx_field.item.node.response_key().node.clone();
243 let extensions = &ctx.query_env.extensions;
244
245 if extensions.is_empty() && field.node.directives.is_empty() {
246 Ok((
247 field_name,
248 root.resolve_field(&ctx_field).await?.unwrap_or_default(),
249 ))
250 } else {
251 let type_name = T::type_name();
252 let resolve_info = ResolveInfo {
253 path_node: ctx_field.path_node.as_ref().unwrap(),
254 parent_type: &type_name,
255 return_type: match ctx_field
256 .schema_env
257 .registry
258 .types
259 .get(type_name.as_ref())
260 .and_then(|ty| {
261 ty.field_by_name(field.node.name.node.as_str())
262 })
263 .map(|field| &field.ty)
264 {
265 Some(ty) => &ty,
266 None => {
267 return Err(ServerError::new(
268 format!(
269 r#"Cannot query field "{}" on type "{}"."#,
270 field_name, type_name
271 ),
272 Some(ctx_field.item.pos),
273 ));
274 }
275 },
276 name: field.node.name.node.as_str(),
277 alias: field
278 .node
279 .alias
280 .as_ref()
281 .map(|alias| alias.node.as_str()),
282 is_for_introspection: ctx_field.is_for_introspection,
283 field: &field.node,
284 };
285
286 let resolve_fut = root.resolve_field(&ctx_field);
287
288 if field.node.directives.is_empty() {
289 futures_util::pin_mut!(resolve_fut);
290 Ok((
291 field_name,
292 extensions
293 .resolve(resolve_info, &mut resolve_fut)
294 .await?
295 .unwrap_or_default(),
296 ))
297 } else {
298 let mut resolve_fut = resolve_fut.boxed();
299
300 for directive in &field.node.directives {
301 if let Some(directive_factory) = ctx
302 .schema_env
303 .custom_directives
304 .get(directive.node.name.node.as_str())
305 {
306 let ctx_directive = ContextBase {
307 path_node: ctx_field.path_node,
308 is_for_introspection: false,
309 item: directive,
310 schema_env: ctx_field.schema_env,
311 query_env: ctx_field.query_env,
312 execute_data: ctx_field.execute_data,
313 };
314 let directive_instance = directive_factory
315 .create(&ctx_directive, &directive.node)?;
316 resolve_fut = Box::pin({
317 let ctx_field = ctx_field.clone();
318 async move {
319 directive_instance
320 .resolve_field(&ctx_field, &mut resolve_fut)
321 .await
322 }
323 });
324 }
325 }
326
327 Ok((
328 field_name,
329 extensions
330 .resolve(resolve_info, &mut resolve_fut)
331 .await?
332 .unwrap_or_default(),
333 ))
334 }
335 }
336 }
337 });
338
339 self.0.push(resolve_fut);
340 }
341 selection => {
342 let (type_condition, selection_set) = match selection {
343 Selection::Field(_) => unreachable!(),
344 Selection::FragmentSpread(spread) => {
345 let fragment =
346 ctx.query_env.fragments.get(&spread.node.fragment_name.node);
347 let fragment = match fragment {
348 Some(fragment) => fragment,
349 None => {
350 return Err(ServerError::new(
351 format!(
352 r#"Unknown fragment "{}"."#,
353 spread.node.fragment_name.node
354 ),
355 Some(spread.pos),
356 ));
357 }
358 };
359 (
360 Some(&fragment.node.type_condition),
361 &fragment.node.selection_set,
362 )
363 }
364 Selection::InlineFragment(fragment) => (
365 fragment.node.type_condition.as_ref(),
366 &fragment.node.selection_set,
367 ),
368 };
369 let type_condition =
370 type_condition.map(|condition| condition.node.on.node.as_str());
371
372 let introspection_type_name = root.introspection_type_name();
373
374 let applies_concrete_object = type_condition.is_some_and(|condition| {
375 introspection_type_name == condition
376 || ctx
377 .schema_env
378 .registry
379 .implements
380 .get(&*introspection_type_name)
381 .is_some_and(|interfaces| interfaces.contains(condition))
382 });
383 if applies_concrete_object {
384 root.collect_all_fields(&ctx.with_selection_set(selection_set), self)?;
385 } else if type_condition.is_none_or(|condition| T::type_name() == condition) {
386 self.add_set(&ctx.with_selection_set(selection_set), root)?;
388 }
389 }
390 }
391 }
392 Ok(())
393 }
394}