1mod boolean;
2mod id;
3mod list;
4mod number;
5mod object;
6mod optional;
7mod string;
8
9use std::{collections::BTreeMap, sync::Arc};
10
11use async_trait::async_trait;
12use futures_util::{
13 future::{try_join_all, BoxFuture},
14 FutureExt,
15};
16use graphql_parser::query::{Selection, TypeCondition};
17
18use crate::{
19 context::{Context, SelectionSetContext},
20 GqlDirective, GqlError, GqlValue, ResolverResult, TypeDefinition,
21};
22
23#[async_trait]
24pub trait SelectionSetResolver: CollectFields {
25 async fn resolve_selection_set(
26 &self,
27 ctx: &SelectionSetContext<'_>,
28 ) -> ResolverResult<GqlValue>;
29}
30
31#[async_trait]
32pub trait CollectFields: FieldResolver {
33 fn introspection_type_name(&self) -> String {
34 Self::type_name()
35 }
36
37 fn collect_all_fields<'a, 'ctx: 'a>(
38 &'a self,
39 ctx: &SelectionSetContext<'ctx>,
40 fields: &mut Fields<'a>,
41 ) -> ResolverResult<()> {
42 fields.collect_fields(ctx, self)
43 }
44}
45
46#[async_trait]
47pub trait FieldResolver: Send + Sync {
48 async fn resolve_field(&self, ctx: &Context<'_>) -> ResolverResult<Option<GqlValue>>;
49
50 fn type_name() -> String;
51}
52
53#[async_trait::async_trait]
54impl<T: FieldResolver> FieldResolver for &T {
55 #[allow(clippy::trivially_copy_pass_by_ref)]
56 async fn resolve_field(&self, ctx: &Context<'_>) -> ResolverResult<Option<GqlValue>> {
57 T::resolve_field(*self, ctx).await
58 }
59 fn type_name() -> String {
60 T::type_name()
61 }
62}
63
64impl<T: FieldResolver> CollectFields for &T {}
65
66#[async_trait::async_trait]
67impl<T: FieldResolver> FieldResolver for Arc<T> {
68 #[allow(clippy::trivially_copy_pass_by_ref)]
69 async fn resolve_field(&self, ctx: &Context<'_>) -> ResolverResult<Option<GqlValue>> {
70 T::resolve_field(self, ctx).await
71 }
72 fn type_name() -> String {
73 T::type_name()
74 }
75}
76
77impl<T: FieldResolver> CollectFields for Arc<T> {}
78
79#[async_trait::async_trait]
80impl<T: FieldResolver> FieldResolver for Box<T> {
81 #[allow(clippy::trivially_copy_pass_by_ref)]
82 async fn resolve_field(&self, ctx: &Context<'_>) -> ResolverResult<Option<GqlValue>> {
83 T::resolve_field(self, ctx).await
84 }
85 fn type_name() -> String {
86 T::type_name()
87 }
88}
89
90impl<T: FieldResolver> CollectFields for Box<T> {}
91
92pub async fn resolve_selection_parallelly<'a, 'ctx: 'a, T: CollectFields + SelectionSetResolver>(
93 ctx: &SelectionSetContext<'ctx>,
94 root_type: &'a T,
95) -> ResolverResult<GqlValue> {
96 resolve_selection(ctx, root_type, true).await
97}
98
99pub async fn resolve_selection_serially<'a, 'ctx: 'a, T: CollectFields + SelectionSetResolver>(
100 ctx: &SelectionSetContext<'ctx>,
101 root_type: &'a T,
102) -> ResolverResult<GqlValue> {
103 resolve_selection(ctx, root_type, false).await
104}
105async fn resolve_selection<'a, 'ctx: 'a, T: CollectFields + SelectionSetResolver>(
106 ctx: &SelectionSetContext<'ctx>,
107 root_type: &'a T,
108 parallel: bool,
109) -> ResolverResult<GqlValue> {
110 let mut fields = Fields(Vec::new());
111 fields.collect_fields(ctx, root_type)?;
112
113 let res = if parallel {
114 try_join_all(fields.0).await?
115 } else {
116 let mut results = Vec::new();
117 for resolver in fields.0 {
118 results.push(resolver.await?);
119 }
120 results
121 };
122
123 let mut gql_obj_map = BTreeMap::new();
124
125 for value in res {
126 build_gql_object(&mut gql_obj_map, value);
127 }
128
129 Ok(GqlValue::Object(gql_obj_map))
130}
131
132fn build_gql_object(target_obj: &mut BTreeMap<String, GqlValue>, gql_value: (String, GqlValue)) {
133 let (field_name, value) = gql_value;
134 if let Some(prev_value) = target_obj.get_mut(&field_name) {
135 match prev_value {
136 GqlValue::List(target_list) => {
137 if let GqlValue::List(list) = value {
138 for (index, v) in list.into_iter().enumerate() {
139 if let Some(GqlValue::Object(prev_obj)) = target_list.get_mut(index) {
140 if let GqlValue::Object(new_obj) = v {
141 for (key, value) in new_obj.into_iter() {
142 build_gql_object(prev_obj, (key, value))
143 }
144 }
145 }
146 }
147 }
148 }
149 GqlValue::Object(prev_obj) => {
150 if let GqlValue::Object(obj) = value {
151 for map in obj.into_iter() {
152 build_gql_object(prev_obj, (map.0, map.1))
153 }
154 }
155 }
156 _ => {}
157 }
158 } else {
159 target_obj.insert(field_name, value.clone());
160 }
161}
162
163pub type ResolveFieldFuture<'a> = BoxFuture<'a, ResolverResult<(String, GqlValue)>>;
164pub struct Fields<'a>(Vec<ResolveFieldFuture<'a>>);
165
166impl<'a> Fields<'a> {
167 pub fn collect_fields<'ctx: 'a, T: CollectFields + ?Sized>(
168 &mut self,
169 ctx: &SelectionSetContext<'ctx>,
170 root_type: &'a T,
171 ) -> ResolverResult<()> {
172 for item in &ctx.item.items {
173 match &item {
174 Selection::Field(field) => {
175 if ctx.is_skip(&field.directives) {
176 continue;
177 }
178 if field.name == "__typename" {
179 ctx.with_field(field);
180 let field_name = field.name.clone();
181 let type_name = root_type.introspection_type_name();
182
183 self.0.push(Box::pin(async move {
184 Ok((field_name, GqlValue::String(type_name)))
185 }));
186 continue;
187 }
188
189 self.0.push(Box::pin({
190 let ctx = ctx.clone();
191 async move {
192 let ctx_field = &ctx.with_field(field);
193 let field_name = ctx_field.item.name.clone();
194 let type_name = T::type_name();
195 let empty_vec = vec![];
196
197 let query_directives = &field.directives;
198 let schema_ty_directives = ctx
199 .schema
200 .type_definitions
201 .get(&type_name)
202 .map(|ty_def| ty_def.directives())
203 .unwrap_or_else(|| empty_vec.as_slice());
204 let schema_field_directives = ctx
205 .schema
206 .type_definitions
207 .get(&type_name)
208 .map(|ty_def| ty_def.field_directives(&field_name))
209 .unwrap_or_default();
210 let schema_impl_interface_directives = ctx
211 .schema
212 .type_definitions
213 .get(&type_name)
214 .map(|ty_def| ty_def.impl_interface_directives(ctx.schema))
215 .unwrap_or_default();
216 let resolve_fut = root_type.resolve_field(ctx_field);
217
218 if schema_ty_directives.is_empty()
219 && schema_field_directives.is_empty()
220 && schema_impl_interface_directives.is_empty()
221 && query_directives.is_empty()
222 {
223 Ok((
224 field_name,
225 root_type
226 .resolve_field(ctx_field)
227 .await?
228 .unwrap_or_default(),
229 ))
230 } else {
231 let mut resolve_fut = resolve_fut.boxed();
232
233 for directive in query_directives {
234 if let Some(custom_dir) =
235 ctx.schema.custom_directives.get(directive.name.as_str())
236 {
237 resolve_fut = Box::pin({
238 let directive = GqlDirective::from(directive.clone());
239 let ctx = ctx_field.clone();
240 async move {
241 custom_dir
242 .resolve_field(
243 &ctx,
244 &directive.arguments,
245 &mut resolve_fut,
246 )
247 .await
248 }
249 })
250 }
251 }
252
253 for directive in schema_ty_directives {
254 if let Some(custom_dir) =
255 ctx.schema.custom_directives.get(directive.name.as_str())
256 {
257 resolve_fut = Box::pin({
258 let ctx = ctx_field.clone();
259 async move {
260 custom_dir
261 .resolve_field(
262 &ctx,
263 &directive.arguments,
264 &mut resolve_fut,
265 )
266 .await
267 }
268 })
269 }
270 }
271
272 for directive in &schema_field_directives {
273 if let Some(custom_dir) =
274 ctx.schema.custom_directives.get(directive.name.as_str())
275 {
276 resolve_fut = Box::pin({
277 let ctx = ctx_field.clone();
278 async move {
279 custom_dir
280 .resolve_field(
281 &ctx,
282 &directive.arguments,
283 &mut resolve_fut,
284 )
285 .await
286 }
287 })
288 }
289 }
290
291 for directive in &schema_impl_interface_directives {
292 if let Some(custom_dir) =
293 ctx.schema.custom_directives.get(directive.name.as_str())
294 {
295 resolve_fut = Box::pin({
296 let ctx = ctx_field.clone();
297 async move {
298 custom_dir
299 .resolve_field(
300 &ctx,
301 &directive.arguments,
302 &mut resolve_fut,
303 )
304 .await
305 }
306 })
307 }
308 }
309 Ok((field_name, resolve_fut.await?.unwrap_or_default()))
310 }
311 }
312 }))
313 }
314 Selection::FragmentSpread(fragment_spread) => {
315 let operation_fragment = ctx
316 .operation
317 .fragment_definitions
318 .get(&fragment_spread.fragment_name);
319 let fragment_def = match operation_fragment {
320 Some(fragment) => fragment,
321 None => {
322 return Err(GqlError::new(
323 format!("{:?} is not defined in query", fragment_spread),
324 Some(fragment_spread.position),
325 ))
326 }
327 };
328
329 if is_fragment_condition(
330 ctx,
331 &root_type.introspection_type_name(),
332 Some(&fragment_def.type_condition),
333 ) {
334 root_type.collect_all_fields(
335 &ctx.with_selection_set(&fragment_def.selection_set),
336 self,
337 )?;
338 }
339 }
340 Selection::InlineFragment(inline_fragment) => {
341 if ctx.is_skip(&inline_fragment.directives) {
342 continue;
343 }
344
345 if is_fragment_condition(
346 ctx,
347 &root_type.introspection_type_name(),
348 inline_fragment.type_condition.as_ref(),
349 ) {
350 root_type.collect_all_fields(
351 &ctx.with_selection_set(&inline_fragment.selection_set),
352 self,
353 )?;
354 } else if inline_fragment.type_condition.is_none() {
355 self.collect_fields(
356 &ctx.with_selection_set(&inline_fragment.selection_set),
357 root_type,
358 )?;
359 }
360 }
361 }
362 }
363 Ok(())
364 }
365}
366
367fn is_fragment_condition<'a, 'ctx: 'a>(
368 ctx: &SelectionSetContext<'ctx>,
369 type_name: &str,
370 ty_cond: Option<&TypeCondition<'a, String>>,
371) -> bool {
372 match ty_cond {
373 Some(cond) => {
374 let TypeCondition::On(on_type) = cond;
375 let is_on_type_name = on_type == type_name;
376 let is_impl_interface =
377 ctx.schema
378 .type_definitions
379 .get(type_name)
380 .map_or(false, |ty_def| {
381 if let TypeDefinition::Object(obj) = ty_def {
382 obj.implements_interfaces.contains(on_type)
383 } else {
384 false
385 }
386 });
387 is_on_type_name || is_impl_interface
388 }
389 None => false,
390 }
391}