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}