juniper_puff/macros/
reflect.rs

1//! Compile-time reflection of Rust types into GraphQL types.
2
3use std::{rc::Rc, sync::Arc};
4
5use futures::future::BoxFuture;
6
7use crate::{
8    Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue,
9};
10
11/// Alias for a [GraphQL object][1], [scalar][2] or [interface][3] type's name
12/// in a GraphQL schema.
13///
14/// See [`BaseType`] for more info.
15///
16/// [1]: https://spec.graphql.org/October2021#sec-Objects
17/// [2]: https://spec.graphql.org/October2021#sec-Scalars
18/// [3]: https://spec.graphql.org/October2021#sec-Interfaces
19pub type Type = &'static str;
20
21/// Alias for a slice of [`Type`]s.
22///
23/// See [`BaseSubTypes`] for more info.
24pub type Types = &'static [Type];
25
26/// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`].
27///
28/// This trait is transparent to [`Option`], [`Vec`] and other containers, so to
29/// fully represent a [GraphQL object][1] we additionally use [`WrappedType`].
30///
31/// Different Rust types may have the same [`NAME`]. For example, [`String`] and
32/// `&`[`str`](prim@str) share `String!` GraphQL type.
33///
34/// [`NAME`]: Self::NAME
35/// [1]: https://spec.graphql.org/October2021#sec-Objects
36/// [2]: https://spec.graphql.org/October2021#sec-Scalars
37/// [3]: https://spec.graphql.org/October2021#sec-Interfaces
38pub trait BaseType<S> {
39    /// [`Type`] of the [GraphQL object][1], [scalar][2] or [interface][3].
40    ///
41    /// [1]: https://spec.graphql.org/October2021#sec-Objects
42    /// [2]: https://spec.graphql.org/October2021#sec-Scalars
43    /// [3]: https://spec.graphql.org/October2021#sec-Interfaces
44    const NAME: Type;
45}
46
47impl<'a, S, T: BaseType<S> + ?Sized> BaseType<S> for &'a T {
48    const NAME: Type = T::NAME;
49}
50
51impl<'ctx, S, T> BaseType<S> for (&'ctx T::Context, T)
52where
53    S: ScalarValue,
54    T: BaseType<S> + GraphQLValue<S>,
55{
56    const NAME: Type = T::NAME;
57}
58
59impl<S, T: BaseType<S>> BaseType<S> for Option<T> {
60    const NAME: Type = T::NAME;
61}
62
63impl<S, T: BaseType<S>> BaseType<S> for Nullable<T> {
64    const NAME: Type = T::NAME;
65}
66
67impl<S, T: BaseType<S>, E> BaseType<S> for Result<T, E> {
68    const NAME: Type = T::NAME;
69}
70
71impl<S, T: BaseType<S>> BaseType<S> for Vec<T> {
72    const NAME: Type = T::NAME;
73}
74
75impl<S, T: BaseType<S>> BaseType<S> for [T] {
76    const NAME: Type = T::NAME;
77}
78
79impl<S, T: BaseType<S>, const N: usize> BaseType<S> for [T; N] {
80    const NAME: Type = T::NAME;
81}
82
83impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Box<T> {
84    const NAME: Type = T::NAME;
85}
86
87impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Arc<T> {
88    const NAME: Type = T::NAME;
89}
90
91impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Rc<T> {
92    const NAME: Type = T::NAME;
93}
94
95/// [Sub-types][2] of a [GraphQL object][1].
96///
97/// This trait is transparent to [`Option`], [`Vec`] and other containers.
98///
99/// [1]: https://spec.graphql.org/October2021#sec-Objects
100/// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC
101pub trait BaseSubTypes<S> {
102    /// Sub-[`Types`] of the [GraphQL object][1].
103    ///
104    /// [1]: https://spec.graphql.org/October2021#sec-Objects
105    const NAMES: Types;
106}
107
108impl<'a, S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for &'a T {
109    const NAMES: Types = T::NAMES;
110}
111
112impl<'ctx, S, T> BaseSubTypes<S> for (&'ctx T::Context, T)
113where
114    S: ScalarValue,
115    T: BaseSubTypes<S> + GraphQLValue<S>,
116{
117    const NAMES: Types = T::NAMES;
118}
119
120impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Option<T> {
121    const NAMES: Types = T::NAMES;
122}
123
124impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Nullable<T> {
125    const NAMES: Types = T::NAMES;
126}
127
128impl<S, T: BaseSubTypes<S>, E> BaseSubTypes<S> for Result<T, E> {
129    const NAMES: Types = T::NAMES;
130}
131
132impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Vec<T> {
133    const NAMES: Types = T::NAMES;
134}
135
136impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for [T] {
137    const NAMES: Types = T::NAMES;
138}
139
140impl<S, T: BaseSubTypes<S>, const N: usize> BaseSubTypes<S> for [T; N] {
141    const NAMES: Types = T::NAMES;
142}
143
144impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Box<T> {
145    const NAMES: Types = T::NAMES;
146}
147
148impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Arc<T> {
149    const NAMES: Types = T::NAMES;
150}
151
152impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Rc<T> {
153    const NAMES: Types = T::NAMES;
154}
155
156/// Alias for a value of a [`WrappedType`] (composed GraphQL type).
157pub type WrappedValue = u128;
158
159// TODO: Just use `&str`s once they're allowed in `const` generics.
160/// Encoding of a composed GraphQL type in numbers.
161///
162/// To fully represent a [GraphQL object][1] it's not enough to use [`Type`],
163/// because of the [wrapping types][2]. To work around this we use a
164/// [`WrappedValue`] which is represented via [`u128`] number in the following
165/// encoding:
166/// - In base case of non-nullable [object][1] [`VALUE`] is `1`.
167/// - To represent nullability we "append" `2` to the [`VALUE`], so
168///   [`Option`]`<`[object][1]`>` has [`VALUE`] of `12`.
169/// - To represent list we "append" `3` to the [`VALUE`], so
170///   [`Vec`]`<`[object][1]`>` has [`VALUE`] of `13`.
171///
172/// This approach allows us to uniquely represent any [GraphQL object][1] with a
173/// combination of [`Type`] and [`WrappedValue`] and even format it via
174/// [`format_type!`] macro in a `const` context.
175///
176/// # Examples
177///
178/// ```rust
179/// # use juniper::{
180/// #     format_type,
181/// #     macros::reflect::{WrappedType, BaseType, WrappedValue, Type},
182/// #     DefaultScalarValue,
183/// # };
184/// #
185/// assert_eq!(<Option<i32> as WrappedType<DefaultScalarValue>>::VALUE, 12);
186/// assert_eq!(<Vec<i32> as WrappedType<DefaultScalarValue>>::VALUE, 13);
187/// assert_eq!(<Vec<Option<i32>> as WrappedType<DefaultScalarValue>>::VALUE, 123);
188/// assert_eq!(<Option<Vec<i32>> as WrappedType<DefaultScalarValue>>::VALUE, 132);
189/// assert_eq!(<Option<Vec<Option<i32>>> as WrappedType<DefaultScalarValue>>::VALUE, 1232);
190///
191/// const TYPE_STRING: Type = <Option<Vec<Option<String>>> as BaseType<DefaultScalarValue>>::NAME;
192/// const WRAP_VAL_STRING: WrappedValue = <Option<Vec<Option<String>>> as WrappedType<DefaultScalarValue>>::VALUE;
193/// assert_eq!(format_type!(TYPE_STRING, WRAP_VAL_STRING), "[String]");
194///
195/// const TYPE_STR: Type = <Option<Vec<Option<&str>>> as BaseType<DefaultScalarValue>>::NAME;
196/// const WRAP_VAL_STR: WrappedValue = <Option<Vec<Option<&str>>> as WrappedType<DefaultScalarValue>>::VALUE;
197/// assert_eq!(format_type!(TYPE_STR, WRAP_VAL_STR), "[String]");
198/// ```
199///
200/// [`VALUE`]: Self::VALUE
201/// [1]: https://spec.graphql.org/October2021#sec-Objects
202/// [2]: https://spec.graphql.org/October2021#sec-Wrapping-Types
203pub trait WrappedType<S> {
204    /// [`WrappedValue`] of this type.
205    const VALUE: WrappedValue;
206}
207
208impl<'ctx, S, T: WrappedType<S>> WrappedType<S> for (&'ctx T::Context, T)
209where
210    S: ScalarValue,
211    T: GraphQLValue<S>,
212{
213    const VALUE: u128 = T::VALUE;
214}
215
216impl<S, T: WrappedType<S>> WrappedType<S> for Option<T> {
217    const VALUE: u128 = T::VALUE * 10 + 2;
218}
219
220impl<S, T: WrappedType<S>> WrappedType<S> for Nullable<T> {
221    const VALUE: u128 = T::VALUE * 10 + 2;
222}
223
224impl<S, T: WrappedType<S>, E> WrappedType<S> for Result<T, E> {
225    const VALUE: u128 = T::VALUE;
226}
227
228impl<S, T: WrappedType<S>> WrappedType<S> for Vec<T> {
229    const VALUE: u128 = T::VALUE * 10 + 3;
230}
231
232impl<S, T: WrappedType<S>> WrappedType<S> for [T] {
233    const VALUE: u128 = T::VALUE * 10 + 3;
234}
235
236impl<S, T: WrappedType<S>, const N: usize> WrappedType<S> for [T; N] {
237    const VALUE: u128 = T::VALUE * 10 + 3;
238}
239
240impl<'a, S, T: WrappedType<S> + ?Sized> WrappedType<S> for &'a T {
241    const VALUE: u128 = T::VALUE;
242}
243
244impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Box<T> {
245    const VALUE: u128 = T::VALUE;
246}
247
248impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Arc<T> {
249    const VALUE: u128 = T::VALUE;
250}
251
252impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Rc<T> {
253    const VALUE: u128 = T::VALUE;
254}
255
256/// Alias for a [GraphQL object][1] or [interface][2] [field argument][3] name.
257///
258/// See [`Fields`] for more info.
259///
260/// [1]: https://spec.graphql.org/October2021#sec-Objects
261/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
262/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
263pub type Name = &'static str;
264
265/// Alias for a slice of [`Name`]s.
266///
267/// See [`Fields`] for more info.
268pub type Names = &'static [Name];
269
270/// Alias for [field argument][1]s [`Name`], [`Type`] and [`WrappedValue`].
271///
272/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
273pub type Argument = (Name, Type, WrappedValue);
274
275/// Alias for a slice of [field argument][1]s [`Name`], [`Type`] and
276/// [`WrappedValue`].
277///
278/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
279pub type Arguments = &'static [(Name, Type, WrappedValue)];
280
281/// Alias for a `const`-hashed [`Name`] used in a `const` context.
282pub type FieldName = u128;
283
284/// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`].
285///
286/// [1]: https://spec.graphql.org/October2021#sec-Objects
287/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
288/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
289pub trait Fields<S> {
290    /// [`Names`] of the [GraphQL object][1] or [interface][2]
291    /// [field arguments][3].
292    ///
293    /// [1]: https://spec.graphql.org/October2021#sec-Objects
294    /// [2]: https://spec.graphql.org/October2021#sec-Interfaces
295    /// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
296    const NAMES: Names;
297}
298
299/// [`Types`] of the [GraphQL interfaces][1] implemented by this type.
300///
301/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
302pub trait Implements<S> {
303    /// [`Types`] of the [GraphQL interfaces][1] implemented by this type.
304    ///
305    /// [1]: https://spec.graphql.org/October2021#sec-Interfaces
306    const NAMES: Types;
307}
308
309/// Stores meta information of a [GraphQL field][1]:
310/// - [`Context`] and [`TypeInfo`].
311/// - Return type's [`TYPE`], [`SUB_TYPES`] and [`WRAPPED_VALUE`].
312/// - [`ARGUMENTS`].
313///
314/// [`ARGUMENTS`]: Self::ARGUMENTS
315/// [`Context`]: Self::Context
316/// [`SUB_TYPES`]: Self::SUB_TYPES
317/// [`TYPE`]: Self::TYPE
318/// [`TypeInfo`]: Self::TypeInfo
319/// [`WRAPPED_VALUE`]: Self::WRAPPED_VALUE
320/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
321pub trait FieldMeta<S, const N: FieldName> {
322    /// [`GraphQLValue::Context`] of this [field][1].
323    ///
324    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
325    type Context;
326
327    /// [`GraphQLValue::TypeInfo`] of this [GraphQL field][1].
328    ///
329    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
330    type TypeInfo;
331
332    /// [`Types`] of [GraphQL field's][1] return type.
333    ///
334    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
335    const TYPE: Type;
336
337    /// [Sub-types][1] of [GraphQL field's][2] return type.
338    ///
339    /// [1]: BaseSubTypes
340    /// [2]: https://spec.graphql.org/October2021#sec-Language.Fields
341    const SUB_TYPES: Types;
342
343    /// [`WrappedValue`] of [GraphQL field's][1] return type.
344    ///
345    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
346    const WRAPPED_VALUE: WrappedValue;
347
348    /// [GraphQL field's][1] [`Arguments`].
349    ///
350    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
351    const ARGUMENTS: Arguments;
352}
353
354/// Synchronous field of a [GraphQL object][1] or [interface][2].
355///
356/// [1]: https://spec.graphql.org/October2021#sec-Objects
357/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
358pub trait Field<S, const N: FieldName>: FieldMeta<S, N> {
359    /// Resolves the [`Value`] of this synchronous [`Field`].
360    ///
361    /// The `arguments` object contains all the specified arguments, with the
362    /// default values being substituted for the ones not provided by the query.
363    ///
364    /// The `executor` can be used to drive selections into sub-[objects][1].
365    ///
366    /// [`Value`]: crate::Value
367    /// [1]: https://spec.graphql.org/October2021#sec-Objects
368    fn call(
369        &self,
370        info: &Self::TypeInfo,
371        args: &FieldArguments<S>,
372        executor: &Executor<Self::Context, S>,
373    ) -> ExecutionResult<S>;
374}
375
376/// Asynchronous field of a GraphQL [object][1] or [interface][2].
377///
378/// [1]: https://spec.graphql.org/October2021#sec-Objects
379/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
380pub trait AsyncField<S, const N: FieldName>: FieldMeta<S, N> {
381    /// Resolves the [`Value`] of this asynchronous [`AsyncField`].
382    ///
383    /// The `arguments` object contains all the specified arguments, with the
384    /// default values being substituted for the ones not provided by the query.
385    ///
386    /// The `executor` can be used to drive selections into sub-[objects][1].
387    ///
388    /// [1]: https://spec.graphql.org/October2021#sec-Objects
389    fn call<'b>(
390        &'b self,
391        info: &'b Self::TypeInfo,
392        args: &'b FieldArguments<S>,
393        executor: &'b Executor<Self::Context, S>,
394    ) -> BoxFuture<'b, ExecutionResult<S>>;
395}
396
397/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in
398/// `const` generics. See [spec] for more info.
399///
400/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html
401#[must_use]
402pub const fn fnv1a128(str: Name) -> u128 {
403    const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d;
404    const FNV_PRIME: u128 = 0x0000000001000000000000000000013b;
405
406    let bytes = str.as_bytes();
407    let mut hash = FNV_OFFSET_BASIS;
408    let mut i = 0;
409    while i < bytes.len() {
410        hash ^= bytes[i] as u128;
411        hash = hash.wrapping_mul(FNV_PRIME);
412        i += 1;
413    }
414    hash
415}
416
417/// Length __in bytes__ of the [`format_type!`] macro result.
418#[must_use]
419pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize {
420    let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type!
421
422    let mut curr = val;
423    while curr % 10 != 0 {
424        match curr % 10 {
425            2 => len -= "!".as_bytes().len(),   // remove !
426            3 => len += "[]!".as_bytes().len(), // [Type]!
427            _ => {}
428        }
429        curr /= 10;
430    }
431
432    len
433}
434
435/// Checks whether the given GraphQL [object][1] represents a `subtype` of the
436/// given GraphQL `ty`pe, basing on the [`WrappedType`] encoding.
437///
438/// To fully determine the sub-typing relation the [`Type`] should be one of the
439/// [`BaseSubTypes::NAMES`].
440///
441/// [1]: https://spec.graphql.org/October2021#sec-Objects
442#[must_use]
443pub const fn can_be_subtype(ty: WrappedValue, subtype: WrappedValue) -> bool {
444    let ty_curr = ty % 10;
445    let sub_curr = subtype % 10;
446
447    if ty_curr == sub_curr {
448        if ty_curr == 1 {
449            true
450        } else {
451            can_be_subtype(ty / 10, subtype / 10)
452        }
453    } else if ty_curr == 2 {
454        can_be_subtype(ty / 10, subtype)
455    } else {
456        false
457    }
458}
459
460/// Checks whether the given `val` exists in the given `arr`.
461#[must_use]
462pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool {
463    let mut i = 0;
464    while i < arr.len() {
465        if str_eq(val, arr[i]) {
466            return true;
467        }
468        i += 1;
469    }
470    false
471}
472
473/// Compares strings in a `const` context.
474///
475/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to
476/// write custom comparison function.
477///
478/// [`Eq`]: std::cmp::Eq
479// TODO: Remove once `Eq` trait is allowed in `const` context.
480pub const fn str_eq(l: &str, r: &str) -> bool {
481    let (l, r) = (l.as_bytes(), r.as_bytes());
482
483    if l.len() != r.len() {
484        return false;
485    }
486
487    let mut i = 0;
488    while i < l.len() {
489        if l[i] != r[i] {
490            return false;
491        }
492        i += 1;
493    }
494
495    true
496}
497
498/// Asserts that `#[graphql_interface(for = ...)]` has all the types referencing
499/// this interface in the `impl = ...` attribute argument.
500///
501/// Symmetrical to [`assert_interfaces_impls!`].
502#[macro_export]
503macro_rules! assert_implemented_for {
504    ($scalar: ty, $implementor: ty $(, $interfaces: ty)* $(,)?) => {
505        const _: () = {
506            $({
507                let is_present = $crate::macros::reflect::str_exists_in_arr(
508                    <$implementor as ::juniper::macros::reflect::BaseType<$scalar>>::NAME,
509                    <$interfaces as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES,
510                );
511                if !is_present {
512                    const MSG: &str = $crate::const_concat!(
513                        "Failed to implement interface `",
514                        <$interfaces as $crate::macros::reflect::BaseType<$scalar>>::NAME,
515                        "` on `",
516                        <$implementor as $crate::macros::reflect::BaseType<$scalar>>::NAME,
517                        "`: missing implementer reference in interface's `for` attribute.",
518                    );
519                    ::std::panic!("{}", MSG);
520                }
521            })*
522        };
523    };
524}
525
526/// Asserts that `impl = ...` attribute argument has all the types referencing
527/// this GraphQL type in `#[graphql_interface(for = ...)]`.
528///
529/// Symmetrical to [`assert_implemented_for!`].
530#[macro_export]
531macro_rules! assert_interfaces_impls {
532    ($scalar: ty, $interface: ty $(, $implementers: ty)* $(,)?) => {
533        const _: () = {
534            $({
535                let is_present = $crate::macros::reflect::str_exists_in_arr(
536                    <$interface as ::juniper::macros::reflect::BaseType<$scalar>>::NAME,
537                    <$implementers as ::juniper::macros::reflect::Implements<$scalar>>::NAMES,
538                );
539                if !is_present {
540                    const MSG: &str = $crate::const_concat!(
541                        "Failed to implement interface `",
542                        <$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME,
543                        "` on `",
544                        <$implementers as $crate::macros::reflect::BaseType<$scalar>>::NAME,
545                        "`: missing interface reference in implementer's `impl` attribute.",
546                    );
547                    ::std::panic!("{}", MSG);
548                }
549            })*
550        };
551    };
552}
553
554/// Asserts that all [transitive interfaces][0] (the ones implemented by the
555/// `$interface`) are also implemented by the `$implementor`.
556///
557/// [0]: https://spec.graphql.org/October2021#sel-FAHbhBHCAACGB35P
558#[macro_export]
559macro_rules! assert_transitive_impls {
560    ($scalar: ty, $interface: ty, $implementor: ty $(, $transitive: ty)* $(,)?) => {
561        const _: () = {
562            $({
563                let is_present = $crate::macros::reflect::str_exists_in_arr(
564                    <$implementor as ::juniper::macros::reflect::BaseType<$scalar>>::NAME,
565                    <$transitive as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES,
566                );
567                if !is_present {
568                    const MSG: &str = $crate::const_concat!(
569                        "Failed to implement interface `",
570                        <$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME,
571                        "` on `",
572                        <$implementor as $crate::macros::reflect::BaseType<$scalar>>::NAME,
573                        "`: missing `impl = ` for transitive interface `",
574                        <$transitive as $crate::macros::reflect::BaseType<$scalar>>::NAME,
575                        "` on `",
576                        <$implementor as $crate::macros::reflect::BaseType<$scalar>>::NAME,
577                        "`."
578                    );
579                    ::std::panic!("{}", MSG);
580                }
581            })*
582        };
583    };
584}
585
586/// Asserts validness of [`Field`] [`Arguments`] and returned [`Type`].
587///
588/// This assertion is a combination of [`assert_subtype`] and
589/// [`assert_field_args`].
590///
591/// See [spec][1] for more info.
592///
593/// [1]: https://spec.graphql.org/October2021#IsValidImplementation()
594#[macro_export]
595macro_rules! assert_field {
596    (
597        $base_ty: ty,
598        $impl_ty: ty,
599        $scalar: ty,
600        $field_name: expr $(,)?
601    ) => {
602        $crate::assert_field_args!($base_ty, $impl_ty, $scalar, $field_name);
603        $crate::assert_subtype!($base_ty, $impl_ty, $scalar, $field_name);
604    };
605}
606
607/// Asserts validness of a [`Field`] return type.
608///
609/// See [spec][1] for more info.
610///
611/// [1]: https://spec.graphql.org/October2021#IsValidImplementationFieldType()
612#[macro_export]
613macro_rules! assert_subtype {
614    (
615        $base_ty: ty,
616        $impl_ty: ty,
617        $scalar: ty,
618        $field_name: expr $(,)?
619    ) => {
620        const _: () = {
621            const BASE_TY: $crate::macros::reflect::Type =
622                <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
623            const IMPL_TY: $crate::macros::reflect::Type =
624                <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
625            const ERR_PREFIX: &str = $crate::const_concat!(
626                "Failed to implement interface `",
627                BASE_TY,
628                "` on `",
629                IMPL_TY,
630                "`: ",
631            );
632
633            const FIELD_NAME: $crate::macros::reflect::Name =
634                $field_name;
635
636            const BASE_RETURN_WRAPPED_VAL: $crate::macros::reflect::WrappedValue =
637                <$base_ty as $crate::macros::reflect::FieldMeta<
638                    $scalar,
639                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
640                >>::WRAPPED_VALUE;
641            const IMPL_RETURN_WRAPPED_VAL: $crate::macros::reflect::WrappedValue =
642                <$impl_ty as $crate::macros::reflect::FieldMeta<
643                    $scalar,
644                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
645                >>::WRAPPED_VALUE;
646
647            const BASE_RETURN_TY: $crate::macros::reflect::Type =
648                <$base_ty as $crate::macros::reflect::FieldMeta<
649                    $scalar,
650                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
651                >>::TYPE;
652            const IMPL_RETURN_TY: $crate::macros::reflect::Type =
653                <$impl_ty as $crate::macros::reflect::FieldMeta<
654                    $scalar,
655                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
656                >>::TYPE;
657
658            const BASE_RETURN_SUB_TYPES: $crate::macros::reflect::Types =
659                <$base_ty as $crate::macros::reflect::FieldMeta<
660                    $scalar,
661                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
662                >>::SUB_TYPES;
663
664            let is_subtype = $crate::macros::reflect::str_exists_in_arr(IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES)
665                && $crate::macros::reflect::can_be_subtype(BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL);
666            if !is_subtype {
667                const MSG: &str = $crate::const_concat!(
668                    ERR_PREFIX,
669                    "Field `",
670                    FIELD_NAME,
671                    "`: implementor is expected to return a subtype of interface's return object: `",
672                    $crate::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL),
673                    "` is not a subtype of `",
674                    $crate::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL),
675                    "`.",
676                );
677                ::std::panic!("{}", MSG);
678            }
679        };
680    };
681}
682
683/// Asserts validness of the [`Field`]s arguments. See [spec][1] for more
684/// info.
685///
686/// [1]: https://spec.graphql.org/October2021#sel-IAHZhCHCDEEFAAADHD8Cxob
687#[macro_export]
688macro_rules! assert_field_args {
689    (
690        $base_ty: ty,
691        $impl_ty: ty,
692        $scalar: ty,
693        $field_name: expr $(,)?
694    ) => {
695        const _: () = {
696            const BASE_NAME: &str = <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
697            const IMPL_NAME: &str = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
698            const ERR_PREFIX: &str = $crate::const_concat!(
699                "Failed to implement interface `",
700                BASE_NAME,
701                "` on `",
702                IMPL_NAME,
703                "`: ",
704            );
705
706            const FIELD_NAME: &str = $field_name;
707
708            const BASE_ARGS: ::juniper::macros::reflect::Arguments =
709                <$base_ty as $crate::macros::reflect::FieldMeta<
710                    $scalar,
711                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
712                >>::ARGUMENTS;
713            const IMPL_ARGS: ::juniper::macros::reflect::Arguments =
714                <$impl_ty as $crate::macros::reflect::FieldMeta<
715                    $scalar,
716                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
717                >>::ARGUMENTS;
718
719            struct Error {
720                cause: Cause,
721                base: ::juniper::macros::reflect::Argument,
722                implementation: ::juniper::macros::reflect::Argument,
723            }
724
725            enum Cause {
726                RequiredField,
727                AdditionalNonNullableField,
728                TypeMismatch,
729            }
730
731            const fn unwrap_error(v: ::std::result::Result<(), Error>) -> Error {
732                match v {
733                    // Unfortunately we can't use `unreachable!()` here, as this
734                    // branch will be executed either way.
735                    Ok(()) => Error {
736                        cause: Cause::RequiredField,
737                        base: ("unreachable", "unreachable", 1),
738                        implementation: ("unreachable", "unreachable", 1),
739                    },
740                    Err(err) => err,
741                }
742            }
743
744            const fn check() -> Result<(), Error> {
745                let mut base_i = 0;
746                while base_i < BASE_ARGS.len() {
747                    let (base_name, base_type, base_wrap_val) = BASE_ARGS[base_i];
748
749                    let mut impl_i = 0;
750                    let mut was_found = false;
751                    while impl_i < IMPL_ARGS.len() {
752                        let (impl_name, impl_type, impl_wrap_val) = IMPL_ARGS[impl_i];
753
754                        if $crate::macros::reflect::str_eq(base_name, impl_name) {
755                            if $crate::macros::reflect::str_eq(base_type, impl_type)
756                                && base_wrap_val == impl_wrap_val
757                            {
758                                was_found = true;
759                                break;
760                            } else {
761                                return Err(Error {
762                                    cause: Cause::TypeMismatch,
763                                    base: (base_name, base_type, base_wrap_val),
764                                    implementation: (impl_name, impl_type, impl_wrap_val),
765                                });
766                            }
767                        }
768
769                        impl_i += 1;
770                    }
771
772                    if !was_found {
773                        return Err(Error {
774                            cause: Cause::RequiredField,
775                            base: (base_name, base_type, base_wrap_val),
776                            implementation: (base_name, base_type, base_wrap_val),
777                        });
778                    }
779
780                    base_i += 1;
781                }
782
783                let mut impl_i = 0;
784                while impl_i < IMPL_ARGS.len() {
785                    let (impl_name, impl_type, impl_wrapped_val) = IMPL_ARGS[impl_i];
786                    impl_i += 1;
787
788                    if impl_wrapped_val % 10 == 2 {
789                        continue;
790                    }
791
792                    let mut base_i = 0;
793                    let mut was_found = false;
794                    while base_i < BASE_ARGS.len() {
795                        let (base_name, _, _) = BASE_ARGS[base_i];
796                        if $crate::macros::reflect::str_eq(base_name, impl_name) {
797                            was_found = true;
798                            break;
799                        }
800                        base_i += 1;
801                    }
802                    if !was_found {
803                        return Err(Error {
804                            cause: Cause::AdditionalNonNullableField,
805                            base: (impl_name, impl_type, impl_wrapped_val),
806                            implementation: (impl_name, impl_type, impl_wrapped_val),
807                        });
808                    }
809                }
810
811                Ok(())
812            }
813
814            const RES: ::std::result::Result<(), Error> = check();
815            if RES.is_err() {
816                const ERROR: Error = unwrap_error(RES);
817
818                const BASE_ARG_NAME: &str = ERROR.base.0;
819                const IMPL_ARG_NAME: &str = ERROR.implementation.0;
820
821                const BASE_TYPE_FORMATTED: &str = $crate::format_type!(ERROR.base.1, ERROR.base.2);
822                const IMPL_TYPE_FORMATTED: &str =
823                    $crate::format_type!(ERROR.implementation.1, ERROR.implementation.2);
824
825                const MSG: &str = match ERROR.cause {
826                    Cause::TypeMismatch => {
827                        $crate::const_concat!(
828                            "Argument `",
829                            BASE_ARG_NAME,
830                            "`: expected type `",
831                            BASE_TYPE_FORMATTED,
832                            "`, found: `",
833                            IMPL_TYPE_FORMATTED,
834                            "`.",
835                        )
836                    }
837                    Cause::RequiredField => {
838                        $crate::const_concat!(
839                            "Argument `",
840                            BASE_ARG_NAME,
841                            "` of type `",
842                            BASE_TYPE_FORMATTED,
843                            "` was expected, but not found."
844                        )
845                    }
846                    Cause::AdditionalNonNullableField => {
847                        $crate::const_concat!(
848                            "Argument `",
849                            IMPL_ARG_NAME,
850                            "` of type `",
851                            IMPL_TYPE_FORMATTED,
852                            "` isn't present on the interface and so has to be nullable."
853                        )
854                    }
855                };
856                const ERROR_MSG: &str =
857                    $crate::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG);
858                ::std::panic!("{}", ERROR_MSG);
859            }
860        };
861    };
862}
863
864/// Concatenates `const` [`str`](prim@str)s in a `const` context.
865#[macro_export]
866macro_rules! const_concat {
867    ($($s:expr),* $(,)?) => {{
868        const LEN: usize = 0 $(+ $s.as_bytes().len())*;
869        const CNT: usize = [$($s),*].len();
870        const fn concat(input: [&str; CNT]) -> [u8; LEN] {
871            let mut bytes = [0; LEN];
872            let (mut i, mut byte) = (0, 0);
873            while i < CNT {
874                let mut b = 0;
875                while b < input[i].len() {
876                    bytes[byte] = input[i].as_bytes()[b];
877                    byte += 1;
878                    b += 1;
879                }
880                i += 1;
881            }
882            bytes
883        }
884        const CON: [u8; LEN] = concat([$($s),*]);
885
886        // TODO: Use `.unwrap()` once it becomes `const`.
887        match ::std::str::from_utf8(&CON) {
888            ::std::result::Result::Ok(s) => s,
889            _ => unreachable!(),
890        }
891    }};
892}
893
894/// Ensures that the given `$impl_ty` implements [`Field`] and returns a
895/// [`fnv1a128`] hash for it, otherwise panics with understandable message.
896#[macro_export]
897macro_rules! checked_hash {
898    ($field_name: expr, $impl_ty: ty, $scalar: ty $(, $prefix: expr)? $(,)?) => {{
899        let exists = $crate::macros::reflect::str_exists_in_arr(
900            $field_name,
901            <$impl_ty as $crate::macros::reflect::Fields<$scalar>>::NAMES,
902        );
903        if exists {
904            $crate::macros::reflect::fnv1a128(FIELD_NAME)
905        } else {
906            const MSG: &str = $crate::const_concat!(
907                $($prefix,)?
908                "Field `",
909                $field_name,
910                "` isn't implemented on `",
911                <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME,
912                "`."
913            );
914            ::std::panic!("{}", MSG)
915        }
916    }};
917}
918
919/// Formats the given [`Type`] and [`WrappedValue`] into a readable GraphQL type
920/// name.
921///
922/// # Examples
923///
924/// ```rust
925/// # use juniper::format_type;
926/// #
927/// assert_eq!(format_type!("String", 123), "[String]!");
928/// assert_eq!(format_type!("🦀", 123), "[🦀]!");
929/// ```
930#[macro_export]
931macro_rules! format_type {
932    ($ty: expr, $wrapped_value: expr $(,)?) => {{
933        const TYPE: (
934            $crate::macros::reflect::Type,
935            $crate::macros::reflect::WrappedValue,
936        ) = ($ty, $wrapped_value);
937        const RES_LEN: usize = $crate::macros::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1);
938
939        const OPENING_BRACKET: &str = "[";
940        const CLOSING_BRACKET: &str = "]";
941        const BANG: &str = "!";
942
943        const fn format_type_arr() -> [u8; RES_LEN] {
944            let (ty, wrap_val) = TYPE;
945            let mut type_arr: [u8; RES_LEN] = [0; RES_LEN];
946
947            let mut current_start = 0;
948            let mut current_end = RES_LEN - 1;
949            let mut current_wrap_val = wrap_val;
950            let mut is_null = false;
951            while current_wrap_val % 10 != 0 {
952                match current_wrap_val % 10 {
953                    2 => is_null = true, // Skips writing `BANG` later.
954                    3 => {
955                        // Write `OPENING_BRACKET` at `current_start`.
956                        let mut i = 0;
957                        while i < OPENING_BRACKET.as_bytes().len() {
958                            type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i];
959                            i += 1;
960                        }
961                        current_start += i;
962                        if !is_null {
963                            // Write `BANG` at `current_end`.
964                            i = 0;
965                            while i < BANG.as_bytes().len() {
966                                type_arr[current_end - BANG.as_bytes().len() + i + 1] =
967                                    BANG.as_bytes()[i];
968                                i += 1;
969                            }
970                            current_end -= i;
971                        }
972                        // Write `CLOSING_BRACKET` at `current_end`.
973                        i = 0;
974                        while i < CLOSING_BRACKET.as_bytes().len() {
975                            type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] =
976                                CLOSING_BRACKET.as_bytes()[i];
977                            i += 1;
978                        }
979                        current_end -= i;
980                        is_null = false;
981                    }
982                    _ => {}
983                }
984
985                current_wrap_val /= 10;
986            }
987
988            // Writes `Type` at `current_start`.
989            let mut i = 0;
990            while i < ty.as_bytes().len() {
991                type_arr[current_start + i] = ty.as_bytes()[i];
992                i += 1;
993            }
994            i = 0;
995            if !is_null {
996                // Writes `BANG` at `current_end`.
997                while i < BANG.as_bytes().len() {
998                    type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i];
999                    i += 1;
1000                }
1001            }
1002
1003            type_arr
1004        }
1005
1006        const TYPE_ARR: [u8; RES_LEN] = format_type_arr();
1007
1008        // TODO: Use `.unwrap()` once it becomes `const`.
1009        const TYPE_FORMATTED: &str = match ::std::str::from_utf8(TYPE_ARR.as_slice()) {
1010            ::std::result::Result::Ok(s) => s,
1011            _ => unreachable!(),
1012        };
1013
1014        TYPE_FORMATTED
1015    }};
1016}