juniper/types/
base.rs

1use arcstr::ArcStr;
2use indexmap::IndexMap;
3
4use crate::{
5    FieldResult, GraphQLEnum, IntoFieldError,
6    ast::{Directive, FromInputValue, InputValue, Selection},
7    executor::{ExecutionResult, Executor, Registry, Variables},
8    parser::Spanning,
9    schema::meta::{Argument, MetaType},
10    value::{DefaultScalarValue, Object, ScalarValue, Value},
11};
12
13/// GraphQL type kind
14///
15/// The GraphQL specification defines a number of type kinds - the meta type\
16/// of a type.
17#[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)]
18#[graphql(name = "__TypeKind", internal)]
19pub enum TypeKind {
20    /// ## Scalar types
21    ///
22    /// Scalar types appear as the leaf nodes of GraphQL queries. Strings,\
23    /// numbers, and booleans are the built in types, and while it's possible\
24    /// to define your own, it's relatively uncommon.
25    Scalar,
26
27    /// ## Object types
28    ///
29    /// The most common type to be implemented by users. Objects have fields\
30    /// and can implement interfaces.
31    Object,
32
33    /// ## Interface types
34    ///
35    /// Interface types are used to represent overlapping fields between\
36    /// multiple types, and can be queried for their concrete type.
37    Interface,
38
39    /// ## Union types
40    ///
41    /// Unions are similar to interfaces but can not contain any fields on\
42    /// their own.
43    Union,
44
45    /// ## Enum types
46    ///
47    /// Like scalars, enum types appear as the leaf nodes of GraphQL queries.
48    Enum,
49
50    /// ## Input objects
51    ///
52    /// Represents complex values provided in queries _into_ the system.
53    InputObject,
54
55    /// ## List types
56    ///
57    /// Represent lists of other types. This library provides implementations\
58    /// for vectors and slices, but other Rust types can be extended to serve\
59    /// as GraphQL lists.
60    List,
61
62    /// ## Non-null types
63    ///
64    /// In GraphQL, nullable types are the default. By putting a `!` after a\
65    /// type, it becomes non-nullable.
66    NonNull,
67}
68
69/// Field argument container
70#[derive(Debug)]
71pub struct Arguments<'a, S = DefaultScalarValue> {
72    args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
73}
74
75impl<'a, S> Arguments<'a, S> {
76    #[doc(hidden)]
77    pub fn new(
78        mut args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
79        meta_args: &'a Option<Vec<Argument<S>>>,
80    ) -> Self
81    where
82        S: Clone,
83    {
84        if meta_args.is_some() && args.is_none() {
85            args = Some(IndexMap::new());
86        }
87
88        if let (Some(args), Some(meta_args)) = (&mut args, meta_args) {
89            for arg in meta_args {
90                let arg_name = arg.name.as_str();
91                if args.get(arg_name).is_none() {
92                    if let Some(val) = arg.default_value.as_ref() {
93                        args.insert(arg_name, Spanning::unlocated(val.clone()));
94                    }
95                }
96            }
97        }
98
99        Self { args }
100    }
101
102    /// Gets an argument by the given `name` and converts it into the desired
103    /// type.
104    ///
105    /// If the argument is found, or a default argument has been provided, the
106    /// given [`InputValue`] will be converted into the type `T`.
107    ///
108    /// Returns [`None`] if an argument with such `name` is not present.
109    ///
110    /// # Errors
111    ///
112    /// If the [`FromInputValue`] conversion fails.
113    pub fn get<T>(&self, name: &str) -> FieldResult<Option<T>, S>
114    where
115        T: FromInputValue<S>,
116        T::Error: IntoFieldError<S>,
117    {
118        self.args
119            .as_ref()
120            .and_then(|args| args.get(name))
121            .map(|spanning| &spanning.item)
122            .map(InputValue::convert)
123            .transpose()
124            .map_err(IntoFieldError::into_field_error)
125    }
126
127    /// Gets a direct reference to the [`Spanning`] argument [`InputValue`].
128    pub fn get_input_value(&self, name: &str) -> Option<&Spanning<InputValue<S>>> {
129        self.args.as_ref().and_then(|args| args.get(name))
130    }
131}
132
133/// Primary trait used to resolve GraphQL values.
134///
135/// All the convenience macros ultimately expand into an implementation of this trait for the given
136/// type. The macros remove duplicated definitions of fields and arguments, and add type checks on
137/// all resolving functions automatically. This can all be done manually too.
138///
139/// [`GraphQLValue`] provides _some_ convenience methods for you, in the form of optional trait
140/// methods. The `type_name` method is mandatory, but other than that, it depends on what type
141/// you're exposing:
142/// - [Scalars][4], [enums][5], [lists][6] and [non-null wrappers][7] only require `resolve`.
143/// - [Interfaces][1] and [objects][3] require `resolve_field` _or_ `resolve` if you want to
144///   implement a custom resolution logic (probably not).
145/// - [Interfaces][1] and [unions][2] require `resolve_into_type` and `concrete_type_name`.
146/// - [Input objects][8] do not require anything.
147///
148/// # Object safety
149///
150/// This trait is [object safe][11], therefore may be turned into a [trait object][12] and used for
151/// resolving GraphQL values even when a concrete Rust type is erased.
152///
153/// # Example
154///
155/// This trait is intended to be used in a conjunction with a [`GraphQLType`] trait. See the example
156/// in the documentation of a [`GraphQLType`] trait.
157///
158/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
159/// [2]: https://spec.graphql.org/October2021#sec-Unions
160/// [3]: https://spec.graphql.org/October2021#sec-Objects
161/// [4]: https://spec.graphql.org/October2021#sec-Scalars
162/// [5]: https://spec.graphql.org/October2021#sec-Enums
163/// [6]: https://spec.graphql.org/October2021#sec-List
164/// [7]: https://spec.graphql.org/October2021#sec-Non-Null
165/// [8]: https://spec.graphql.org/October2021#sec-Input-Objects
166/// [11]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
167/// [12]: https://doc.rust-lang.org/reference/types/trait-object.html
168pub trait GraphQLValue<S = DefaultScalarValue>
169where
170    S: ScalarValue,
171{
172    /// Context type for this [`GraphQLValue`].
173    ///
174    /// It's threaded through a query execution to all affected nodes, and can be used to hold
175    /// common data, e.g. database connections or request session information.
176    type Context;
177
178    /// Type that may carry additional schema information for this [`GraphQLValue`].
179    ///
180    /// It can be used to implement a schema that is partly dynamic, meaning that it can use
181    /// information that is not known at compile time, for instance by reading it from a
182    /// configuration file at startup.
183    type TypeInfo;
184
185    /// Returns name of the [`GraphQLType`] exposed by this [`GraphQLValue`].
186    ///
187    /// This function will be called multiple times during a query execution. It must _not_ perform
188    /// any calculation and _always_ return the same value.
189    ///
190    /// Usually, it should just call a [`GraphQLType::name`] inside.
191    fn type_name(&self, info: &Self::TypeInfo) -> Option<ArcStr>;
192
193    /// Resolves the value of a single field on this [`GraphQLValue`].
194    ///
195    /// The `arguments` object contains all the specified arguments, with default values being
196    /// substituted for the ones not provided by the query.
197    ///
198    /// The `executor` can be used to drive selections into sub-[objects][3].
199    ///
200    /// # Panics
201    ///
202    /// The default implementation panics.
203    ///
204    /// [3]: https://spec.graphql.org/October2021#sec-Objects
205    fn resolve_field(
206        &self,
207        _info: &Self::TypeInfo,
208        _field_name: &str,
209        _arguments: &Arguments<S>,
210        _executor: &Executor<Self::Context, S>,
211    ) -> ExecutionResult<S> {
212        panic!("GraphQLValue::resolve_field() must be implemented by objects and interfaces");
213    }
214
215    /// Resolves this [`GraphQLValue`] (being an [interface][1] or an [union][2]) into a concrete
216    /// downstream [object][3] type.
217    ///
218    /// Tries to resolve this [`GraphQLValue`] into the provided `type_name`. If the type matches,
219    /// then passes the instance along to [`Executor::resolve`].
220    ///
221    /// # Panics
222    ///
223    /// The default implementation panics.
224    ///
225    /// [1]: https://spec.graphql.org/October2021#sec-Interfaces
226    /// [2]: https://spec.graphql.org/October2021#sec-Unions
227    /// [3]: https://spec.graphql.org/October2021#sec-Objects
228    fn resolve_into_type(
229        &self,
230        info: &Self::TypeInfo,
231        type_name: &str,
232        selection_set: Option<&[Selection<S>]>,
233        executor: &Executor<Self::Context, S>,
234    ) -> ExecutionResult<S> {
235        if self.type_name(info).unwrap() == type_name {
236            self.resolve(info, selection_set, executor)
237        } else {
238            panic!(
239                "GraphQLValue::resolve_into_type() must be implemented by unions and interfaces"
240            );
241        }
242    }
243
244    /// Returns the concrete [`GraphQLType`] name for this [`GraphQLValue`] being an [interface][1],
245    /// an [union][2] or an [object][3].
246    ///
247    /// # Panics
248    ///
249    /// The default implementation panics.
250    ///
251    /// [1]: https://spec.graphql.org/October2021#sec-Interfaces
252    /// [2]: https://spec.graphql.org/October2021#sec-Unions
253    /// [3]: https://spec.graphql.org/October2021#sec-Objects
254    #[expect(unused_variables, reason = "nice interface declaration")]
255    fn concrete_type_name(&self, context: &Self::Context, info: &Self::TypeInfo) -> String {
256        panic!(
257            "GraphQLValue::concrete_type_name() must be implemented by unions, interfaces \
258             and objects",
259        );
260    }
261
262    /// Resolves the provided `selection_set` against this [`GraphQLValue`].
263    ///
264    /// For non-[object][3] types, the `selection_set` will be [`None`] and the value should simply
265    /// be returned.
266    ///
267    /// For [objects][3], all fields in the `selection_set` should be resolved. The default
268    /// implementation uses [`GraphQLValue::resolve_field`] to resolve all fields, including those
269    /// through a fragment expansion.
270    ///
271    /// Since the [GraphQL spec specifies][0] that errors during field processing should result in
272    /// a null-value, this might return `Ok(Null)` in case of a failure. Errors are recorded
273    /// internally.
274    ///
275    /// # Panics
276    ///
277    /// The default implementation panics, if `selection_set` is [`None`].
278    ///
279    /// [0]: https://spec.graphql.org/October2021#sec-Errors-and-Non-Nullability
280    /// [3]: https://spec.graphql.org/October2021#sec-Objects
281    fn resolve(
282        &self,
283        info: &Self::TypeInfo,
284        selection_set: Option<&[Selection<S>]>,
285        executor: &Executor<Self::Context, S>,
286    ) -> ExecutionResult<S> {
287        if let Some(sel) = selection_set {
288            let mut res = Object::with_capacity(sel.len());
289            Ok(
290                if resolve_selection_set_into(self, info, sel, executor, &mut res) {
291                    Value::Object(res)
292                } else {
293                    Value::null()
294                },
295            )
296        } else {
297            panic!("GraphQLValue::resolve() must be implemented by non-object output types");
298        }
299    }
300}
301
302/// Primary trait used to expose Rust types in a GraphQL schema.
303///
304/// All of the convenience macros ultimately expand into an implementation of
305/// this trait for the given type. This can all be done manually.
306///
307/// # Example
308///
309/// Manually deriving an [object][3] is straightforward, but tedious. This is the equivalent of the
310/// `User` object as shown in the example in the documentation root:
311/// ```
312/// # use std::collections::HashMap;
313/// use juniper::{
314///     arcstr, meta::MetaType, ArcStr, Arguments, Context, DefaultScalarValue, Executor,
315///     ExecutionResult, FieldResult, GraphQLType, GraphQLValue, Registry,
316/// };
317///
318/// #[derive(Debug)]
319/// struct Database { users: HashMap<String, User> }
320/// impl Context for Database {}
321///
322/// #[derive(Debug)]
323/// struct User { id: String, name: String, friend_ids: Vec<String> }
324///
325/// impl GraphQLType<DefaultScalarValue> for User {
326///    fn name(_: &()) -> Option<ArcStr> {
327///        Some(arcstr::literal!("User"))
328///    }
329///
330///    fn meta(_: &(), registry: &mut Registry) -> MetaType {
331///        // First, we need to define all fields and their types on this type.
332///        //
333///        // If we need arguments, want to implement interfaces, or want to add documentation
334///        // strings, we can do it here.
335///        let fields = &[
336///            registry.field::<String>(arcstr::literal!("id"), &()),
337///            registry.field::<String>(arcstr::literal!("name"), &()),
338///            registry.field::<Vec<User>>(arcstr::literal!("friends"), &()),
339///        ];
340///        registry.build_object_type::<User>(&(), fields).into_meta()
341///    }
342/// }
343///
344/// impl GraphQLValue<DefaultScalarValue> for User {
345///     type Context = Database;
346///     type TypeInfo = ();
347///
348///     fn type_name(&self, _: &()) -> Option<ArcStr> {
349///         <User as GraphQLType>::name(&())
350///     }
351///
352///     fn resolve_field(
353///         &self,
354///         info: &(),
355///         field_name: &str,
356///         args: &Arguments,
357///         executor: &Executor<Database>
358///     ) -> ExecutionResult
359///     {
360///         // Next, we need to match the queried field name. All arms of this match statement
361///         // return `ExecutionResult`, which makes it hard to statically verify that the type you
362///         // pass on to `executor.resolve*` actually matches the one that you defined in `meta()`
363///         // above.
364///         let database = executor.context();
365///         match field_name {
366///             // Because scalars are defined with another `Context` associated type, you must use
367///             // `resolve_with_ctx` here to make the `executor` perform automatic type conversion
368///             // of its argument.
369///             "id" => executor.resolve_with_ctx(info, &self.id),
370///             "name" => executor.resolve_with_ctx(info, &self.name),
371///
372///             // You pass a vector of `User` objects to `executor.resolve`, and it will determine
373///             // which fields of the sub-objects to actually resolve based on the query.
374///             // The `executor` instance keeps track of its current position in the query.
375///             "friends" => executor.resolve(info,
376///                 &self.friend_ids.iter()
377///                     .filter_map(|id| database.users.get(id))
378///                     .collect::<Vec<_>>()
379///             ),
380///
381///             // We can only reach this panic in two cases: either a mismatch between the defined
382///             // schema in `meta()` above, or a validation failed because of a this library bug.
383///             //
384///             // In either of those two cases, the only reasonable way out is to panic the thread.
385///             _ => panic!("Field {field_name} not found on type User"),
386///         }
387///     }
388/// }
389/// ```
390///
391/// [3]: https://spec.graphql.org/October2021#sec-Objects
392pub trait GraphQLType<S = DefaultScalarValue>: GraphQLValue<S>
393where
394    S: ScalarValue,
395{
396    /// Returns name of this [`GraphQLType`] to expose.
397    ///
398    /// This function will be called multiple times during schema construction. It must _not_
399    /// perform any calculation and _always_ return the same value.
400    fn name(info: &Self::TypeInfo) -> Option<ArcStr>;
401
402    /// Returns [`MetaType`] representing this [`GraphQLType`].
403    fn meta(info: &Self::TypeInfo, registry: &mut Registry<S>) -> MetaType<S>;
404}
405
406/// Resolver logic for queries'/mutations' selection set.
407/// Calls appropriate resolver method for each field or fragment found
408/// and then merges returned values into `result` or pushes errors to
409/// field's/fragment's sub executor.
410///
411/// Returns false if any errors occurred and true otherwise.
412pub(crate) fn resolve_selection_set_into<T, S>(
413    instance: &T,
414    info: &T::TypeInfo,
415    selection_set: &[Selection<S>],
416    executor: &Executor<T::Context, S>,
417    result: &mut Object<S>,
418) -> bool
419where
420    T: GraphQLValue<S> + ?Sized,
421    S: ScalarValue,
422{
423    let meta_type = executor
424        .schema()
425        .concrete_type_by_name(
426            instance
427                .type_name(info)
428                .expect("Resolving named type's selection set"),
429        )
430        .expect("Type not found in schema");
431
432    for selection in selection_set {
433        match *selection {
434            Selection::Field(Spanning {
435                item: ref f,
436                ref span,
437            }) => {
438                if is_excluded(&f.directives, executor.variables()) {
439                    continue;
440                }
441
442                let response_name = f.alias.as_ref().unwrap_or(&f.name).item;
443
444                if f.name.item == "__typename" {
445                    result.add_field(
446                        response_name,
447                        Value::Scalar(instance.concrete_type_name(executor.context(), info).into()),
448                    );
449                    continue;
450                }
451
452                let meta_field = meta_type.field_by_name(f.name.item).unwrap_or_else(|| {
453                    panic!(
454                        "Field {} not found on type {:?}",
455                        f.name.item,
456                        meta_type.name(),
457                    )
458                });
459
460                let exec_vars = executor.variables();
461
462                let sub_exec = executor.field_sub_executor(
463                    response_name,
464                    f.name.item,
465                    span.start,
466                    f.selection_set.as_ref().map(|v| &v[..]),
467                );
468
469                let field_result = instance.resolve_field(
470                    info,
471                    f.name.item,
472                    &Arguments::new(
473                        f.arguments.as_ref().map(|m| {
474                            m.item
475                                .iter()
476                                .filter_map(|(k, v)| {
477                                    let val = v.item.clone().into_const(exec_vars)?;
478                                    Some((k.item, Spanning::new(v.span, val)))
479                                })
480                                .collect()
481                        }),
482                        &meta_field.arguments,
483                    ),
484                    &sub_exec,
485                );
486
487                match field_result {
488                    Ok(Value::Null) if meta_field.field_type.is_non_null() => return false,
489                    Ok(v) => merge_key_into(result, response_name, v),
490                    Err(e) => {
491                        sub_exec.push_error_at(e, span.start);
492
493                        if meta_field.field_type.is_non_null() {
494                            return false;
495                        }
496
497                        result.add_field(response_name, Value::null());
498                    }
499                }
500            }
501            Selection::FragmentSpread(Spanning {
502                item: ref spread,
503                span,
504            }) => {
505                if is_excluded(&spread.directives, executor.variables()) {
506                    continue;
507                }
508
509                let fragment = &executor
510                    .fragment_by_name(spread.name.item)
511                    .expect("Fragment could not be found");
512
513                let sub_exec = executor.type_sub_executor(
514                    Some(fragment.type_condition.item),
515                    Some(&fragment.selection_set[..]),
516                );
517
518                let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
519                let type_name = instance.type_name(info);
520                if executor
521                    .schema()
522                    .is_named_subtype(&concrete_type_name, fragment.type_condition.item)
523                    || Some(fragment.type_condition.item) == type_name.as_deref()
524                {
525                    let sub_result = instance.resolve_into_type(
526                        info,
527                        &concrete_type_name,
528                        Some(&fragment.selection_set[..]),
529                        &sub_exec,
530                    );
531
532                    if let Ok(Value::Object(object)) = sub_result {
533                        for (k, v) in object {
534                            merge_key_into(result, &k, v);
535                        }
536                    } else {
537                        if let Err(e) = sub_result {
538                            sub_exec.push_error_at(e, span.start);
539                        }
540                        // NOTE: Executing a fragment cannot really result in anything other
541                        //       than `Value::Object`, because it represents a set of fields.
542                        //       So, if an error happens or a `Value::Null` is returned, it's an
543                        //       indication that the fragment execution failed somewhere and,
544                        //       because of non-`null` types involved, its error should be
545                        //       propagated to the parent field, which is done here by returning
546                        //       a `false`.
547                        return false;
548                    }
549                }
550            }
551            Selection::InlineFragment(Spanning {
552                item: ref fragment,
553                ref span,
554            }) => {
555                if is_excluded(&fragment.directives, executor.variables()) {
556                    continue;
557                }
558
559                let sub_exec = executor.type_sub_executor(
560                    fragment.type_condition.as_ref().map(|c| c.item),
561                    Some(&fragment.selection_set[..]),
562                );
563
564                if let Some(ref type_condition) = fragment.type_condition {
565                    // Check whether the type matches the type condition.
566                    let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
567                    if executor
568                        .schema()
569                        .is_named_subtype(&concrete_type_name, type_condition.item)
570                    {
571                        let sub_result = instance.resolve_into_type(
572                            info,
573                            &concrete_type_name,
574                            Some(&fragment.selection_set[..]),
575                            &sub_exec,
576                        );
577
578                        if let Ok(Value::Object(object)) = sub_result {
579                            for (k, v) in object {
580                                merge_key_into(result, &k, v);
581                            }
582                        } else {
583                            if let Err(e) = sub_result {
584                                sub_exec.push_error_at(e, span.start);
585                            }
586                            // NOTE: Executing a fragment cannot really result in anything other
587                            //       than `Value::Object`, because it represents a set of fields.
588                            //       So, if an error happens or a `Value::Null` is returned, it's an
589                            //       indication that the fragment execution failed somewhere and,
590                            //       because of non-`null` types involved, its error should be
591                            //       propagated to the parent field, which is done here by returning
592                            //       a `false`.
593                            return false;
594                        }
595                    }
596                } else if !resolve_selection_set_into(
597                    instance,
598                    info,
599                    &fragment.selection_set[..],
600                    &sub_exec,
601                    result,
602                ) {
603                    return false;
604                }
605            }
606        }
607    }
608
609    true
610}
611
612pub(super) fn is_excluded<S>(
613    directives: &Option<Vec<Spanning<Directive<S>>>>,
614    vars: &Variables<S>,
615) -> bool
616where
617    S: ScalarValue,
618{
619    if let Some(directives) = directives {
620        for Spanning {
621            item: directive, ..
622        } in directives
623        {
624            let condition: bool = directive
625                .arguments
626                .iter()
627                .flat_map(|m| m.item.get("if"))
628                .filter_map(|v| v.item.clone().into_const(vars)?.convert().ok())
629                .next()
630                .unwrap();
631
632            if (directive.name.item == "skip" && condition)
633                || (directive.name.item == "include" && !condition)
634            {
635                return true;
636            }
637        }
638    }
639    false
640}
641
642/// Merges `response_name`/`value` pair into `result`
643pub(crate) fn merge_key_into<S>(result: &mut Object<S>, response_name: &str, value: Value<S>) {
644    if let Some(v) = result.get_mut_field_value(response_name) {
645        match v {
646            Value::Object(dest_obj) => {
647                if let Value::Object(src_obj) = value {
648                    merge_maps(dest_obj, src_obj);
649                }
650            }
651            Value::List(dest_list) => {
652                if let Value::List(src_list) = value {
653                    dest_list.iter_mut().zip(src_list).for_each(|(d, s)| {
654                        if let Value::Object(d_obj) = d {
655                            if let Value::Object(s_obj) = s {
656                                merge_maps(d_obj, s_obj);
657                            }
658                        }
659                    });
660                }
661            }
662            _ => {}
663        }
664        return;
665    }
666    result.add_field(response_name, value);
667}
668
669/// Merges `src` object's fields into `dest`
670fn merge_maps<S>(dest: &mut Object<S>, src: Object<S>) {
671    for (key, value) in src {
672        if dest.contains_field(&key) {
673            merge_key_into(dest, &key, value);
674        } else {
675            dest.add_field(key, value);
676        }
677    }
678}