Compare commits
68 commits
master
...
rework-cor
Author | SHA1 | Date | |
---|---|---|---|
|
2d01486ee9 | ||
|
0b6544db48 | ||
|
1ddf0bd611 | ||
|
bf219867b6 | ||
|
129ff559d1 | ||
|
d1f2173109 | ||
|
c75d33ae0a | ||
|
e6861bc516 | ||
|
b3659b88a4 | ||
|
37257934b7 | ||
|
fea722b196 | ||
|
464ff10c14 | ||
|
d8813aff13 | ||
|
fa03c7fa4d | ||
|
4257e72c75 | ||
|
98b282d06d | ||
|
7364187437 | ||
|
48631e9b25 | ||
|
505e25cea2 | ||
|
2e2d365d64 | ||
|
1819ab6e12 | ||
|
23e01956b3 | ||
|
42e2780142 | ||
|
07ed785a51 | ||
|
3cfc4eb0e4 | ||
|
4a6b3abfc6 | ||
|
17af7c5ac5 | ||
|
d648181b26 | ||
|
2d89de3403 | ||
|
66c11ec782 | ||
|
4d7770d226 | ||
|
1aafd3da5d | ||
|
679c1c1162 | ||
|
a05a0091f6 | ||
|
f2718cc01a | ||
|
25404eb4a7 | ||
|
067b1e532a | ||
|
88046e3ce4 | ||
|
be010e8123 | ||
|
5ab398e22b | ||
|
017e87c791 | ||
|
5a62ddfbf2 | ||
|
e347f25718 | ||
|
b28acbd96d | ||
|
b716a45215 | ||
|
97c88d219c | ||
|
8235ac22c0 | ||
|
97d2da581a | ||
|
740aa9061e | ||
|
113b112daf | ||
|
22f3f1887e | ||
|
b1be8f1d29 | ||
|
b381c696ff | ||
|
224b04973a | ||
|
8ea231f395 | ||
|
21c7a3a653 | ||
|
5a3bd6c8a9 | ||
|
e381602d5a | ||
|
912d31f66b | ||
|
ffc42b16a1 | ||
|
bde5b83eaf | ||
|
72cd4be715 | ||
|
64cf7adb4f | ||
|
c8759cd16e | ||
|
2d1e5d38b7 | ||
|
d00549968c | ||
|
4ef63e0b7c | ||
|
56a68a9e24 |
61 changed files with 8722 additions and 892 deletions
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, fmt, hash::Hash, slice, vec};
|
use std::{any::TypeId, borrow::Cow, convert::Into, fmt, hash::Hash, mem, slice, vec};
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
@ -256,13 +256,17 @@ impl<S> InputValue<S> {
|
||||||
Self::Variable(v.as_ref().into())
|
Self::Variable(v.as_ref().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a [`Spanning::unlocated`] list.
|
/// Constructs a [`Spanning::unlocated`] [`InputValue::List`].
|
||||||
///
|
///
|
||||||
/// Convenience function to make each [`InputValue`] in the input vector
|
/// Convenience function to make each [`InputValue`] in the input `list` to
|
||||||
/// not contain any location information. Can be used from [`ToInputValue`]
|
/// not contain any location information.
|
||||||
/// implementations, where no source code position information is available.
|
///
|
||||||
pub fn list(l: Vec<Self>) -> Self {
|
/// Intended for [`resolve::ToInputValue`] implementations, where no source
|
||||||
Self::List(l.into_iter().map(Spanning::unlocated).collect())
|
/// code position information is available.
|
||||||
|
///
|
||||||
|
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
|
||||||
|
pub fn list(list: impl IntoIterator<Item = Self>) -> Self {
|
||||||
|
Self::List(list.into_iter().map(Spanning::unlocated).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a located list.
|
/// Construct a located list.
|
||||||
|
@ -270,16 +274,25 @@ impl<S> InputValue<S> {
|
||||||
Self::List(l)
|
Self::List(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct aa [`Spanning::unlocated`] object.
|
/// Construct a [`Spanning::unlocated`] [`InputValue::Onject`].
|
||||||
///
|
///
|
||||||
/// Similarly to [`InputValue::list`] it makes each key and value in the
|
/// Similarly to [`InputValue::list()`] it makes each key and value in the
|
||||||
/// given hash map not contain any location information.
|
/// given `obj`ect to not contain any location information.
|
||||||
pub fn object<K>(o: IndexMap<K, Self>) -> Self
|
///
|
||||||
|
/// Intended for [`resolve::ToInputValue`] implementations, where no source
|
||||||
|
/// code position information is available.
|
||||||
|
///
|
||||||
|
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
|
||||||
|
// TODO: Use `impl IntoIterator<Item = (K, Self)>` argument once feature
|
||||||
|
// `explicit_generic_args_with_impl_trait` hits stable:
|
||||||
|
// https://github.com/rust-lang/rust/issues/83701
|
||||||
|
pub fn object<K, O>(obj: O) -> Self
|
||||||
where
|
where
|
||||||
K: AsRef<str> + Eq + Hash,
|
K: AsRef<str> + Eq + Hash,
|
||||||
|
O: IntoIterator<Item = (K, Self)>,
|
||||||
{
|
{
|
||||||
Self::Object(
|
Self::Object(
|
||||||
o.into_iter()
|
obj.into_iter()
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
(
|
(
|
||||||
Spanning::unlocated(k.as_ref().into()),
|
Spanning::unlocated(k.as_ref().into()),
|
||||||
|
@ -459,6 +472,42 @@ impl<S> InputValue<S> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps the [`ScalarValue`] type of this [`InputValue`] into the specified
|
||||||
|
/// one.
|
||||||
|
pub fn map_scalar_value<Into>(self) -> InputValue<Into>
|
||||||
|
where
|
||||||
|
S: ScalarValue,
|
||||||
|
Into: ScalarValue,
|
||||||
|
{
|
||||||
|
if TypeId::of::<Into>() == TypeId::of::<S>() {
|
||||||
|
// SAFETY: This is safe, because we're transmuting the value into
|
||||||
|
// itself, so no invariants may change and we're just
|
||||||
|
// satisfying the type checker.
|
||||||
|
// As `mem::transmute_copy` creates a copy of data, we need
|
||||||
|
// `mem::ManuallyDrop` here to omit double-free when
|
||||||
|
// `S: Drop`.
|
||||||
|
let val = mem::ManuallyDrop::new(self);
|
||||||
|
unsafe { mem::transmute_copy(&*val) }
|
||||||
|
} else {
|
||||||
|
match self {
|
||||||
|
Self::Null => InputValue::Null,
|
||||||
|
Self::Scalar(s) => InputValue::Scalar(s.into_another()),
|
||||||
|
Self::Enum(v) => InputValue::Enum(v),
|
||||||
|
Self::Variable(n) => InputValue::Variable(n),
|
||||||
|
Self::List(l) => InputValue::List(
|
||||||
|
l.into_iter()
|
||||||
|
.map(|i| i.map(InputValue::map_scalar_value))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Self::Object(o) => InputValue::Object(
|
||||||
|
o.into_iter()
|
||||||
|
.map(|(k, v)| (k, v.map(InputValue::map_scalar_value)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ScalarValue> fmt::Display for InputValue<S> {
|
impl<S: ScalarValue> fmt::Display for InputValue<S> {
|
||||||
|
|
130
juniper/src/behavior.rs
Normal file
130
juniper/src/behavior.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
//! GraphQL types behavior machinery.
|
||||||
|
|
||||||
|
use std::{marker::PhantomData, sync::atomic::AtomicPtr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Registry,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Default standard behavior of GraphQL types implementation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Standard {}
|
||||||
|
|
||||||
|
/// Transparent wrapper allowing coercion of behavior types and type parameters.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Coerce<T: ?Sized, To: ?Sized = Standard>(PhantomData<AtomicPtr<Box<To>>>, T);
|
||||||
|
|
||||||
|
impl<T, To: ?Sized> Coerce<T, To> {
|
||||||
|
/// Wraps the provided `value` into a [`Coerce`] wrapper.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn wrap(value: T) -> Self {
|
||||||
|
Self(PhantomData, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwraps into the inner value.
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps the provided `value` into a [`Coerce`] wrapper.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn coerce<T, To: ?Sized>(value: T) -> Coerce<T, To> {
|
||||||
|
Coerce::wrap(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, SV, B1, B2> resolve::Type<TI, SV, B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, B2> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, B1, B2> resolve::TypeName<TI, B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, B2> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, B1, B2> resolve::InputValue<'i, SV, B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, B2>,
|
||||||
|
SV: 'i,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v).map(Self::wrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null().map(Self::wrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, B1, B2> resolve::ScalarToken<SV, B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, B2> + ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, B1, B2> reflect::BaseType<B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<B2> + ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, B1, B2> reflect::BaseSubTypes<B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<B2> + ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, B1, B2> reflect::WrappedType<B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<B2> + ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, B1, B2> reflect::Implements<B1> for Coerce<T, B2>
|
||||||
|
where
|
||||||
|
T: reflect::Implements<B2> + ?Sized,
|
||||||
|
B1: ?Sized,
|
||||||
|
B2: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::{hash_map, HashMap},
|
||||||
|
convert,
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
@ -17,6 +18,7 @@ use crate::{
|
||||||
Selection, ToInputValue, Type,
|
Selection, ToInputValue, Type,
|
||||||
},
|
},
|
||||||
parser::{SourcePosition, Spanning},
|
parser::{SourcePosition, Spanning},
|
||||||
|
resolve,
|
||||||
schema::{
|
schema::{
|
||||||
meta::{
|
meta::{
|
||||||
Argument, DeprecationStatus, EnumMeta, EnumValue, Field, InputObjectMeta,
|
Argument, DeprecationStatus, EnumMeta, EnumValue, Field, InputObjectMeta,
|
||||||
|
@ -69,7 +71,7 @@ pub enum FieldPath<'a> {
|
||||||
/// of the current field stack, context, variables, and errors.
|
/// of the current field stack, context, variables, and errors.
|
||||||
pub struct Executor<'r, 'a, CtxT, S = DefaultScalarValue>
|
pub struct Executor<'r, 'a, CtxT, S = DefaultScalarValue>
|
||||||
where
|
where
|
||||||
CtxT: 'a,
|
CtxT: ?Sized + 'a,
|
||||||
S: 'a,
|
S: 'a,
|
||||||
{
|
{
|
||||||
fragments: &'r HashMap<&'a str, Fragment<'a, S>>,
|
fragments: &'r HashMap<&'a str, Fragment<'a, S>>,
|
||||||
|
@ -83,6 +85,41 @@ where
|
||||||
field_path: Arc<FieldPath<'a>>,
|
field_path: Arc<FieldPath<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'r, 'a, CX: ?Sized, SV> Executor<'r, 'a, CX, SV> {
|
||||||
|
pub(crate) fn current_type_reworked(&self) -> &TypeType<'a, SV> {
|
||||||
|
&self.current_type
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the specified single arbitrary `Type` `value` as
|
||||||
|
/// [`graphql::Value`].
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Whenever [`Type::resolve_value()`] errors.
|
||||||
|
///
|
||||||
|
/// [`graphql::Value`]: crate::graphql::Value
|
||||||
|
/// [`Type::resolve_value()`]: resolve::Value::resolve_value
|
||||||
|
pub fn resolve_value<BH, Type, TI>(&self, value: &Type, type_info: &TI) -> ExecutionResult<SV>
|
||||||
|
where
|
||||||
|
Type: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
value.resolve_value(self.current_selection_set, type_info, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current context of this [`Executor`].
|
||||||
|
///
|
||||||
|
/// Context is usually provided when the top-level [`execute()`] function is
|
||||||
|
/// called.
|
||||||
|
///
|
||||||
|
/// [`execute()`]: crate::execute
|
||||||
|
#[must_use]
|
||||||
|
pub fn context(&self) -> &'r CX {
|
||||||
|
self.context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Error type for errors that occur during query execution
|
/// Error type for errors that occur during query execution
|
||||||
///
|
///
|
||||||
/// All execution errors contain the source position in the query of the field
|
/// All execution errors contain the source position in the query of the field
|
||||||
|
@ -627,14 +664,6 @@ where
|
||||||
self.current_selection_set
|
self.current_selection_set
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the current context
|
|
||||||
///
|
|
||||||
/// You usually provide the context when calling the top-level `execute`
|
|
||||||
/// function, or using the context factory in the Iron integration.
|
|
||||||
pub fn context(&self) -> &'r CtxT {
|
|
||||||
self.context
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The currently executing schema
|
/// The currently executing schema
|
||||||
pub fn schema(&self) -> &'a SchemaType<S> {
|
pub fn schema(&self) -> &'a SchemaType<S> {
|
||||||
self.schema
|
self.schema
|
||||||
|
@ -1183,6 +1212,21 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an entry with a [`Type`] meta information for the specified
|
||||||
|
/// named [`graphql::Type`], registered in this [`Registry`].
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
pub fn entry_type<T, TI>(
|
||||||
|
&mut self,
|
||||||
|
type_info: &TI,
|
||||||
|
) -> hash_map::Entry<'_, Name, MetaType<'r, S>>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
{
|
||||||
|
self.types.entry(T::type_name(type_info).parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [`Field`] with the provided `name`.
|
/// Creates a [`Field`] with the provided `name`.
|
||||||
pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S>
|
pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S>
|
||||||
where
|
where
|
||||||
|
@ -1240,6 +1284,16 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
Argument::new(name, self.get_type::<T>(info)).default_value(value.to_input_value())
|
Argument::new(name, self.get_type::<T>(info)).default_value(value.to_input_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an [`Argument`] with the provided `name`.
|
||||||
|
pub fn arg_reworked<'ti, T, TI>(&mut self, name: &str, type_info: &'ti TI) -> Argument<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, S> + resolve::InputValueOwned<S>,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
{
|
||||||
|
Argument::new(name, T::meta(self, type_info).as_type())
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) {
|
fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) {
|
||||||
self.types
|
self.types
|
||||||
.entry(name)
|
.entry(name)
|
||||||
|
@ -1258,6 +1312,84 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
ScalarMeta::new::<T>(Cow::Owned(name.into()))
|
ScalarMeta::new::<T>(Cow::Owned(name.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`],
|
||||||
|
/// allowing to `customize` the created [`ScalarMeta`], and stores it in
|
||||||
|
/// this [`Registry`].
|
||||||
|
///
|
||||||
|
/// # Idempotent
|
||||||
|
///
|
||||||
|
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||||
|
/// already, then just returns it without doing anything.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
/// [`TypeName`]: resolve::TypeName
|
||||||
|
pub fn register_scalar_with<'ti, T, TI>(
|
||||||
|
&mut self,
|
||||||
|
type_info: &'ti TI,
|
||||||
|
customize: impl FnOnce(ScalarMeta<'r, S>) -> ScalarMeta<'r, S>,
|
||||||
|
) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + resolve::InputValueOwned<S> + resolve::ScalarToken<S>,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
self.entry_type::<T, _>(type_info)
|
||||||
|
.or_insert_with(move || {
|
||||||
|
customize(ScalarMeta::new_reworked::<T>(T::type_name(type_info))).into_meta()
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a [`ScalarMeta`] information for the specified non-[`Sized`]
|
||||||
|
/// [`graphql::Type`], and stores it in this [`Registry`].
|
||||||
|
///
|
||||||
|
/// # Idempotent
|
||||||
|
///
|
||||||
|
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||||
|
/// already, then just returns it without doing anything.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
/// [`TypeName`]: resolve::TypeName
|
||||||
|
pub fn register_scalar_unsized<'ti, T, TI>(&mut self, type_info: &'ti TI) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + resolve::InputValueAsRef<S> + resolve::ScalarToken<S> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
self.register_scalar_unsized_with::<T, TI>(type_info, convert::identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a [`ScalarMeta`] information for the specified non-[`Sized`]
|
||||||
|
/// [`graphql::Type`], allowing to `customize` the created [`ScalarMeta`],
|
||||||
|
/// and stores it in this [`Registry`].
|
||||||
|
///
|
||||||
|
/// # Idempotent
|
||||||
|
///
|
||||||
|
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||||
|
/// already, then just returns it without doing anything.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
/// [`TypeName`]: resolve::TypeName
|
||||||
|
pub fn register_scalar_unsized_with<'ti, T, TI>(
|
||||||
|
&mut self,
|
||||||
|
type_info: &'ti TI,
|
||||||
|
customize: impl FnOnce(ScalarMeta<'r, S>) -> ScalarMeta<'r, S>,
|
||||||
|
) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + resolve::InputValueAsRef<S> + resolve::ScalarToken<S> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
self.entry_type::<T, _>(type_info)
|
||||||
|
.or_insert_with(move || {
|
||||||
|
customize(ScalarMeta::new_unsized::<T>(T::type_name(type_info))).into_meta()
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [`ListMeta`] type.
|
/// Creates a [`ListMeta`] type.
|
||||||
///
|
///
|
||||||
/// Specifying `expected_size` will be used to ensure that values of this
|
/// Specifying `expected_size` will be used to ensure that values of this
|
||||||
|
@ -1275,6 +1407,25 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
ListMeta::new(of_type, expected_size)
|
ListMeta::new(of_type, expected_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a [`ListMeta`] information for the specified [`graphql::Type`].
|
||||||
|
///
|
||||||
|
/// Specifying `expected_size` will be used in validation to ensure that
|
||||||
|
/// values of this type matches it.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
pub fn wrap_list<'ti, T, TI>(
|
||||||
|
&mut self,
|
||||||
|
type_info: &'ti TI,
|
||||||
|
expected_size: Option<usize>,
|
||||||
|
) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, S> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
{
|
||||||
|
ListMeta::new(T::meta(self, type_info).into(), expected_size).into_meta()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [`NullableMeta`] type.
|
/// Creates a [`NullableMeta`] type.
|
||||||
pub fn build_nullable_type<T>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r>
|
pub fn build_nullable_type<T>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r>
|
||||||
where
|
where
|
||||||
|
@ -1285,6 +1436,19 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
NullableMeta::new(of_type)
|
NullableMeta::new(of_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a [`NullableMeta`] information for the specified
|
||||||
|
/// [`graphql::Type`].
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
pub fn wrap_nullable<'ti, T, TI>(&mut self, type_info: &'ti TI) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, S> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
{
|
||||||
|
NullableMeta::new(T::meta(self, type_info).into()).into_meta()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an [`ObjectMeta`] type with the given `fields`.
|
/// Creates an [`ObjectMeta`] type with the given `fields`.
|
||||||
pub fn build_object_type<T>(
|
pub fn build_object_type<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1318,6 +1482,36 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
EnumMeta::new::<T>(Cow::Owned(name.into()), values)
|
EnumMeta::new::<T>(Cow::Owned(name.into()), values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds an [`EnumMeta`] information for the specified [`graphql::Type`],
|
||||||
|
/// allowing to `customize` the created [`ScalarMeta`], and stores it in
|
||||||
|
/// this [`Registry`].
|
||||||
|
///
|
||||||
|
/// # Idempotent
|
||||||
|
///
|
||||||
|
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||||
|
/// already, then just returns it without doing anything.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
/// [`TypeName`]: resolve::TypeName
|
||||||
|
pub fn register_enum_with<'ti, T, TI>(
|
||||||
|
&mut self,
|
||||||
|
values: &[EnumValue],
|
||||||
|
type_info: &'ti TI,
|
||||||
|
customize: impl FnOnce(EnumMeta<'r, S>) -> EnumMeta<'r, S>,
|
||||||
|
) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + resolve::InputValueOwned<S>,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
self.entry_type::<T, _>(type_info)
|
||||||
|
.or_insert_with(move || {
|
||||||
|
customize(EnumMeta::new_reworked::<T>(T::type_name(type_info), values)).into_meta()
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an [`InterfaceMeta`] type with the given `fields`.
|
/// Creates an [`InterfaceMeta`] type with the given `fields`.
|
||||||
pub fn build_interface_type<T>(
|
pub fn build_interface_type<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1361,4 +1555,38 @@ impl<'r, S: 'r> Registry<'r, S> {
|
||||||
|
|
||||||
InputObjectMeta::new::<T>(Cow::Owned(name.into()), args)
|
InputObjectMeta::new::<T>(Cow::Owned(name.into()), args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds an [`InputObjectMeta`] information for the specified
|
||||||
|
/// [`graphql::Type`], allowing to `customize` the created [`ScalarMeta`],
|
||||||
|
/// and stores it in this [`Registry`].
|
||||||
|
///
|
||||||
|
/// # Idempotent
|
||||||
|
///
|
||||||
|
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||||
|
/// already, then just returns it without doing anything.
|
||||||
|
///
|
||||||
|
/// [`graphql::Type`]: resolve::Type
|
||||||
|
/// [`TypeName`]: resolve::TypeName
|
||||||
|
pub fn register_input_object_with<'ti, T, TI>(
|
||||||
|
&mut self,
|
||||||
|
fields: &[Argument<'r, S>],
|
||||||
|
type_info: &'ti TI,
|
||||||
|
customize: impl FnOnce(InputObjectMeta<'r, S>) -> InputObjectMeta<'r, S>,
|
||||||
|
) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI> + resolve::InputValueOwned<S>,
|
||||||
|
TI: ?Sized,
|
||||||
|
'ti: 'r,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
self.entry_type::<T, _>(type_info)
|
||||||
|
.or_insert_with(move || {
|
||||||
|
customize(InputObjectMeta::new_reworked::<T>(
|
||||||
|
T::type_name(type_info),
|
||||||
|
fields,
|
||||||
|
))
|
||||||
|
.into_meta()
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,6 +369,8 @@ mod threads_context_correctly {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove as should be unnecessary with generic context.
|
||||||
|
/*
|
||||||
mod dynamic_context_switching {
|
mod dynamic_context_switching {
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
@ -672,7 +674,7 @@ mod dynamic_context_switching {
|
||||||
assert_eq!(result, graphql_value!({"first": {"value": "First value"}}));
|
assert_eq!(result, graphql_value!({"first": {"value": "First value"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
mod propagates_errors_to_nullable_fields {
|
mod propagates_errors_to_nullable_fields {
|
||||||
use crate::{
|
use crate::{
|
||||||
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
|
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod interface {
|
mod interface {
|
||||||
use crate::{
|
use crate::{
|
||||||
graphql_interface, graphql_object,
|
graphql_interface, graphql_object, graphql_value,
|
||||||
schema::model::RootNode,
|
schema::model::RootNode,
|
||||||
types::scalars::{EmptyMutation, EmptySubscription},
|
types::scalars::{EmptyMutation, EmptySubscription},
|
||||||
GraphQLObject,
|
GraphQLObject,
|
||||||
|
@ -96,19 +96,16 @@ mod interface {
|
||||||
|
|
||||||
mod union {
|
mod union {
|
||||||
use crate::{
|
use crate::{
|
||||||
graphql_object, graphql_union,
|
graphql_object, graphql_value,
|
||||||
schema::model::RootNode,
|
schema::model::RootNode,
|
||||||
types::scalars::{EmptyMutation, EmptySubscription},
|
types::scalars::{EmptyMutation, EmptySubscription},
|
||||||
|
GraphQLUnion,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[graphql_union]
|
#[derive(GraphQLUnion)]
|
||||||
trait Pet {
|
enum Pet {
|
||||||
fn as_dog(&self) -> Option<&Dog> {
|
Dog(Dog),
|
||||||
None
|
Cat(Cat),
|
||||||
}
|
|
||||||
fn as_cat(&self) -> Option<&Cat> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Dog {
|
struct Dog {
|
||||||
|
@ -116,12 +113,6 @@ mod union {
|
||||||
woofs: bool,
|
woofs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pet for Dog {
|
|
||||||
fn as_dog(&self) -> Option<&Dog> {
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Dog {
|
impl Dog {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
@ -137,12 +128,6 @@ mod union {
|
||||||
meows: bool,
|
meows: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pet for Cat {
|
|
||||||
fn as_cat(&self) -> Option<&Cat> {
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Cat {
|
impl Cat {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
@ -154,13 +139,13 @@ mod union {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Schema {
|
struct Schema {
|
||||||
pets: Vec<Box<dyn Pet + Send + Sync>>,
|
pets: Vec<Pet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Schema {
|
impl Schema {
|
||||||
fn pets(&self) -> Vec<&(dyn Pet + Send + Sync)> {
|
fn pets(&self) -> &[Pet] {
|
||||||
self.pets.iter().map(|p| p.as_ref()).collect()
|
&self.pets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,11 +154,11 @@ mod union {
|
||||||
let schema = RootNode::new(
|
let schema = RootNode::new(
|
||||||
Schema {
|
Schema {
|
||||||
pets: vec![
|
pets: vec![
|
||||||
Box::new(Dog {
|
Pet::Dog(Dog {
|
||||||
name: "Odie".into(),
|
name: "Odie".into(),
|
||||||
woofs: true,
|
woofs: true,
|
||||||
}),
|
}),
|
||||||
Box::new(Cat {
|
Pet::Cat(Cat {
|
||||||
name: "Garfield".into(),
|
name: "Garfield".into(),
|
||||||
meows: false,
|
meows: false,
|
||||||
}),
|
}),
|
||||||
|
|
9
juniper/src/extract.rs
Normal file
9
juniper/src/extract.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
pub trait Extract<T: ?Sized> {
|
||||||
|
fn extract(&self) -> &T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Extract<T> for T {
|
||||||
|
fn extract(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
140
juniper/src/graphql/mod.rs
Normal file
140
juniper/src/graphql/mod.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
use crate::{behavior, resolve};
|
||||||
|
|
||||||
|
pub use crate::{
|
||||||
|
ast::InputValue,
|
||||||
|
executor::Variables,
|
||||||
|
macros::{input_value, value, vars},
|
||||||
|
resolve::Type,
|
||||||
|
value::Value,
|
||||||
|
GraphQLEnum as Enum, GraphQLScalar as Scalar,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Enum<
|
||||||
|
'inp,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
InputType<'inp, TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ OutputType<TypeInfo, Context, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_enum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub trait Interface<S>: OutputType<S>
|
||||||
|
+ resolve::TypeName
|
||||||
|
+ resolve::ConcreteTypeName
|
||||||
|
+ resolve::Value<S>
|
||||||
|
+ resolve::ValueAsync<S>
|
||||||
|
+ resolve::ConcreteValue<S>
|
||||||
|
+ resolve::ConcreteValueAsync<S>
|
||||||
|
+ resolve::Field<S>
|
||||||
|
+ resolve::FieldAsync<S>
|
||||||
|
{
|
||||||
|
fn assert_interface();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Object<S>: OutputType<S>
|
||||||
|
+ resolve::TypeName
|
||||||
|
+ resolve::ConcreteTypeName
|
||||||
|
+ resolve::Value<S>
|
||||||
|
+ resolve::ValueAsync<S>
|
||||||
|
+ resolve::Field<S>
|
||||||
|
+ resolve::FieldAsync<S>
|
||||||
|
{
|
||||||
|
fn assert_object();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub trait InputObject<
|
||||||
|
'inp,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>: InputType<'inp, TypeInfo, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_input_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Scalar<
|
||||||
|
'inp,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
InputType<'inp, TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ OutputType<TypeInfo, Context, ScalarValue, Behavior>
|
||||||
|
+ resolve::ScalarToken<ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_scalar();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ScalarAs<
|
||||||
|
'inp,
|
||||||
|
Wrapper,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
InputTypeAs<'inp, Wrapper, TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ OutputType<TypeInfo, Context, ScalarValue, Behavior>
|
||||||
|
+ resolve::ScalarToken<ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_scalar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub trait Union<S>
|
||||||
|
OutputType<S>
|
||||||
|
+ resolve::TypeName
|
||||||
|
+ resolve::ConcreteTypeName
|
||||||
|
+ resolve::Value<S>
|
||||||
|
+ resolve::ValueAsync<S>
|
||||||
|
+ resolve::ConcreteValue<S>
|
||||||
|
+ resolve::ConcreteValueAsync<S>
|
||||||
|
{
|
||||||
|
fn assert_union();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub trait InputType<
|
||||||
|
'inp,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
Type<TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ resolve::ToInputValue<ScalarValue, Behavior>
|
||||||
|
+ resolve::InputValue<'inp, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_input_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputTypeAs<
|
||||||
|
'inp,
|
||||||
|
Wrapper,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
ScalarValue: 'inp,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
Type<TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ resolve::ToInputValue<ScalarValue, Behavior>
|
||||||
|
+ resolve::InputValueAs<'inp, Wrapper, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_input_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OutputType<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>:
|
||||||
|
Type<TypeInfo, ScalarValue, Behavior>
|
||||||
|
+ resolve::Value<TypeInfo, Context, ScalarValue, Behavior>
|
||||||
|
+ resolve::ValueAsync<TypeInfo, Context, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
fn assert_output_type();
|
||||||
|
}
|
|
@ -29,19 +29,23 @@ pub use juniper_codegen::{
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
pub mod behavior;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
pub mod extract;
|
||||||
|
pub mod graphql;
|
||||||
|
pub mod http;
|
||||||
|
pub mod integrations;
|
||||||
mod introspection;
|
mod introspection;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod reflect;
|
||||||
|
pub mod resolve;
|
||||||
pub(crate) mod schema;
|
pub(crate) mod schema;
|
||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
pub mod validation;
|
pub mod validation;
|
||||||
mod value;
|
pub(crate) mod value;
|
||||||
// This needs to be public until docs have support for private modules:
|
|
||||||
// https://github.com/rust-lang/cargo/issues/1520
|
|
||||||
pub mod http;
|
|
||||||
pub mod integrations;
|
|
||||||
|
|
||||||
#[cfg(all(test, not(feature = "expose-test-schema")))]
|
#[cfg(all(test, not(feature = "expose-test-schema")))]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -71,6 +75,7 @@ pub use crate::{
|
||||||
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods,
|
FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadMethods,
|
||||||
LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables,
|
LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables,
|
||||||
},
|
},
|
||||||
|
extract::Extract,
|
||||||
introspection::IntrospectionFormat,
|
introspection::IntrospectionFormat,
|
||||||
macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult},
|
macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult},
|
||||||
parser::{ParseError, ScalarToken, Spanning},
|
parser::{ParseError, ScalarToken, Spanning},
|
||||||
|
@ -82,12 +87,12 @@ pub use crate::{
|
||||||
async_await::{GraphQLTypeAsync, GraphQLValueAsync},
|
async_await::{GraphQLTypeAsync, GraphQLValueAsync},
|
||||||
base::{Arguments, GraphQLType, GraphQLValue, TypeKind},
|
base::{Arguments, GraphQLType, GraphQLValue, TypeKind},
|
||||||
marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion},
|
marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion},
|
||||||
nullable::Nullable,
|
|
||||||
scalars::{EmptyMutation, EmptySubscription, ID},
|
scalars::{EmptyMutation, EmptySubscription, ID},
|
||||||
subscriptions::{
|
subscriptions::{
|
||||||
ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue,
|
ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue,
|
||||||
SubscriptionConnection, SubscriptionCoordinator,
|
SubscriptionConnection, SubscriptionCoordinator,
|
||||||
},
|
},
|
||||||
|
Nullable,
|
||||||
},
|
},
|
||||||
validation::RuleError,
|
validation::RuleError,
|
||||||
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
||||||
|
|
|
@ -1,45 +1,43 @@
|
||||||
//! [`graphql_input_value!`] macro implementation.
|
//! [`input_value!`] macro implementation.
|
||||||
//!
|
|
||||||
//! [`graphql_input_value!`]: graphql_input_value
|
|
||||||
|
|
||||||
/// Constructs [`InputValue`]s via JSON-like syntax.
|
/// Constructs [`graphql::InputValue`]s via JSON-like syntax.
|
||||||
///
|
///
|
||||||
/// # Differences from [`graphql_value!`]
|
/// # Differences from [`graphql_value!`]
|
||||||
///
|
///
|
||||||
/// - [`InputValue::Enum`] is constructed with `ident`, so to capture outer
|
/// - [`InputValue::Enum`] is constructed with `ident`, so to capture outer
|
||||||
/// variable as [`InputValue::Scalar`] surround it with parens: `(var)`.
|
/// variable as [`InputValue::Scalar`] surround it with parens: `(var)`.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::{graphql_input_value, graphql_value};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// # type InputValue = juniper::InputValue;
|
/// # type InputValue = graphql::InputValue;
|
||||||
/// # type Value = juniper::Value;
|
/// # type Value = graphql::Value;
|
||||||
/// #
|
/// #
|
||||||
/// const OUTER_VAR: i32 = 42;
|
/// const OUTER_VAR: i32 = 42;
|
||||||
/// assert_eq!(graphql_value!(OUTER_VAR), Value::scalar(42));
|
/// assert_eq!(graphql::value!(OUTER_VAR), Value::scalar(42));
|
||||||
/// assert_eq!(graphql_input_value!(OUTER_VAR), InputValue::enum_value("OUTER_VAR"));
|
/// assert_eq!(graphql::input_value!(OUTER_VAR), InputValue::enum_value("OUTER_VAR"));
|
||||||
/// assert_eq!(graphql_input_value!((OUTER_VAR)), InputValue::scalar(42));
|
/// assert_eq!(graphql::input_value!((OUTER_VAR)), InputValue::scalar(42));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// - [`InputValue::Variable`] is constructed by prefixing `ident` with `@`.
|
/// - [`InputValue::Variable`] is constructed by prefixing `ident` with `@`.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::graphql_input_value;
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// # type InputValue = juniper::InputValue;
|
/// # type InputValue = graphql::InputValue;
|
||||||
/// #
|
/// #
|
||||||
/// assert_eq!(graphql_input_value!(@var), InputValue::variable("var"));
|
/// assert_eq!(graphql::input_value!(@var), InputValue::variable("var"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// - [`InputValue::Object`] key should implement [`Into`]`<`[`String`]`>`.
|
/// - [`InputValue::Object`] key should implement [`Into`]`<`[`String`]`>`.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// #
|
/// #
|
||||||
/// # use juniper::{graphql_input_value, InputValue};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// let code = 200;
|
/// let code = 200;
|
||||||
/// let features = vec!["key", "value"];
|
/// let features = vec!["key", "value"];
|
||||||
/// let key: Cow<'static, str> = "key".into();
|
/// let key: Cow<'static, str> = "key".into();
|
||||||
///
|
///
|
||||||
/// let value: InputValue = graphql_input_value!({
|
/// let value: graphql::InputValue = graphql::input_value!({
|
||||||
/// "code": code,
|
/// "code": code,
|
||||||
/// "success": code == 200,
|
/// "success": code == 200,
|
||||||
/// "payload": {
|
/// "payload": {
|
||||||
|
@ -55,35 +53,35 @@
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::{graphql_input_value, InputValue};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// # type V = InputValue;
|
/// # type V = graphql::InputValue;
|
||||||
/// #
|
/// #
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!(null);
|
/// graphql::input_value!(null);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!(1234);
|
/// graphql::input_value!(1234);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!("test");
|
/// graphql::input_value!("test");
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!([1234, "test", true]);
|
/// graphql::input_value!([1234, "test", true]);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!({"key": "value", "foo": 1234});
|
/// graphql::input_value!({"key": "value", "foo": 1234});
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!({"key": ENUM});
|
/// graphql::input_value!({"key": ENUM});
|
||||||
/// let captured_var = 42;
|
/// let captured_var = 42;
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!({"key": (captured_var)});
|
/// graphql::input_value!({"key": (captured_var)});
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_input_value!({"key": @variable});
|
/// graphql::input_value!({"key": @variable});
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`InputValue`]: crate::InputValue
|
/// [`graphql::InputValue`]: crate::graphql::InputValue
|
||||||
/// [`InputValue::Enum`]: crate::InputValue::Enum
|
/// [`InputValue::Enum`]: crate::graphql::InputValue::Enum
|
||||||
/// [`InputValue::List`]: crate::InputValue::List
|
/// [`InputValue::List`]: crate::graphql::InputValue::List
|
||||||
/// [`InputValue::Object`]: crate::InputValue::Object
|
/// [`InputValue::Object`]: crate::graphql::InputValue::Object
|
||||||
/// [`InputValue::Scalar`]: crate::InputValue::Scalar
|
/// [`InputValue::Scalar`]: crate::graphql::InputValue::Scalar
|
||||||
/// [`InputValue::Variable`]: crate::InputValue::Variable
|
/// [`InputValue::Variable`]: crate::graphql::InputValue::Variable
|
||||||
/// [`Spanning::unlocated`]: crate::Spanning::unlocated
|
/// [`Spanning::unlocated`]: crate::Spanning::unlocated
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graphql_input_value {
|
macro_rules! graphql_input_value {
|
||||||
|
@ -93,90 +91,90 @@ macro_rules! graphql_input_value {
|
||||||
|
|
||||||
// Done with trailing comma.
|
// Done with trailing comma.
|
||||||
(@@array [$($elems:expr,)*]) => {
|
(@@array [$($elems:expr,)*]) => {
|
||||||
$crate::InputValue::list(vec![
|
$crate::graphql::InputValue::list(::std::vec![
|
||||||
$( $elems, )*
|
$( $elems, )*
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Done without trailing comma.
|
// Done without trailing comma.
|
||||||
(@@array [$($elems:expr),*]) => {
|
(@@array [$($elems:expr),*]) => {
|
||||||
$crate::InputValue::list(vec![
|
$crate::graphql::InputValue::list(::std::vec![
|
||||||
$( $elems, )*
|
$( $elems, )*
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `null`.
|
// Next element is `null`.
|
||||||
(@@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!(null)] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!(null)] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `None`.
|
// Next element is `None`.
|
||||||
(@@array [$($elems:expr,)*] None $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] None $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!(None)] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!(None)] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is a variable.
|
// Next element is a variable.
|
||||||
(@@array [$($elems:expr,)*] @$var:ident $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] @$var:ident $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!(@$var)] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!(@$var)] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Next element is an array.
|
// Next element is an array.
|
||||||
(@@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!([$($array)*])] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!([$($array)*])] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is a map.
|
// Next element is a map.
|
||||||
(@@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!({$($map)*})] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!({$($map)*})] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `true`, `false` or enum ident followed by comma.
|
// Next element is `true`, `false` or enum ident followed by comma.
|
||||||
(@@array [$($elems:expr,)*] $ident:ident, $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] $ident:ident, $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!($ident),] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!($ident),] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `true`, `false` or enum ident without trailing comma.
|
// Next element is `true`, `false` or enum ident without trailing comma.
|
||||||
(@@array [$($elems:expr,)*] $last:ident ) => {
|
(@@array [$($elems:expr,)*] $last:ident ) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!($last)]
|
@@array [$($elems,)* $crate::graphql::input_value!($last)]
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is an expression followed by comma.
|
// Next element is an expression followed by comma.
|
||||||
(@@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
(@@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!($next),] $($rest)*
|
@@array [$($elems,)* $crate::graphql::input_value!($next),] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Last element is an expression with no trailing comma.
|
// Last element is an expression with no trailing comma.
|
||||||
(@@array [$($elems:expr,)*] $last:expr) => {
|
(@@array [$($elems:expr,)*] $last:expr) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@array [$($elems,)* $crate::graphql_input_value!($last)]
|
@@array [$($elems,)* $crate::graphql::input_value!($last)]
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Comma after the most recent element.
|
// Comma after the most recent element.
|
||||||
(@@array [$($elems:expr),*] , $($rest:tt)*) => {
|
(@@array [$($elems:expr),*] , $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(@@array [$($elems,)*] $($rest)*)
|
$crate::graphql::input_value!(@@array [$($elems,)*] $($rest)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unexpected token after most recent element.
|
// Unexpected token after most recent element.
|
||||||
(@@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
(@@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(@unexpected $unexpected)
|
$crate::graphql::input_value!(@unexpected $unexpected)
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////
|
////////////
|
||||||
|
@ -192,12 +190,12 @@ macro_rules! graphql_input_value {
|
||||||
$crate::Spanning::unlocated(($($key)+).into()),
|
$crate::Spanning::unlocated(($($key)+).into()),
|
||||||
$crate::Spanning::unlocated($value),
|
$crate::Spanning::unlocated($value),
|
||||||
));
|
));
|
||||||
$crate::graphql_input_value!(@@object $object () ($($rest)*) ($($rest)*));
|
$crate::graphql::input_value!(@@object $object () ($($rest)*) ($($rest)*));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Current entry followed by unexpected token.
|
// Current entry followed by unexpected token.
|
||||||
(@@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
(@@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
||||||
$crate::graphql_input_value!(@unexpected $unexpected);
|
$crate::graphql::input_value!(@unexpected $unexpected);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert the last entry without trailing comma.
|
// Insert the last entry without trailing comma.
|
||||||
|
@ -210,114 +208,114 @@ macro_rules! graphql_input_value {
|
||||||
|
|
||||||
// Next value is `null`.
|
// Next value is `null`.
|
||||||
(@@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(null)) $($rest)*
|
($crate::graphql::input_value!(null)) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is `None`.
|
// Next value is `None`.
|
||||||
(@@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(None)) $($rest)*
|
($crate::graphql::input_value!(None)) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is a variable.
|
// Next value is a variable.
|
||||||
(@@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(@$var)) $($rest)*
|
($crate::graphql::input_value!(@$var)) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is an array.
|
// Next value is an array.
|
||||||
(@@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!([$($array)*])) $($rest)*
|
($crate::graphql::input_value!([$($array)*])) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is a map.
|
// Next value is a map.
|
||||||
(@@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!({$($map)*})) $($rest)*
|
($crate::graphql::input_value!({$($map)*})) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is `true`, `false` or enum ident followed by comma.
|
// Next value is `true`, `false` or enum ident followed by comma.
|
||||||
(@@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($ident)) , $($rest)*
|
($crate::graphql::input_value!($ident)) , $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is `true`, `false` or enum ident without trailing comma.
|
// Next value is `true`, `false` or enum ident without trailing comma.
|
||||||
(@@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($last))
|
($crate::graphql::input_value!($last))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is an expression followed by comma.
|
// Next value is an expression followed by comma.
|
||||||
(@@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($value)) , $($rest)*
|
($crate::graphql::input_value!($value)) , $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Last value is an expression with no trailing comma.
|
// Last value is an expression with no trailing comma.
|
||||||
(@@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($value))
|
($crate::graphql::input_value!($value))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Missing value for last entry. Trigger a reasonable error message.
|
// Missing value for last entry. Trigger a reasonable error message.
|
||||||
(@@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_input_value!();
|
$crate::graphql::input_value!();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Missing colon and value for last entry. Trigger a reasonable error
|
// Missing colon and value for last entry. Trigger a reasonable error
|
||||||
// message.
|
// message.
|
||||||
(@@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
(@@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_input_value!();
|
$crate::graphql::input_value!();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Misplaced colon. Trigger a reasonable error message.
|
// Misplaced colon. Trigger a reasonable error message.
|
||||||
(@@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
(@@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `:`".
|
// Takes no arguments so "no rules expected the token `:`".
|
||||||
$crate::graphql_input_value!(@unexpected $colon);
|
$crate::graphql::input_value!(@unexpected $colon);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Found a comma inside a key. Trigger a reasonable error message.
|
// Found a comma inside a key. Trigger a reasonable error message.
|
||||||
(@@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
(@@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `,`".
|
// Takes no arguments so "no rules expected the token `,`".
|
||||||
$crate::graphql_input_value!(@unexpected $comma);
|
$crate::graphql::input_value!(@unexpected $comma);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Key is fully parenthesized. This avoids `clippy::double_parens` false
|
// Key is fully parenthesized. This avoids `clippy::double_parens` false
|
||||||
// positives because the parenthesization may be necessary here.
|
// positives because the parenthesization may be necessary here.
|
||||||
(@@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
($key)
|
($key)
|
||||||
(: $($rest)*) (: $($rest)*)
|
(: $($rest)*) (: $($rest)*)
|
||||||
|
@ -326,12 +324,12 @@ macro_rules! graphql_input_value {
|
||||||
|
|
||||||
// Refuse to absorb colon token into key expression.
|
// Refuse to absorb colon token into key expression.
|
||||||
(@@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(@@unexpected $($unexpected)+);
|
$crate::graphql::input_value!(@@unexpected $($unexpected)+);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Munch a token into the current key.
|
// Munch a token into the current key.
|
||||||
(@@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
(@@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_input_value!(
|
$crate::graphql::input_value!(
|
||||||
@@object $object
|
@@object $object
|
||||||
($($key)* $tt)
|
($($key)* $tt)
|
||||||
($($rest)*) ($($rest)*)
|
($($rest)*) ($($rest)*)
|
||||||
|
@ -349,109 +347,107 @@ macro_rules! graphql_input_value {
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
([ $($arr:tt)* ]$(,)?) => {
|
([ $($arr:tt)* ]$(,)?) => {
|
||||||
$crate::graphql_input_value!(@@array [] $($arr)*)
|
$crate::graphql::input_value!(@@array [] $($arr)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
({}$(,)?) => {
|
({}$(,)?) => {
|
||||||
$crate::InputValue::parsed_object(vec![])
|
$crate::graphql::InputValue::parsed_object(vec![])
|
||||||
};
|
};
|
||||||
|
|
||||||
({ $($map:tt)+ }$(,)?) => {
|
({ $($map:tt)+ }$(,)?) => {
|
||||||
$crate::InputValue::parsed_object({
|
$crate::graphql::InputValue::parsed_object({
|
||||||
let mut object = vec![];
|
let mut object = vec![];
|
||||||
$crate::graphql_input_value!(@@object object () ($($map)*) ($($map)*));
|
$crate::graphql::input_value!(@@object object () ($($map)*) ($($map)*));
|
||||||
object
|
object
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
(null$(,)?) => ($crate::InputValue::null());
|
(null$(,)?) => ($crate::graphql::InputValue::null());
|
||||||
|
|
||||||
(None$(,)?) => ($crate::InputValue::null());
|
(None$(,)?) => ($crate::graphql::InputValue::null());
|
||||||
|
|
||||||
(true$(,)?) => ($crate::InputValue::from(true));
|
(true$(,)?) => ($crate::graphql::InputValue::from(true));
|
||||||
|
|
||||||
(false$(,)?) => ($crate::InputValue::from(false));
|
(false$(,)?) => ($crate::graphql::InputValue::from(false));
|
||||||
|
|
||||||
(@$var:ident$(,)?) => ($crate::InputValue::variable(stringify!($var)));
|
(@$var:ident$(,)?) => ($crate::graphql::InputValue::variable(stringify!($var)));
|
||||||
|
|
||||||
($enum:ident$(,)?) => ($crate::InputValue::enum_value(stringify!($enum)));
|
($enum:ident$(,)?) => ($crate::graphql::InputValue::enum_value(stringify!($enum)));
|
||||||
|
|
||||||
(($e:expr)$(,)?) => ($crate::InputValue::from($e));
|
(($e:expr)$(,)?) => ($crate::graphql::InputValue::from($e));
|
||||||
|
|
||||||
($e:expr$(,)?) => ($crate::InputValue::from($e));
|
($e:expr$(,)?) => ($crate::graphql::InputValue::from($e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use graphql_input_value as input_value;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use indexmap::{indexmap, IndexMap};
|
use indexmap::{indexmap, IndexMap};
|
||||||
|
|
||||||
type V = crate::InputValue;
|
use crate::graphql;
|
||||||
|
|
||||||
|
use super::input_value;
|
||||||
|
|
||||||
|
type V = graphql::InputValue;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn null() {
|
fn null() {
|
||||||
assert_eq!(graphql_input_value!(null), V::Null);
|
assert_eq!(input_value!(null), V::Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scalar() {
|
fn scalar() {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
assert_eq!(graphql_input_value!(1), V::scalar(1));
|
assert_eq!(input_value!(1), V::scalar(1));
|
||||||
assert_eq!(graphql_input_value!("val"), V::scalar("val"));
|
assert_eq!(input_value!("val"), V::scalar("val"));
|
||||||
assert_eq!(graphql_input_value!(1.34), V::scalar(1.34));
|
assert_eq!(input_value!(1.34), V::scalar(1.34));
|
||||||
assert_eq!(graphql_input_value!(false), V::scalar(false));
|
assert_eq!(input_value!(false), V::scalar(false));
|
||||||
assert_eq!(graphql_input_value!(1 + 2), V::scalar(3));
|
assert_eq!(input_value!(1 + 2), V::scalar(3));
|
||||||
assert_eq!(graphql_input_value!((val)), V::scalar(42));
|
assert_eq!(input_value!((val)), V::scalar(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#enum() {
|
fn r#enum() {
|
||||||
assert_eq!(graphql_input_value!(ENUM), V::enum_value("ENUM"));
|
assert_eq!(input_value!(ENUM), V::enum_value("ENUM"));
|
||||||
assert_eq!(graphql_input_value!(lowercase), V::enum_value("lowercase"));
|
assert_eq!(input_value!(lowercase), V::enum_value("lowercase"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variable() {
|
fn variable() {
|
||||||
assert_eq!(graphql_input_value!(@var), V::variable("var"));
|
assert_eq!(input_value!(@var), V::variable("var"));
|
||||||
assert_eq!(graphql_input_value!(@array), V::variable("array"));
|
assert_eq!(input_value!(@array), V::variable("array"));
|
||||||
assert_eq!(graphql_input_value!(@object), V::variable("object"));
|
assert_eq!(input_value!(@object), V::variable("object"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list() {
|
fn list() {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(graphql_input_value!([]), V::list(vec![]));
|
assert_eq!(input_value!([]), V::list(vec![]));
|
||||||
|
|
||||||
assert_eq!(graphql_input_value!([null]), V::list(vec![V::Null]));
|
assert_eq!(input_value!([null]), V::list(vec![V::Null]));
|
||||||
|
|
||||||
assert_eq!(graphql_input_value!([1]), V::list(vec![V::scalar(1)]));
|
assert_eq!(input_value!([1]), V::list(vec![V::scalar(1)]));
|
||||||
assert_eq!(graphql_input_value!([1 + 2]), V::list(vec![V::scalar(3)]));
|
assert_eq!(input_value!([1 + 2]), V::list(vec![V::scalar(3)]));
|
||||||
assert_eq!(graphql_input_value!([(val)]), V::list(vec![V::scalar(42)]));
|
assert_eq!(input_value!([(val)]), V::list(vec![V::scalar(42)]));
|
||||||
|
|
||||||
|
assert_eq!(input_value!([ENUM]), V::list(vec![V::enum_value("ENUM")]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([ENUM]),
|
input_value!([lowercase]),
|
||||||
V::list(vec![V::enum_value("ENUM")]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
graphql_input_value!([lowercase]),
|
|
||||||
V::list(vec![V::enum_value("lowercase")]),
|
V::list(vec![V::enum_value("lowercase")]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(input_value!([@var]), V::list(vec![V::variable("var")]),);
|
||||||
|
assert_eq!(input_value!([@array]), V::list(vec![V::variable("array")]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([@var]),
|
input_value!([@object]),
|
||||||
V::list(vec![V::variable("var")]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
graphql_input_value!([@array]),
|
|
||||||
V::list(vec![V::variable("array")]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
graphql_input_value!([@object]),
|
|
||||||
V::list(vec![V::variable("object")]),
|
V::list(vec![V::variable("object")]),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([1, [2], 3]),
|
input_value!([1, [2], 3]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(1),
|
V::scalar(1),
|
||||||
V::list(vec![V::scalar(2)]),
|
V::list(vec![V::scalar(2)]),
|
||||||
|
@ -459,7 +455,7 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([1, [2 + 3], 3]),
|
input_value!([1, [2 + 3], 3]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(1),
|
V::scalar(1),
|
||||||
V::list(vec![V::scalar(5)]),
|
V::list(vec![V::scalar(5)]),
|
||||||
|
@ -467,7 +463,7 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([1, [ENUM], (val)]),
|
input_value!([1, [ENUM], (val)]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(1),
|
V::scalar(1),
|
||||||
V::list(vec![V::enum_value("ENUM")]),
|
V::list(vec![V::enum_value("ENUM")]),
|
||||||
|
@ -475,7 +471,7 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([1 + 2, [(val)], @val]),
|
input_value!([1 + 2, [(val)], @val]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(3),
|
V::scalar(3),
|
||||||
V::list(vec![V::scalar(42)]),
|
V::list(vec![V::scalar(42)]),
|
||||||
|
@ -483,7 +479,7 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!([1, [@val], ENUM]),
|
input_value!([1, [@val], ENUM]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(1),
|
V::scalar(1),
|
||||||
V::list(vec![V::variable("val")]),
|
V::list(vec![V::variable("val")]),
|
||||||
|
@ -495,68 +491,65 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn object() {
|
fn object() {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
assert_eq!(
|
assert_eq!(input_value!({}), V::object(IndexMap::<String, _>::new()));
|
||||||
graphql_input_value!({}),
|
|
||||||
V::object(IndexMap::<String, _>::new()),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": null }),
|
input_value!({ "key": null }),
|
||||||
V::object(indexmap! {"key" => V::Null}),
|
V::object(indexmap! {"key" => V::Null}),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": 123}),
|
input_value!({"key": 123}),
|
||||||
V::object(indexmap! {"key" => V::scalar(123)}),
|
V::object(indexmap! {"key" => V::scalar(123)}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": 1 + 2}),
|
input_value!({"key": 1 + 2}),
|
||||||
V::object(indexmap! {"key" => V::scalar(3)}),
|
V::object(indexmap! {"key" => V::scalar(3)}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": (val) }),
|
input_value!({ "key": (val) }),
|
||||||
V::object(indexmap! {"key" => V::scalar(42)}),
|
V::object(indexmap! {"key" => V::scalar(42)}),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": []}),
|
input_value!({"key": []}),
|
||||||
V::object(indexmap! {"key" => V::list(vec![])}),
|
V::object(indexmap! {"key" => V::list(vec![])}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": [null] }),
|
input_value!({ "key": [null] }),
|
||||||
V::object(indexmap! {"key" => V::list(vec![V::Null])}),
|
V::object(indexmap! {"key" => V::list(vec![V::Null])}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": [1] }),
|
input_value!({"key": [1] }),
|
||||||
V::object(indexmap! {"key" => V::list(vec![V::scalar(1)])}),
|
V::object(indexmap! {"key" => V::list(vec![V::scalar(1)])}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": [1 + 2] }),
|
input_value!({"key": [1 + 2] }),
|
||||||
V::object(indexmap! {"key" => V::list(vec![V::scalar(3)])}),
|
V::object(indexmap! {"key" => V::list(vec![V::scalar(3)])}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": [(val)] }),
|
input_value!({ "key": [(val)] }),
|
||||||
V::object(indexmap! {"key" => V::list(vec![V::scalar(42)])}),
|
V::object(indexmap! {"key" => V::list(vec![V::scalar(42)])}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": ENUM }),
|
input_value!({ "key": ENUM }),
|
||||||
V::object(indexmap! {"key" => V::enum_value("ENUM")}),
|
V::object(indexmap! {"key" => V::enum_value("ENUM")}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({ "key": lowercase }),
|
input_value!({ "key": lowercase }),
|
||||||
V::object(indexmap! {"key" => V::enum_value("lowercase")}),
|
V::object(indexmap! {"key" => V::enum_value("lowercase")}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": @val}),
|
input_value!({"key": @val}),
|
||||||
V::object(indexmap! {"key" => V::variable("val")}),
|
V::object(indexmap! {"key" => V::variable("val")}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({"key": @array }),
|
input_value!({"key": @array }),
|
||||||
V::object(indexmap! {"key" => V::variable("array")}),
|
V::object(indexmap! {"key" => V::variable("array")}),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_input_value!({
|
input_value!({
|
||||||
"inner": {
|
"inner": {
|
||||||
"key1": (val),
|
"key1": (val),
|
||||||
"key2": "val",
|
"key2": "val",
|
||||||
|
@ -606,8 +599,8 @@ mod tests {
|
||||||
fn option() {
|
fn option() {
|
||||||
let val = Some(42);
|
let val = Some(42);
|
||||||
|
|
||||||
assert_eq!(graphql_input_value!(None), V::Null);
|
assert_eq!(input_value!(None), V::Null);
|
||||||
assert_eq!(graphql_input_value!(Some(42)), V::scalar(42));
|
assert_eq!(input_value!(Some(42)), V::scalar(42));
|
||||||
assert_eq!(graphql_input_value!((val)), V::scalar(42));
|
assert_eq!(input_value!((val)), V::scalar(42));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
//! [`graphql_value!`] macro implementation.
|
//! [`value!`] macro implementation.
|
||||||
//!
|
|
||||||
//! [`graphql_value!`]: graphql_value
|
|
||||||
|
|
||||||
/// Constructs [`Value`]s via JSON-like syntax.
|
/// Constructs [`graphql::Value`]s via JSON-like syntax.
|
||||||
///
|
///
|
||||||
/// [`Value`] objects are used mostly when creating custom errors from fields.
|
/// [`graphql::Value`] objects are used mostly when creating custom errors from
|
||||||
|
/// fields.
|
||||||
///
|
///
|
||||||
/// [`Value::Object`] key should implement [`AsRef`]`<`[`str`]`>`.
|
/// [`Value::Object`] key should implement [`AsRef`]`<`[`str`]`>`.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::{graphql_value, Value};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// let code = 200;
|
/// let code = 200;
|
||||||
/// let features = ["key", "value"];
|
/// let features = ["key", "value"];
|
||||||
///
|
///
|
||||||
/// let value: Value = graphql_value!({
|
/// let value: graphql::Value = graphql::value!({
|
||||||
/// "code": code,
|
/// "code": code,
|
||||||
/// "success": code == 200,
|
/// "success": code == 200,
|
||||||
/// "payload": {
|
/// "payload": {
|
||||||
|
@ -26,24 +25,24 @@
|
||||||
///
|
///
|
||||||
/// Resulting JSON will look just like what you passed in.
|
/// Resulting JSON will look just like what you passed in.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::{graphql_value, DefaultScalarValue, Value};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// # type V = Value<DefaultScalarValue>;
|
/// # type V = graphql::Value;
|
||||||
/// #
|
/// #
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_value!(null);
|
/// graphql::value!(null);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_value!(1234);
|
/// graphql::value!(1234);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_value!("test");
|
/// graphql::value!("test");
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_value!([1234, "test", true]);
|
/// graphql::value!([1234, "test", true]);
|
||||||
/// # let _: V =
|
/// # let _: V =
|
||||||
/// graphql_value!({"key": "value", "foo": 1234});
|
/// graphql::value!({"key": "value", "foo": 1234});
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Value`]: crate::Value
|
/// [`graphql::Value`]: crate::graphql::Value
|
||||||
/// [`Value::Object`]: crate::Value::Object
|
/// [`Value::Object`]: crate::graphql::Value::Object
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graphql_value {
|
macro_rules! graphql_value {
|
||||||
///////////
|
///////////
|
||||||
|
@ -52,68 +51,68 @@ macro_rules! graphql_value {
|
||||||
|
|
||||||
// Done with trailing comma.
|
// Done with trailing comma.
|
||||||
(@array [$($elems:expr,)*]) => {
|
(@array [$($elems:expr,)*]) => {
|
||||||
$crate::Value::list(vec![
|
$crate::graphql::Value::list(::std::vec![
|
||||||
$( $elems, )*
|
$( $elems, )*
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Done without trailing comma.
|
// Done without trailing comma.
|
||||||
(@array [$($elems:expr),*]) => {
|
(@array [$($elems:expr),*]) => {
|
||||||
$crate::Value::list(vec![
|
$crate::graphql::Value::list(::std::vec![
|
||||||
$( $crate::graphql_value!($elems), )*
|
$( $crate::graphql::value!($elems), )*
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `null`.
|
// Next element is `null`.
|
||||||
(@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
(@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!(null)] $($rest)*
|
@array [$($elems,)* $crate::graphql::value!(null)] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is `None`.
|
// Next element is `None`.
|
||||||
(@array [$($elems:expr,)*] None $($rest:tt)*) => {
|
(@array [$($elems:expr,)*] None $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!(None)] $($rest)*
|
@array [$($elems,)* $crate::graphql::value!(None)] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is an array.
|
// Next element is an array.
|
||||||
(@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
(@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!([$($array)*])] $($rest)*
|
@array [$($elems,)* $crate::graphql::value!([$($array)*])] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is a map.
|
// Next element is a map.
|
||||||
(@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
(@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!({$($map)*})] $($rest)*
|
@array [$($elems,)* $crate::graphql::value!({$($map)*})] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next element is an expression followed by comma.
|
// Next element is an expression followed by comma.
|
||||||
(@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
(@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!($next),] $($rest)*
|
@array [$($elems,)* $crate::graphql::value!($next),] $($rest)*
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Last element is an expression with no trailing comma.
|
// Last element is an expression with no trailing comma.
|
||||||
(@array [$($elems:expr,)*] $last:expr) => {
|
(@array [$($elems:expr,)*] $last:expr) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@array [$($elems,)* $crate::graphql_value!($last)]
|
@array [$($elems,)* $crate::graphql::value!($last)]
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Comma after the most recent element.
|
// Comma after the most recent element.
|
||||||
(@array [$($elems:expr),*] , $($rest:tt)*) => {
|
(@array [$($elems:expr),*] , $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(@array [$($elems,)*] $($rest)*)
|
$crate::graphql::value!(@array [$($elems,)*] $($rest)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unexpected token after most recent element.
|
// Unexpected token after most recent element.
|
||||||
(@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
(@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(@unexpected $unexpected)
|
$crate::graphql::value!(@unexpected $unexpected)
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////
|
////////////
|
||||||
|
@ -126,12 +125,12 @@ macro_rules! graphql_value {
|
||||||
// Insert the current entry followed by trailing comma.
|
// Insert the current entry followed by trailing comma.
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
||||||
let _ = $object.add_field(($($key)+), $value);
|
let _ = $object.add_field(($($key)+), $value);
|
||||||
$crate::graphql_value!(@object $object () ($($rest)*) ($($rest)*));
|
$crate::graphql::value!(@object $object () ($($rest)*) ($($rest)*));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Current entry followed by unexpected token.
|
// Current entry followed by unexpected token.
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
||||||
$crate::graphql_value!(@unexpected $unexpected);
|
$crate::graphql::value!(@unexpected $unexpected);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert the last entry without trailing comma.
|
// Insert the last entry without trailing comma.
|
||||||
|
@ -141,97 +140,97 @@ macro_rules! graphql_value {
|
||||||
|
|
||||||
// Next value is `null`.
|
// Next value is `null`.
|
||||||
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!(null)) $($rest)*
|
($crate::graphql::value!(null)) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is `None`.
|
// Next value is `None`.
|
||||||
(@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!(None)) $($rest)*
|
($crate::graphql::value!(None)) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is an array.
|
// Next value is an array.
|
||||||
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!([$($array)*])) $($rest)*
|
($crate::graphql::value!([$($array)*])) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is a map.
|
// Next value is a map.
|
||||||
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!({$($map)*})) $($rest)*
|
($crate::graphql::value!({$($map)*})) $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Next value is an expression followed by comma.
|
// Next value is an expression followed by comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!($value)) , $($rest)*
|
($crate::graphql::value!($value)) , $($rest)*
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Last value is an expression with no trailing comma.
|
// Last value is an expression with no trailing comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_value!($value))
|
($crate::graphql::value!($value))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Missing value for last entry. Trigger a reasonable error message.
|
// Missing value for last entry. Trigger a reasonable error message.
|
||||||
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_value!();
|
$crate::graphql::value!();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Missing colon and value for last entry. Trigger a reasonable error
|
// Missing colon and value for last entry. Trigger a reasonable error
|
||||||
// message.
|
// message.
|
||||||
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_value!();
|
$crate::graphql::value!();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Misplaced colon. Trigger a reasonable error message.
|
// Misplaced colon. Trigger a reasonable error message.
|
||||||
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `:`".
|
// Takes no arguments so "no rules expected the token `:`".
|
||||||
$crate::graphql_value!(@unexpected $colon);
|
$crate::graphql::value!(@unexpected $colon);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Found a comma inside a key. Trigger a reasonable error message.
|
// Found a comma inside a key. Trigger a reasonable error message.
|
||||||
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `,`".
|
// Takes no arguments so "no rules expected the token `,`".
|
||||||
$crate::graphql_value!(@unexpected $comma);
|
$crate::graphql::value!(@unexpected $comma);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Key is fully parenthesized. This avoids `clippy::double_parens` false
|
// Key is fully parenthesized. This avoids `clippy::double_parens` false
|
||||||
// positives because the parenthesization may be necessary here.
|
// positives because the parenthesization may be necessary here.
|
||||||
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(@object $object ($key) (: $($rest)*) (: $($rest)*));
|
$crate::graphql::value!(@object $object ($key) (: $($rest)*) (: $($rest)*));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Refuse to absorb colon token into key expression.
|
// Refuse to absorb colon token into key expression.
|
||||||
(@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
(@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
||||||
$crate::graphql_value!(@unexpected $($unexpected)+);
|
$crate::graphql::value!(@unexpected $($unexpected)+);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Munch a token into the current key.
|
// Munch a token into the current key.
|
||||||
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_value!(
|
$crate::graphql::value!(
|
||||||
@object $object
|
@object $object
|
||||||
($($key)* $tt)
|
($($key)* $tt)
|
||||||
($($rest)*) ($($rest)*)
|
($($rest)*) ($($rest)*)
|
||||||
|
@ -249,63 +248,70 @@ macro_rules! graphql_value {
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
([ $($arr:tt)* ]$(,)?) => {
|
([ $($arr:tt)* ]$(,)?) => {
|
||||||
$crate::graphql_value!(@array [] $($arr)*)
|
$crate::graphql::value!(@array [] $($arr)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
({}$(,)?) => {
|
({}$(,)?) => {
|
||||||
$crate::Value::object($crate::Object::with_capacity(0))
|
$crate::graphql::Value::object($crate::Object::with_capacity(0))
|
||||||
};
|
};
|
||||||
|
|
||||||
({ $($map:tt)+ }$(,)?) => {
|
({ $($map:tt)+ }$(,)?) => {
|
||||||
$crate::Value::object({
|
$crate::graphql::Value::object({
|
||||||
let mut object = $crate::Object::with_capacity(0);
|
let mut object = $crate::Object::with_capacity(0);
|
||||||
$crate::graphql_value!(@object object () ($($map)*) ($($map)*));
|
$crate::graphql::value!(@object object () ($($map)*) ($($map)*));
|
||||||
object
|
object
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
(null$(,)?) => ($crate::Value::null());
|
(null$(,)?) => ($crate::graphql::Value::null());
|
||||||
|
|
||||||
(None$(,)?) => ($crate::Value::null());
|
(None$(,)?) => ($crate::graphql::Value::null());
|
||||||
|
|
||||||
($e:expr$(,)?) => ($crate::Value::from($e));
|
($e:expr$(,)?) => ($crate::graphql::Value::from($e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use graphql_value as value;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
type V = crate::Value;
|
use crate::graphql;
|
||||||
|
|
||||||
|
use super::value;
|
||||||
|
|
||||||
|
type V = graphql::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn null() {
|
fn null() {
|
||||||
assert_eq!(graphql_value!(null), V::Null);
|
assert_eq!(value!(null), V::Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scalar() {
|
fn scalar() {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(graphql_value!(1), V::scalar(1));
|
assert_eq!(value!(1), V::scalar(1));
|
||||||
assert_eq!(graphql_value!("val"), V::scalar("val"));
|
assert_eq!(value!("val"), V::scalar("val"));
|
||||||
assert_eq!(graphql_value!(1.34), V::scalar(1.34));
|
assert_eq!(value!(1.34), V::scalar(1.34));
|
||||||
assert_eq!(graphql_value!(false), V::scalar(false));
|
assert_eq!(value!(false), V::scalar(false));
|
||||||
assert_eq!(graphql_value!(1 + 2), V::scalar(3));
|
assert_eq!(value!(1 + 2), V::scalar(3));
|
||||||
assert_eq!(graphql_value!(val), V::scalar(42));
|
assert_eq!(value!(val), V::scalar(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list() {
|
fn list() {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(graphql_value!([]), V::list(vec![]));
|
assert_eq!(value!([]), V::list(vec![]));
|
||||||
|
|
||||||
assert_eq!(graphql_value!([null]), V::list(vec![V::Null]));
|
assert_eq!(value!([null]), V::list(vec![V::Null]));
|
||||||
|
|
||||||
assert_eq!(graphql_value!([1]), V::list(vec![V::scalar(1)]));
|
assert_eq!(value!([1]), V::list(vec![V::scalar(1)]));
|
||||||
assert_eq!(graphql_value!([1 + 2]), V::list(vec![V::scalar(3)]));
|
assert_eq!(value!([1 + 2]), V::list(vec![V::scalar(3)]));
|
||||||
assert_eq!(graphql_value!([val]), V::list(vec![V::scalar(42)]));
|
assert_eq!(value!([val]), V::list(vec![V::scalar(42)]));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!([1, [2], 3]),
|
value!([1, [2], 3]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar(1),
|
V::scalar(1),
|
||||||
V::list(vec![V::scalar(2)]),
|
V::list(vec![V::scalar(2)]),
|
||||||
|
@ -313,7 +319,7 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!(["string", [2 + 3], true]),
|
value!(["string", [2 + 3], true]),
|
||||||
V::list(vec![
|
V::list(vec![
|
||||||
V::scalar("string"),
|
V::scalar("string"),
|
||||||
V::list(vec![V::scalar(5)]),
|
V::list(vec![V::scalar(5)]),
|
||||||
|
@ -327,31 +333,31 @@ mod tests {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({}),
|
value!({}),
|
||||||
V::object(Vec::<(String, _)>::new().into_iter().collect()),
|
V::object(Vec::<(String, _)>::new().into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": null }),
|
value!({ "key": null }),
|
||||||
V::object(vec![("key", V::Null)].into_iter().collect()),
|
V::object(vec![("key", V::Null)].into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": 123 }),
|
value!({ "key": 123 }),
|
||||||
V::object(vec![("key", V::scalar(123))].into_iter().collect()),
|
V::object(vec![("key", V::scalar(123))].into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": 1 + 2 }),
|
value!({ "key": 1 + 2 }),
|
||||||
V::object(vec![("key", V::scalar(3))].into_iter().collect()),
|
V::object(vec![("key", V::scalar(3))].into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": [] }),
|
value!({ "key": [] }),
|
||||||
V::object(vec![("key", V::list(vec![]))].into_iter().collect()),
|
V::object(vec![("key", V::list(vec![]))].into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": [null] }),
|
value!({ "key": [null] }),
|
||||||
V::object(vec![("key", V::list(vec![V::Null]))].into_iter().collect()),
|
V::object(vec![("key", V::list(vec![V::Null]))].into_iter().collect()),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": [1] }),
|
value!({ "key": [1] }),
|
||||||
V::object(
|
V::object(
|
||||||
vec![("key", V::list(vec![V::scalar(1)]))]
|
vec![("key", V::list(vec![V::scalar(1)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -359,7 +365,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": [1 + 2] }),
|
value!({ "key": [1 + 2] }),
|
||||||
V::object(
|
V::object(
|
||||||
vec![("key", V::list(vec![V::scalar(3)]))]
|
vec![("key", V::list(vec![V::scalar(3)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -367,7 +373,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_value!({ "key": [val] }),
|
value!({ "key": [val] }),
|
||||||
V::object(
|
V::object(
|
||||||
vec![("key", V::list(vec![V::scalar(42)]))]
|
vec![("key", V::list(vec![V::scalar(42)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -380,8 +386,8 @@ mod tests {
|
||||||
fn option() {
|
fn option() {
|
||||||
let val = Some(42);
|
let val = Some(42);
|
||||||
|
|
||||||
assert_eq!(graphql_value!(None), V::Null);
|
assert_eq!(value!(None), V::Null);
|
||||||
assert_eq!(graphql_value!(Some(42)), V::scalar(42));
|
assert_eq!(value!(Some(42)), V::scalar(42));
|
||||||
assert_eq!(graphql_value!(val), V::scalar(42));
|
assert_eq!(value!(val), V::scalar(42));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
//! [`graphql_vars!`] macro implementation.
|
//! [`vars!`] macro implementation.
|
||||||
//!
|
|
||||||
//! [`graphql_vars!`]: graphql_vars
|
|
||||||
|
|
||||||
/// Constructs [`Variables`] via JSON-like syntax.
|
/// Constructs [`graphql::Variables`] via JSON-like syntax.
|
||||||
///
|
///
|
||||||
/// [`Variables`] key should implement [`Into`]`<`[`String`]`>`.
|
/// [`graphql::Variables`] key should implement [`Into`]`<`[`String`]`>`.
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// #
|
/// #
|
||||||
/// # use juniper::{graphql_vars, Variables};
|
/// # use juniper::graphql;
|
||||||
/// #
|
/// #
|
||||||
/// let code = 200;
|
/// let code = 200;
|
||||||
/// let features = vec!["key", "value"];
|
/// let features = vec!["key", "value"];
|
||||||
/// let key: Cow<'static, str> = "key".into();
|
/// let key: Cow<'static, str> = "key".into();
|
||||||
///
|
///
|
||||||
/// let value: Variables = graphql_vars! {
|
/// let value: graphql::Variables = graphql::vars! {
|
||||||
/// "code": code,
|
/// "code": code,
|
||||||
/// "success": code == 200,
|
/// "success": code == 200,
|
||||||
/// features[0]: features[1],
|
/// features[0]: features[1],
|
||||||
|
@ -22,10 +20,10 @@
|
||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// See [`graphql_input_value!`] for more info on syntax of value after `:`.
|
/// See [`graphql::input_value!`] for more info on syntax of value after `:`.
|
||||||
///
|
///
|
||||||
/// [`graphql_input_value!`]: crate::graphql_input_value
|
/// [`graphql::input_value!`]: crate::graphql::input_value
|
||||||
/// [`Variables`]: crate::Variables
|
/// [`graphql::Variables`]: crate::graphql::Variables
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graphql_vars {
|
macro_rules! graphql_vars {
|
||||||
////////////
|
////////////
|
||||||
|
@ -38,12 +36,12 @@ macro_rules! graphql_vars {
|
||||||
// Insert the current entry followed by trailing comma.
|
// Insert the current entry followed by trailing comma.
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
||||||
let _ = $object.insert(($($key)+).into(), $value);
|
let _ = $object.insert(($($key)+).into(), $value);
|
||||||
$crate::graphql_vars! {@object $object () ($($rest)*) ($($rest)*)};
|
$crate::graphql::vars! {@object $object () ($($rest)*) ($($rest)*)};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Current entry followed by unexpected token.
|
// Current entry followed by unexpected token.
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
||||||
$crate::graphql_vars! {@unexpected $unexpected};
|
$crate::graphql::vars! {@unexpected $unexpected};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert the last entry without trailing comma.
|
// Insert the last entry without trailing comma.
|
||||||
|
@ -53,7 +51,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is `null`.
|
// Next value is `null`.
|
||||||
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(null)) $($rest)*
|
($crate::graphql_input_value!(null)) $($rest)*
|
||||||
|
@ -62,7 +60,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is `None`.
|
// Next value is `None`.
|
||||||
(@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(None)) $($rest)*
|
($crate::graphql_input_value!(None)) $($rest)*
|
||||||
|
@ -71,7 +69,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is a variable.
|
// Next value is a variable.
|
||||||
(@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!(@$var)) $($rest)*
|
($crate::graphql_input_value!(@$var)) $($rest)*
|
||||||
|
@ -80,7 +78,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is an array.
|
// Next value is an array.
|
||||||
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!([$($array)*])) $($rest)*
|
($crate::graphql_input_value!([$($array)*])) $($rest)*
|
||||||
|
@ -89,7 +87,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is a map.
|
// Next value is a map.
|
||||||
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!({$($map)*})) $($rest)*
|
($crate::graphql_input_value!({$($map)*})) $($rest)*
|
||||||
|
@ -98,7 +96,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is `true`, `false` or enum ident followed by a comma.
|
// Next value is `true`, `false` or enum ident followed by a comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($ident)) , $($rest)*
|
($crate::graphql_input_value!($ident)) , $($rest)*
|
||||||
|
@ -107,7 +105,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is `true`, `false` or enum ident without trailing comma.
|
// Next value is `true`, `false` or enum ident without trailing comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($last))
|
($crate::graphql_input_value!($last))
|
||||||
|
@ -116,7 +114,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Next value is an expression followed by comma.
|
// Next value is an expression followed by comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($value)) , $($rest)*
|
($crate::graphql_input_value!($value)) , $($rest)*
|
||||||
|
@ -125,7 +123,7 @@ macro_rules! graphql_vars {
|
||||||
|
|
||||||
// Last value is an expression with no trailing comma.
|
// Last value is an expression with no trailing comma.
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
[$($key)+]
|
[$($key)+]
|
||||||
($crate::graphql_input_value!($value))
|
($crate::graphql_input_value!($value))
|
||||||
|
@ -135,44 +133,44 @@ macro_rules! graphql_vars {
|
||||||
// Missing value for last entry. Trigger a reasonable error message.
|
// Missing value for last entry. Trigger a reasonable error message.
|
||||||
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_vars! {};
|
$crate::graphql::vars! {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Missing colon and value for last entry. Trigger a reasonable error
|
// Missing colon and value for last entry. Trigger a reasonable error
|
||||||
// message.
|
// message.
|
||||||
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
||||||
// "unexpected end of macro invocation"
|
// "unexpected end of macro invocation"
|
||||||
$crate::graphql_vars! {};
|
$crate::graphql::vars! {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Misplaced colon. Trigger a reasonable error message.
|
// Misplaced colon. Trigger a reasonable error message.
|
||||||
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `:`".
|
// Takes no arguments so "no rules expected the token `:`".
|
||||||
$crate::graphql_vars! {@unexpected $colon};
|
$crate::graphql::vars! {@unexpected $colon};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Found a comma inside a key. Trigger a reasonable error message.
|
// Found a comma inside a key. Trigger a reasonable error message.
|
||||||
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
||||||
// Takes no arguments so "no rules expected the token `,`".
|
// Takes no arguments so "no rules expected the token `,`".
|
||||||
$crate::graphql_vars! {@unexpected $comma};
|
$crate::graphql::vars! {@unexpected $comma};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Key is fully parenthesized. This avoids clippy double_parens false
|
// Key is fully parenthesized. This avoids clippy double_parens false
|
||||||
// positives because the parenthesization may be necessary here.
|
// positives because the parenthesization may be necessary here.
|
||||||
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object ($key) (: $($rest)*) (: $($rest)*)
|
@object $object ($key) (: $($rest)*) (: $($rest)*)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Refuse to absorb colon token into key expression.
|
// Refuse to absorb colon token into key expression.
|
||||||
(@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
(@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
||||||
$crate::graphql_vars! {@unexpected $($unexpected)+};
|
$crate::graphql::vars! {@unexpected $($unexpected)+};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Munch a token into the current key.
|
// Munch a token into the current key.
|
||||||
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
||||||
$crate::graphql_vars! {
|
$crate::graphql::vars! {
|
||||||
@object $object
|
@object $object
|
||||||
($($key)* $tt)
|
($($key)* $tt)
|
||||||
($($rest)*) ($($rest)*)
|
($($rest)*) ($($rest)*)
|
||||||
|
@ -189,26 +187,33 @@ macro_rules! graphql_vars {
|
||||||
// Defaults //
|
// Defaults //
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
() => {{ $crate::Variables::<_>::new() }};
|
() => {{ $crate::graphql::Variables::<_>::new() }};
|
||||||
|
|
||||||
( $($map:tt)+ ) => {{
|
( $($map:tt)+ ) => {{
|
||||||
let mut object = $crate::Variables::<_>::new();
|
let mut object = $crate::graphql::Variables::<_>::new();
|
||||||
$crate::graphql_vars! {@object object () ($($map)*) ($($map)*)};
|
$crate::graphql::vars! {@object object () ($($map)*) ($($map)*)};
|
||||||
object
|
object
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use graphql_vars as vars;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use indexmap::{indexmap, IndexMap};
|
use indexmap::{indexmap, IndexMap};
|
||||||
|
|
||||||
type V = crate::Variables;
|
use crate::graphql;
|
||||||
|
|
||||||
type IV = crate::InputValue;
|
use super::vars;
|
||||||
|
|
||||||
|
type V = graphql::Variables;
|
||||||
|
|
||||||
|
type IV = graphql::InputValue;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
assert_eq!(graphql_vars! {}, V::new());
|
assert_eq!(vars! {}, V::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -216,35 +221,35 @@ mod tests {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": 123},
|
vars! {"key": 123},
|
||||||
vec![("key".into(), IV::scalar(123))]
|
vec![("key".into(), IV::scalar(123))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": "val"},
|
vars! {"key": "val"},
|
||||||
vec![("key".into(), IV::scalar("val"))]
|
vec![("key".into(), IV::scalar("val"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": 1.23},
|
vars! {"key": 1.23},
|
||||||
vec![("key".into(), IV::scalar(1.23))]
|
vec![("key".into(), IV::scalar(1.23))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": 1 + 2},
|
vars! {"key": 1 + 2},
|
||||||
vec![("key".into(), IV::scalar(3))].into_iter().collect(),
|
vec![("key".into(), IV::scalar(3))].into_iter().collect(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": false},
|
vars! {"key": false},
|
||||||
vec![("key".into(), IV::scalar(false))]
|
vec![("key".into(), IV::scalar(false))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": (val)},
|
vars! {"key": (val)},
|
||||||
vec![("key".into(), IV::scalar(42))]
|
vec![("key".into(), IV::scalar(42))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
|
@ -254,13 +259,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn r#enum() {
|
fn r#enum() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": ENUM},
|
vars! {"key": ENUM},
|
||||||
vec![("key".into(), IV::enum_value("ENUM"))]
|
vec![("key".into(), IV::enum_value("ENUM"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": lowercase},
|
vars! {"key": lowercase},
|
||||||
vec![("key".into(), IV::enum_value("lowercase"))]
|
vec![("key".into(), IV::enum_value("lowercase"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
|
@ -270,19 +275,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn variable() {
|
fn variable() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": @var},
|
vars! {"key": @var},
|
||||||
vec![("key".into(), IV::variable("var"))]
|
vec![("key".into(), IV::variable("var"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": @array},
|
vars! {"key": @array},
|
||||||
vec![("key".into(), IV::variable("array"))]
|
vec![("key".into(), IV::variable("array"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": @object},
|
vars! {"key": @object},
|
||||||
vec![("key".into(), IV::variable("object"))]
|
vec![("key".into(), IV::variable("object"))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
|
@ -294,72 +299,72 @@ mod tests {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": []},
|
vars! {"key": []},
|
||||||
vec![("key".into(), IV::list(vec![]))]
|
vec![("key".into(), IV::list(vec![]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [null]},
|
vars! {"key": [null]},
|
||||||
vec![("key".into(), IV::list(vec![IV::Null]))]
|
vec![("key".into(), IV::list(vec![IV::Null]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1]},
|
vars! {"key": [1]},
|
||||||
vec![("key".into(), IV::list(vec![IV::scalar(1)]))]
|
vec![("key".into(), IV::list(vec![IV::scalar(1)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1 + 2]},
|
vars! {"key": [1 + 2]},
|
||||||
vec![("key".into(), IV::list(vec![IV::scalar(3)]))]
|
vec![("key".into(), IV::list(vec![IV::scalar(3)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [(val)]},
|
vars! {"key": [(val)]},
|
||||||
vec![("key".into(), IV::list(vec![IV::scalar(42)]))]
|
vec![("key".into(), IV::list(vec![IV::scalar(42)]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [ENUM]},
|
vars! {"key": [ENUM]},
|
||||||
vec![("key".into(), IV::list(vec![IV::enum_value("ENUM")]))]
|
vec![("key".into(), IV::list(vec![IV::enum_value("ENUM")]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [lowercase]},
|
vars! {"key": [lowercase]},
|
||||||
vec![("key".into(), IV::list(vec![IV::enum_value("lowercase")]))]
|
vec![("key".into(), IV::list(vec![IV::enum_value("lowercase")]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [@var]},
|
vars! {"key": [@var]},
|
||||||
vec![("key".into(), IV::list(vec![IV::variable("var")]))]
|
vec![("key".into(), IV::list(vec![IV::variable("var")]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [@array]},
|
vars! {"key": [@array]},
|
||||||
vec![("key".into(), IV::list(vec![IV::variable("array")]))]
|
vec![("key".into(), IV::list(vec![IV::variable("array")]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [@object]},
|
vars! {"key": [@object]},
|
||||||
vec![("key".into(), IV::list(vec![IV::variable("object")]))]
|
vec![("key".into(), IV::list(vec![IV::variable("object")]))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1, [2], 3]},
|
vars! {"key": [1, [2], 3]},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::list(vec![
|
IV::list(vec![
|
||||||
|
@ -372,7 +377,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1, [2 + 3], 3]},
|
vars! {"key": [1, [2 + 3], 3]},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::list(vec![
|
IV::list(vec![
|
||||||
|
@ -385,7 +390,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1, [ENUM], (val)]},
|
vars! {"key": [1, [ENUM], (val)]},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::list(vec![
|
IV::list(vec![
|
||||||
|
@ -398,7 +403,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1 + 2, [(val)], @val]},
|
vars! {"key": [1 + 2, [(val)], @val]},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::list(vec![
|
IV::list(vec![
|
||||||
|
@ -411,7 +416,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": [1, [@val], ENUM]},
|
vars! {"key": [1, [@val], ENUM]},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::list(vec![
|
IV::list(vec![
|
||||||
|
@ -430,21 +435,21 @@ mod tests {
|
||||||
let val = 42;
|
let val = 42;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {}},
|
vars! {"key": {}},
|
||||||
vec![("key".into(), IV::object(IndexMap::<String, _>::new()))]
|
vec![("key".into(), IV::object(IndexMap::<String, _>::new()))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": null}},
|
vars! {"key": {"key": null}},
|
||||||
vec![("key".into(), IV::object(indexmap! {"key" => IV::Null}))]
|
vec![("key".into(), IV::object(indexmap! {"key" => IV::Null}))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": 123}},
|
vars! {"key": {"key": 123}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::scalar(123)}),
|
IV::object(indexmap! {"key" => IV::scalar(123)}),
|
||||||
|
@ -453,13 +458,13 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": 1 + 2}},
|
vars! {"key": {"key": 1 + 2}},
|
||||||
vec![("key".into(), IV::object(indexmap! {"key" => IV::scalar(3)}),)]
|
vec![("key".into(), IV::object(indexmap! {"key" => IV::scalar(3)}),)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": (val)}},
|
vars! {"key": {"key": (val)}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::scalar(42)}),
|
IV::object(indexmap! {"key" => IV::scalar(42)}),
|
||||||
|
@ -469,7 +474,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": []}},
|
vars! {"key": {"key": []}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::list(vec![])}),
|
IV::object(indexmap! {"key" => IV::list(vec![])}),
|
||||||
|
@ -478,7 +483,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": [null]}},
|
vars! {"key": {"key": [null]}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::list(vec![IV::Null])}),
|
IV::object(indexmap! {"key" => IV::list(vec![IV::Null])}),
|
||||||
|
@ -487,7 +492,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": [1]}},
|
vars! {"key": {"key": [1]}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(1)])}),
|
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(1)])}),
|
||||||
|
@ -496,7 +501,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": [1 + 2]}},
|
vars! {"key": {"key": [1 + 2]}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(3)])}),
|
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(3)])}),
|
||||||
|
@ -505,7 +510,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": [(val)]}},
|
vars! {"key": {"key": [(val)]}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(42)])}),
|
IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(42)])}),
|
||||||
|
@ -514,7 +519,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": ENUM}},
|
vars! {"key": {"key": ENUM}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::enum_value("ENUM")}),
|
IV::object(indexmap! {"key" => IV::enum_value("ENUM")}),
|
||||||
|
@ -523,7 +528,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": lowercase}},
|
vars! {"key": {"key": lowercase}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::enum_value("lowercase")}),
|
IV::object(indexmap! {"key" => IV::enum_value("lowercase")}),
|
||||||
|
@ -532,7 +537,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": @val}},
|
vars! {"key": {"key": @val}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::variable("val")}),
|
IV::object(indexmap! {"key" => IV::variable("val")}),
|
||||||
|
@ -541,7 +546,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {"key": {"key": @array}},
|
vars! {"key": {"key": @array}},
|
||||||
vec![(
|
vec![(
|
||||||
"key".into(),
|
"key".into(),
|
||||||
IV::object(indexmap! {"key" => IV::variable("array")}),
|
IV::object(indexmap! {"key" => IV::variable("array")}),
|
||||||
|
@ -550,7 +555,7 @@ mod tests {
|
||||||
.collect::<V>(),
|
.collect::<V>(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graphql_vars! {
|
vars! {
|
||||||
"inner": {
|
"inner": {
|
||||||
"key1": (val),
|
"key1": (val),
|
||||||
"key2": "val",
|
"key2": "val",
|
||||||
|
|
|
@ -6,9 +6,9 @@ pub mod helper;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod reflect;
|
pub mod reflect;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod graphql_input_value;
|
mod graphql_input_value;
|
||||||
#[macro_use]
|
|
||||||
mod graphql_value;
|
mod graphql_value;
|
||||||
#[macro_use]
|
|
||||||
mod graphql_vars;
|
mod graphql_vars;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use self::{graphql_input_value::input_value, graphql_value::value, graphql_vars::vars};
|
||||||
|
|
|
@ -8,20 +8,10 @@ use crate::{
|
||||||
Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue,
|
Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Alias for a [GraphQL object][1], [scalar][2] or [interface][3] type's name
|
pub use crate::reflect::{
|
||||||
/// in a GraphQL schema.
|
can_be_subtype, fnv1a128, str_eq, str_exists_in_arr, type_len_with_wrapped_val, Argument,
|
||||||
///
|
Arguments, FieldName, Name, Names, Type, Types, WrappedValue,
|
||||||
/// See [`BaseType`] for more info.
|
};
|
||||||
///
|
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Scalars
|
|
||||||
/// [3]: https://spec.graphql.org/October2021#sec-Interfaces
|
|
||||||
pub type Type = &'static str;
|
|
||||||
|
|
||||||
/// Alias for a slice of [`Type`]s.
|
|
||||||
///
|
|
||||||
/// See [`BaseSubTypes`] for more info.
|
|
||||||
pub type Types = &'static [Type];
|
|
||||||
|
|
||||||
/// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`].
|
/// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`].
|
||||||
///
|
///
|
||||||
|
@ -153,9 +143,6 @@ impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Rc<T> {
|
||||||
const NAMES: Types = T::NAMES;
|
const NAMES: Types = T::NAMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias for a value of a [`WrappedType`] (composed GraphQL type).
|
|
||||||
pub type WrappedValue = u128;
|
|
||||||
|
|
||||||
// TODO: Just use `&str`s once they're allowed in `const` generics.
|
// TODO: Just use `&str`s once they're allowed in `const` generics.
|
||||||
/// Encoding of a composed GraphQL type in numbers.
|
/// Encoding of a composed GraphQL type in numbers.
|
||||||
///
|
///
|
||||||
|
@ -163,7 +150,7 @@ pub type WrappedValue = u128;
|
||||||
/// because of the [wrapping types][2]. To work around this we use a
|
/// because of the [wrapping types][2]. To work around this we use a
|
||||||
/// [`WrappedValue`] which is represented via [`u128`] number in the following
|
/// [`WrappedValue`] which is represented via [`u128`] number in the following
|
||||||
/// encoding:
|
/// encoding:
|
||||||
/// - In base case of non-nullable [object][1] [`VALUE`] is `1`.
|
/// - In base case of non-nullable singular [object][1] [`VALUE`] is `1`.
|
||||||
/// - To represent nullability we "append" `2` to the [`VALUE`], so
|
/// - To represent nullability we "append" `2` to the [`VALUE`], so
|
||||||
/// [`Option`]`<`[object][1]`>` has [`VALUE`] of `12`.
|
/// [`Option`]`<`[object][1]`>` has [`VALUE`] of `12`.
|
||||||
/// - To represent list we "append" `3` to the [`VALUE`], so
|
/// - To represent list we "append" `3` to the [`VALUE`], so
|
||||||
|
@ -177,7 +164,7 @@ pub type WrappedValue = u128;
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use juniper::{
|
/// # use juniper::{
|
||||||
/// # format_type,
|
/// # reflect::format_type,
|
||||||
/// # macros::reflect::{WrappedType, BaseType, WrappedValue, Type},
|
/// # macros::reflect::{WrappedType, BaseType, WrappedValue, Type},
|
||||||
/// # DefaultScalarValue,
|
/// # DefaultScalarValue,
|
||||||
/// # };
|
/// # };
|
||||||
|
@ -253,34 +240,6 @@ impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Rc<T> {
|
||||||
const VALUE: u128 = T::VALUE;
|
const VALUE: u128 = T::VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias for a [GraphQL object][1] or [interface][2] [field argument][3] name.
|
|
||||||
///
|
|
||||||
/// See [`Fields`] for more info.
|
|
||||||
///
|
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
|
|
||||||
/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
|
||||||
pub type Name = &'static str;
|
|
||||||
|
|
||||||
/// Alias for a slice of [`Name`]s.
|
|
||||||
///
|
|
||||||
/// See [`Fields`] for more info.
|
|
||||||
pub type Names = &'static [Name];
|
|
||||||
|
|
||||||
/// Alias for [field argument][1]s [`Name`], [`Type`] and [`WrappedValue`].
|
|
||||||
///
|
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
|
||||||
pub type Argument = (Name, Type, WrappedValue);
|
|
||||||
|
|
||||||
/// Alias for a slice of [field argument][1]s [`Name`], [`Type`] and
|
|
||||||
/// [`WrappedValue`].
|
|
||||||
///
|
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
|
||||||
pub type Arguments = &'static [(Name, Type, WrappedValue)];
|
|
||||||
|
|
||||||
/// Alias for a `const`-hashed [`Name`] used in a `const` context.
|
|
||||||
pub type FieldName = u128;
|
|
||||||
|
|
||||||
/// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`].
|
/// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`].
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
@ -394,107 +353,6 @@ pub trait AsyncField<S, const N: FieldName>: FieldMeta<S, N> {
|
||||||
) -> BoxFuture<'b, ExecutionResult<S>>;
|
) -> BoxFuture<'b, ExecutionResult<S>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in
|
|
||||||
/// `const` generics. See [spec] for more info.
|
|
||||||
///
|
|
||||||
/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html
|
|
||||||
#[must_use]
|
|
||||||
pub const fn fnv1a128(str: Name) -> u128 {
|
|
||||||
const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d;
|
|
||||||
const FNV_PRIME: u128 = 0x0000000001000000000000000000013b;
|
|
||||||
|
|
||||||
let bytes = str.as_bytes();
|
|
||||||
let mut hash = FNV_OFFSET_BASIS;
|
|
||||||
let mut i = 0;
|
|
||||||
while i < bytes.len() {
|
|
||||||
hash ^= bytes[i] as u128;
|
|
||||||
hash = hash.wrapping_mul(FNV_PRIME);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
hash
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length __in bytes__ of the [`format_type!`] macro result.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize {
|
|
||||||
let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type!
|
|
||||||
|
|
||||||
let mut curr = val;
|
|
||||||
while curr % 10 != 0 {
|
|
||||||
match curr % 10 {
|
|
||||||
2 => len -= "!".as_bytes().len(), // remove !
|
|
||||||
3 => len += "[]!".as_bytes().len(), // [Type]!
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
curr /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks whether the given GraphQL [object][1] represents a `subtype` of the
|
|
||||||
/// given GraphQL `ty`pe, basing on the [`WrappedType`] encoding.
|
|
||||||
///
|
|
||||||
/// To fully determine the sub-typing relation the [`Type`] should be one of the
|
|
||||||
/// [`BaseSubTypes::NAMES`].
|
|
||||||
///
|
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
|
||||||
#[must_use]
|
|
||||||
pub const fn can_be_subtype(ty: WrappedValue, subtype: WrappedValue) -> bool {
|
|
||||||
let ty_curr = ty % 10;
|
|
||||||
let sub_curr = subtype % 10;
|
|
||||||
|
|
||||||
if ty_curr == sub_curr {
|
|
||||||
if ty_curr == 1 {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
can_be_subtype(ty / 10, subtype / 10)
|
|
||||||
}
|
|
||||||
} else if ty_curr == 2 {
|
|
||||||
can_be_subtype(ty / 10, subtype)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks whether the given `val` exists in the given `arr`.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool {
|
|
||||||
let mut i = 0;
|
|
||||||
while i < arr.len() {
|
|
||||||
if str_eq(val, arr[i]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compares strings in a `const` context.
|
|
||||||
///
|
|
||||||
/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to
|
|
||||||
/// write custom comparison function.
|
|
||||||
///
|
|
||||||
/// [`Eq`]: std::cmp::Eq
|
|
||||||
// TODO: Remove once `Eq` trait is allowed in `const` context.
|
|
||||||
pub const fn str_eq(l: &str, r: &str) -> bool {
|
|
||||||
let (l, r) = (l.as_bytes(), r.as_bytes());
|
|
||||||
|
|
||||||
if l.len() != r.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
while i < l.len() {
|
|
||||||
if l[i] != r[i] {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asserts that `#[graphql_interface(for = ...)]` has all the types referencing
|
/// Asserts that `#[graphql_interface(for = ...)]` has all the types referencing
|
||||||
/// this interface in the `impl = ...` attribute argument.
|
/// this interface in the `impl = ...` attribute argument.
|
||||||
///
|
///
|
||||||
|
@ -509,7 +367,7 @@ macro_rules! assert_implemented_for {
|
||||||
<$interfaces as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES,
|
<$interfaces as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES,
|
||||||
);
|
);
|
||||||
if !is_present {
|
if !is_present {
|
||||||
const MSG: &str = $crate::const_concat!(
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
"Failed to implement interface `",
|
"Failed to implement interface `",
|
||||||
<$interfaces as $crate::macros::reflect::BaseType<$scalar>>::NAME,
|
<$interfaces as $crate::macros::reflect::BaseType<$scalar>>::NAME,
|
||||||
"` on `",
|
"` on `",
|
||||||
|
@ -537,7 +395,7 @@ macro_rules! assert_interfaces_impls {
|
||||||
<$implementers as ::juniper::macros::reflect::Implements<$scalar>>::NAMES,
|
<$implementers as ::juniper::macros::reflect::Implements<$scalar>>::NAMES,
|
||||||
);
|
);
|
||||||
if !is_present {
|
if !is_present {
|
||||||
const MSG: &str = $crate::const_concat!(
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
"Failed to implement interface `",
|
"Failed to implement interface `",
|
||||||
<$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME,
|
<$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME,
|
||||||
"` on `",
|
"` on `",
|
||||||
|
@ -622,7 +480,7 @@ macro_rules! assert_subtype {
|
||||||
<$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
<$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
||||||
const IMPL_TY: $crate::macros::reflect::Type =
|
const IMPL_TY: $crate::macros::reflect::Type =
|
||||||
<$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
<$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
||||||
const ERR_PREFIX: &str = $crate::const_concat!(
|
const ERR_PREFIX: &str = $crate::reflect::const_concat!(
|
||||||
"Failed to implement interface `",
|
"Failed to implement interface `",
|
||||||
BASE_TY,
|
BASE_TY,
|
||||||
"` on `",
|
"` on `",
|
||||||
|
@ -664,14 +522,14 @@ macro_rules! assert_subtype {
|
||||||
let is_subtype = $crate::macros::reflect::str_exists_in_arr(IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES)
|
let is_subtype = $crate::macros::reflect::str_exists_in_arr(IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES)
|
||||||
&& $crate::macros::reflect::can_be_subtype(BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL);
|
&& $crate::macros::reflect::can_be_subtype(BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL);
|
||||||
if !is_subtype {
|
if !is_subtype {
|
||||||
const MSG: &str = $crate::const_concat!(
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
ERR_PREFIX,
|
ERR_PREFIX,
|
||||||
"Field `",
|
"Field `",
|
||||||
FIELD_NAME,
|
FIELD_NAME,
|
||||||
"`: implementor is expected to return a subtype of interface's return object: `",
|
"`: implementor is expected to return a subtype of interface's return object: `",
|
||||||
$crate::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL),
|
$crate::reflect::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL),
|
||||||
"` is not a subtype of `",
|
"` is not a subtype of `",
|
||||||
$crate::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL),
|
$crate::reflect::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL),
|
||||||
"`.",
|
"`.",
|
||||||
);
|
);
|
||||||
::std::panic!("{}", MSG);
|
::std::panic!("{}", MSG);
|
||||||
|
@ -695,7 +553,7 @@ macro_rules! assert_field_args {
|
||||||
const _: () = {
|
const _: () = {
|
||||||
const BASE_NAME: &str = <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
const BASE_NAME: &str = <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
||||||
const IMPL_NAME: &str = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
const IMPL_NAME: &str = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
|
||||||
const ERR_PREFIX: &str = $crate::const_concat!(
|
const ERR_PREFIX: &str = $crate::reflect::const_concat!(
|
||||||
"Failed to implement interface `",
|
"Failed to implement interface `",
|
||||||
BASE_NAME,
|
BASE_NAME,
|
||||||
"` on `",
|
"` on `",
|
||||||
|
@ -818,13 +676,14 @@ macro_rules! assert_field_args {
|
||||||
const BASE_ARG_NAME: &str = ERROR.base.0;
|
const BASE_ARG_NAME: &str = ERROR.base.0;
|
||||||
const IMPL_ARG_NAME: &str = ERROR.implementation.0;
|
const IMPL_ARG_NAME: &str = ERROR.implementation.0;
|
||||||
|
|
||||||
const BASE_TYPE_FORMATTED: &str = $crate::format_type!(ERROR.base.1, ERROR.base.2);
|
const BASE_TYPE_FORMATTED: &str =
|
||||||
|
$crate::reflect::format_type!(ERROR.base.1, ERROR.base.2);
|
||||||
const IMPL_TYPE_FORMATTED: &str =
|
const IMPL_TYPE_FORMATTED: &str =
|
||||||
$crate::format_type!(ERROR.implementation.1, ERROR.implementation.2);
|
$crate::reflect::format_type!(ERROR.implementation.1, ERROR.implementation.2);
|
||||||
|
|
||||||
const MSG: &str = match ERROR.cause {
|
const MSG: &str = match ERROR.cause {
|
||||||
Cause::TypeMismatch => {
|
Cause::TypeMismatch => {
|
||||||
$crate::const_concat!(
|
$crate::reflect::const_concat!(
|
||||||
"Argument `",
|
"Argument `",
|
||||||
BASE_ARG_NAME,
|
BASE_ARG_NAME,
|
||||||
"`: expected type `",
|
"`: expected type `",
|
||||||
|
@ -835,7 +694,7 @@ macro_rules! assert_field_args {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Cause::RequiredField => {
|
Cause::RequiredField => {
|
||||||
$crate::const_concat!(
|
$crate::reflect::const_concat!(
|
||||||
"Argument `",
|
"Argument `",
|
||||||
BASE_ARG_NAME,
|
BASE_ARG_NAME,
|
||||||
"` of type `",
|
"` of type `",
|
||||||
|
@ -844,7 +703,7 @@ macro_rules! assert_field_args {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Cause::AdditionalNonNullableField => {
|
Cause::AdditionalNonNullableField => {
|
||||||
$crate::const_concat!(
|
$crate::reflect::const_concat!(
|
||||||
"Argument `",
|
"Argument `",
|
||||||
IMPL_ARG_NAME,
|
IMPL_ARG_NAME,
|
||||||
"` of type `",
|
"` of type `",
|
||||||
|
@ -854,43 +713,13 @@ macro_rules! assert_field_args {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const ERROR_MSG: &str =
|
const ERROR_MSG: &str =
|
||||||
$crate::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG);
|
$crate::reflect::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG);
|
||||||
::std::panic!("{}", ERROR_MSG);
|
::std::panic!("{}", ERROR_MSG);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Concatenates `const` [`str`](prim@str)s in a `const` context.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! const_concat {
|
|
||||||
($($s:expr),* $(,)?) => {{
|
|
||||||
const LEN: usize = 0 $(+ $s.as_bytes().len())*;
|
|
||||||
const CNT: usize = [$($s),*].len();
|
|
||||||
const fn concat(input: [&str; CNT]) -> [u8; LEN] {
|
|
||||||
let mut bytes = [0; LEN];
|
|
||||||
let (mut i, mut byte) = (0, 0);
|
|
||||||
while i < CNT {
|
|
||||||
let mut b = 0;
|
|
||||||
while b < input[i].len() {
|
|
||||||
bytes[byte] = input[i].as_bytes()[b];
|
|
||||||
byte += 1;
|
|
||||||
b += 1;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
const CON: [u8; LEN] = concat([$($s),*]);
|
|
||||||
|
|
||||||
// TODO: Use `.unwrap()` once it becomes `const`.
|
|
||||||
match ::std::str::from_utf8(&CON) {
|
|
||||||
::std::result::Result::Ok(s) => s,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ensures that the given `$impl_ty` implements [`Field`] and returns a
|
/// Ensures that the given `$impl_ty` implements [`Field`] and returns a
|
||||||
/// [`fnv1a128`] hash for it, otherwise panics with understandable message.
|
/// [`fnv1a128`] hash for it, otherwise panics with understandable message.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -903,7 +732,7 @@ macro_rules! checked_hash {
|
||||||
if exists {
|
if exists {
|
||||||
$crate::macros::reflect::fnv1a128(FIELD_NAME)
|
$crate::macros::reflect::fnv1a128(FIELD_NAME)
|
||||||
} else {
|
} else {
|
||||||
const MSG: &str = $crate::const_concat!(
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
$($prefix,)?
|
$($prefix,)?
|
||||||
"Field `",
|
"Field `",
|
||||||
$field_name,
|
$field_name,
|
||||||
|
@ -915,102 +744,3 @@ macro_rules! checked_hash {
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the given [`Type`] and [`WrappedValue`] into a readable GraphQL type
|
|
||||||
/// name.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use juniper::format_type;
|
|
||||||
/// #
|
|
||||||
/// assert_eq!(format_type!("String", 123), "[String]!");
|
|
||||||
/// assert_eq!(format_type!("🦀", 123), "[🦀]!");
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! format_type {
|
|
||||||
($ty: expr, $wrapped_value: expr $(,)?) => {{
|
|
||||||
const TYPE: (
|
|
||||||
$crate::macros::reflect::Type,
|
|
||||||
$crate::macros::reflect::WrappedValue,
|
|
||||||
) = ($ty, $wrapped_value);
|
|
||||||
const RES_LEN: usize = $crate::macros::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1);
|
|
||||||
|
|
||||||
const OPENING_BRACKET: &str = "[";
|
|
||||||
const CLOSING_BRACKET: &str = "]";
|
|
||||||
const BANG: &str = "!";
|
|
||||||
|
|
||||||
const fn format_type_arr() -> [u8; RES_LEN] {
|
|
||||||
let (ty, wrap_val) = TYPE;
|
|
||||||
let mut type_arr: [u8; RES_LEN] = [0; RES_LEN];
|
|
||||||
|
|
||||||
let mut current_start = 0;
|
|
||||||
let mut current_end = RES_LEN - 1;
|
|
||||||
let mut current_wrap_val = wrap_val;
|
|
||||||
let mut is_null = false;
|
|
||||||
while current_wrap_val % 10 != 0 {
|
|
||||||
match current_wrap_val % 10 {
|
|
||||||
2 => is_null = true, // Skips writing `BANG` later.
|
|
||||||
3 => {
|
|
||||||
// Write `OPENING_BRACKET` at `current_start`.
|
|
||||||
let mut i = 0;
|
|
||||||
while i < OPENING_BRACKET.as_bytes().len() {
|
|
||||||
type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
current_start += i;
|
|
||||||
if !is_null {
|
|
||||||
// Write `BANG` at `current_end`.
|
|
||||||
i = 0;
|
|
||||||
while i < BANG.as_bytes().len() {
|
|
||||||
type_arr[current_end - BANG.as_bytes().len() + i + 1] =
|
|
||||||
BANG.as_bytes()[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
current_end -= i;
|
|
||||||
}
|
|
||||||
// Write `CLOSING_BRACKET` at `current_end`.
|
|
||||||
i = 0;
|
|
||||||
while i < CLOSING_BRACKET.as_bytes().len() {
|
|
||||||
type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] =
|
|
||||||
CLOSING_BRACKET.as_bytes()[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
current_end -= i;
|
|
||||||
is_null = false;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
current_wrap_val /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes `Type` at `current_start`.
|
|
||||||
let mut i = 0;
|
|
||||||
while i < ty.as_bytes().len() {
|
|
||||||
type_arr[current_start + i] = ty.as_bytes()[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
if !is_null {
|
|
||||||
// Writes `BANG` at `current_end`.
|
|
||||||
while i < BANG.as_bytes().len() {
|
|
||||||
type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type_arr
|
|
||||||
}
|
|
||||||
|
|
||||||
const TYPE_ARR: [u8; RES_LEN] = format_type_arr();
|
|
||||||
|
|
||||||
// TODO: Use `.unwrap()` once it becomes `const`.
|
|
||||||
const TYPE_FORMATTED: &str = match ::std::str::from_utf8(TYPE_ARR.as_slice()) {
|
|
||||||
::std::result::Result::Ok(s) => s,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
TYPE_FORMATTED
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
874
juniper/src/reflect/mod.rs
Normal file
874
juniper/src/reflect/mod.rs
Normal file
|
@ -0,0 +1,874 @@
|
||||||
|
//! Compile-time reflection of Rust types into GraphQL types.
|
||||||
|
|
||||||
|
use crate::behavior;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use self::macros::{
|
||||||
|
assert_field, assert_field_args, assert_field_type, assert_has_field, assert_implemented_for,
|
||||||
|
assert_interfaces_impls, assert_transitive_impls, const_concat, format_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Name of a [GraphQL type][0] in a GraphQL schema.
|
||||||
|
///
|
||||||
|
/// See [`BaseType`] for details.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
pub type Type = &'static str;
|
||||||
|
|
||||||
|
/// List of [`Type`]s.
|
||||||
|
///
|
||||||
|
/// See [`BaseSubTypes`] for details.
|
||||||
|
pub type Types = &'static [Type];
|
||||||
|
|
||||||
|
/// Basic reflection of a [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// This trait is transparent to [`Option`], [`Vec`] and other containers, so to
|
||||||
|
/// fully represent a [GraphQL object][1] we additionally use [`WrappedType`].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
pub trait BaseType<Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// [`Type`] of this [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// Different Rust types may have the same [`NAME`]. For example, [`String`]
|
||||||
|
/// and [`&str`](prim@str) share the `String!` GraphQL [`Type`].
|
||||||
|
///
|
||||||
|
/// [`NAME`]: Self::NAME
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
const NAME: Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflection of [sub-types][2] of a [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// This trait is transparent to [`Option`], [`Vec`] and other containers.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC
|
||||||
|
pub trait BaseSubTypes<Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// Sub-[`Types`] of this [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// Contains [at least][2] the [`BaseType::NAME`] of this [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC
|
||||||
|
const NAMES: Types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflection of [GraphQL interfaces][1] implementations for a
|
||||||
|
/// [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
pub trait Implements<Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// [`Types`] of the [GraphQL interfaces][1] implemented by this
|
||||||
|
/// [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
const NAMES: Types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encoded value of a [`WrappedType`] (composed [GraphQL wrapping type][0]).
|
||||||
|
///
|
||||||
|
/// See [`WrappedType`] for details.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Wrapping-Types
|
||||||
|
// TODO: Just use `&str`s once they're allowed in `const` generics.
|
||||||
|
pub type WrappedValue = u128;
|
||||||
|
|
||||||
|
/// [`WrappedValue`] encoding helpers.
|
||||||
|
pub mod wrap {
|
||||||
|
use super::WrappedValue;
|
||||||
|
|
||||||
|
/// [`WrappedValue`] of a singular non-nullable [GraphQL type][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
pub const SINGULAR: WrappedValue = 1;
|
||||||
|
|
||||||
|
/// Performs wrapping into a nullable [`WrappedValue`].
|
||||||
|
pub const fn nullable(val: WrappedValue) -> WrappedValue {
|
||||||
|
val * 10 + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs wrapping into a list [`WrappedValue`].
|
||||||
|
pub const fn list(val: WrappedValue) -> WrappedValue {
|
||||||
|
val * 10 + 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflection of a composed [GraphQL wrapping type][1], encoded in numbers.
|
||||||
|
///
|
||||||
|
/// To fully represent a [GraphQL type][0] it's not enough to use [`Type`],
|
||||||
|
/// because of the [wrapping types][1]. To work around this, a [`WrappedValue`]
|
||||||
|
/// is used, which is represented via [`u128`] number in the following encoding:
|
||||||
|
/// - In base case of non-nullable singular [type][0] [`VALUE`] is `1`.
|
||||||
|
/// - To represent nullability we "append" `2` to the [`VALUE`], so
|
||||||
|
/// [`Option`]`<`[type][0]`>` has [`VALUE`] of `12`.
|
||||||
|
/// - To represent a list we "append" `3` to the [`VALUE`], so
|
||||||
|
/// [`Vec`]`<`[type][0]`>` has [`VALUE`] of `13`.
|
||||||
|
///
|
||||||
|
/// Note, that due to Rust type system, the encoding here differs from the one
|
||||||
|
/// of [GraphQL wrapping types][1], as it takes nullability as wrapping, while
|
||||||
|
/// GraphQL [does the opposite][1] (takes non-nullability as wrapping).
|
||||||
|
///
|
||||||
|
/// This approach allows to uniquely represent any [GraphQL type][0] with a
|
||||||
|
/// combination of a [`Type`] and a [`WrappedValue`], and even format it via
|
||||||
|
/// [`format_type!`] macro in a `const` context.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use juniper::reflect::{
|
||||||
|
/// # format_type, BaseType, Type, WrappedType, WrappedValue,
|
||||||
|
/// # };
|
||||||
|
/// #
|
||||||
|
/// assert_eq!(<Option<i32> as WrappedType>::VALUE, 12);
|
||||||
|
/// assert_eq!(<Vec<i32> as WrappedType>::VALUE, 13);
|
||||||
|
/// assert_eq!(<Vec<Option<i32>> as WrappedType>::VALUE, 123);
|
||||||
|
/// assert_eq!(<Option<Vec<i32>> as WrappedType>::VALUE, 132);
|
||||||
|
/// assert_eq!(<Option<Vec<Option<i32>>> as WrappedType>::VALUE, 1232);
|
||||||
|
///
|
||||||
|
/// const TYPE_STRING: Type = <Option<Vec<Option<String>>> as BaseType>::NAME;
|
||||||
|
/// const WRAP_VAL_STRING: WrappedValue = <Option<Vec<Option<String>>> as WrappedType>::VALUE;
|
||||||
|
/// assert_eq!(format_type!(TYPE_STRING, WRAP_VAL_STRING), "[String]");
|
||||||
|
///
|
||||||
|
/// const TYPE_STR: Type = <Option<Vec<Option<&str>>> as BaseType>::NAME;
|
||||||
|
/// const WRAP_VAL_STR: WrappedValue = <Option<Vec<Option<&str>>> as WrappedType>::VALUE;
|
||||||
|
/// assert_eq!(format_type!(TYPE_STR, WRAP_VAL_STR), "[String]");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`VALUE`]: Self::VALUE
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Wrapping-Types
|
||||||
|
pub trait WrappedType<Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// [`WrappedValue`] of this this [GraphQL type][0], encoded in a number.
|
||||||
|
///
|
||||||
|
/// Use [`format_type!`] macro on this number to represent it as a
|
||||||
|
/// human-readable [GraphQL type][0] string.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
const VALUE: WrappedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name of a [GraphQL field][0] or a [field argument][1].
|
||||||
|
///
|
||||||
|
/// See [`Fields`] for details.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
||||||
|
pub type Name = &'static str;
|
||||||
|
|
||||||
|
/// List of [`Name`]s.
|
||||||
|
///
|
||||||
|
/// See [`Fields`] for details.
|
||||||
|
pub type Names = &'static [Name];
|
||||||
|
|
||||||
|
/// Reflection of [fields][0] for a [GraphQL object][1] or an [interface][2].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
pub trait Fields<Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// [`Names`] of this [GraphQL object][1]/[interface][2] [fields][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
const NAMES: Names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [GraphQL field argument][0], represented as its [`Name`], [`Type`] and
|
||||||
|
/// [`WrappedValue`].
|
||||||
|
///
|
||||||
|
/// See [`Field`] for details.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
||||||
|
pub type Argument = (Name, Type, WrappedValue);
|
||||||
|
|
||||||
|
/// List of [`Argument`]s.
|
||||||
|
///
|
||||||
|
/// See [`Field`] for details.
|
||||||
|
pub type Arguments = &'static [(Name, Type, WrappedValue)];
|
||||||
|
|
||||||
|
/// Alias for a `const`-hashed [`Name`] used in a `const` context.
|
||||||
|
// TODO: Just use `&str`s once they're allowed in `const` generics.
|
||||||
|
pub type FieldName = u128;
|
||||||
|
|
||||||
|
/// Reflection of a single [GraphQL field][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
pub trait Field<const N: FieldName, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
/// [`Type`] of this [GraphQL field][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
const TYPE: Type;
|
||||||
|
|
||||||
|
/// [Sub-types][1] this [GraphQL field][0] is coercible into.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [1]: BaseSubTypes
|
||||||
|
const SUB_TYPES: Types;
|
||||||
|
|
||||||
|
/// [`WrappedValue`] of this [GraphQL field][0].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
const WRAPPED_VALUE: WrappedValue;
|
||||||
|
|
||||||
|
/// [`Arguments`] of this [GraphQL field][0] .
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
const ARGUMENTS: Arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in
|
||||||
|
/// `const` generics. See [spec] for more info.
|
||||||
|
///
|
||||||
|
/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html
|
||||||
|
#[must_use]
|
||||||
|
pub const fn fnv1a128(str: Name) -> FieldName {
|
||||||
|
const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d;
|
||||||
|
const FNV_PRIME: u128 = 0x0000000001000000000000000000013b;
|
||||||
|
|
||||||
|
let bytes = str.as_bytes();
|
||||||
|
let mut hash = FNV_OFFSET_BASIS;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
hash ^= bytes[i] as u128;
|
||||||
|
hash = hash.wrapping_mul(FNV_PRIME);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Length __in bytes__ of the [`format_type!`] macro result.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize {
|
||||||
|
let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type!
|
||||||
|
|
||||||
|
let mut curr = val;
|
||||||
|
while curr % 10 != 0 {
|
||||||
|
match curr % 10 {
|
||||||
|
2 => len -= "!".as_bytes().len(), // remove !
|
||||||
|
3 => len += "[]!".as_bytes().len(), // [Type]!
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
curr /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether the specified `subtype` [GraphQL type][0] represents a
|
||||||
|
/// [sub-type][1] of the specified `supertype`, basing on the [`WrappedType`]
|
||||||
|
/// encoding.
|
||||||
|
///
|
||||||
|
/// To fully determine the [sub-typing][1] relation the [`Type`] should be one
|
||||||
|
/// of the [`BaseSubTypes::NAMES`].
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Types
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC
|
||||||
|
#[must_use]
|
||||||
|
pub const fn can_be_subtype(supertype: WrappedValue, subtype: WrappedValue) -> bool {
|
||||||
|
let super_curr = supertype % 10;
|
||||||
|
let sub_curr = subtype % 10;
|
||||||
|
|
||||||
|
if super_curr == sub_curr {
|
||||||
|
if super_curr == 1 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
can_be_subtype(supertype / 10, subtype / 10)
|
||||||
|
}
|
||||||
|
} else if super_curr == 2 {
|
||||||
|
can_be_subtype(supertype / 10, subtype)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether the given `val` exists in the given `arr`.
|
||||||
|
// TODO: Remove once `slice::contains()` method is allowed in `const` context.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < arr.len() {
|
||||||
|
if str_eq(val, arr[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares strings in a `const` context.
|
||||||
|
///
|
||||||
|
/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to
|
||||||
|
/// provide a custom comparison function.
|
||||||
|
///
|
||||||
|
/// [`Eq`]: std::cmp::Eq
|
||||||
|
// TODO: Remove once `Eq` trait is allowed in `const` context.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn str_eq(l: &str, r: &str) -> bool {
|
||||||
|
let (l, r) = (l.as_bytes(), r.as_bytes());
|
||||||
|
|
||||||
|
if l.len() != r.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < l.len() {
|
||||||
|
if l[i] != r[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
mod macros {
|
||||||
|
/// Asserts that `#[graphql::interface(for = ...)]` attribute has all the
|
||||||
|
/// types referencing this interface in the `impl = ...` attribute argument.
|
||||||
|
///
|
||||||
|
/// Symmetrical to [`assert_interfaces_impls!`].
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_implemented_for {
|
||||||
|
($behavior: ty, $implementor: ty $(, $interfaces: ty)* $(,)?) => {
|
||||||
|
const _: () = { $({
|
||||||
|
let is_present = $crate::reflect::str_exists_in_arr(
|
||||||
|
<$implementor as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
<$interfaces as $crate::reflect::BaseSubTypes<$behavior>>::NAMES,
|
||||||
|
);
|
||||||
|
if !is_present {
|
||||||
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
"Failed to implement interface `",
|
||||||
|
<$interfaces as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"` on `",
|
||||||
|
<$implementor as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"`: missing implementer reference in interface's `for` attribute.",
|
||||||
|
);
|
||||||
|
::std::panic!("{}", MSG);
|
||||||
|
}
|
||||||
|
})* };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts that `impl = ...` attribute argument has all the interfaces
|
||||||
|
/// referencing this type in `#[graphql::interface(for = ...)]` attribute.
|
||||||
|
///
|
||||||
|
/// Symmetrical to [`assert_implemented_for!`].
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_interfaces_impls {
|
||||||
|
($behavior: ty, $interface: ty $(, $implementers: ty)* $(,)?) => {
|
||||||
|
const _: () = { $({
|
||||||
|
let is_present = $crate::reflect::str_exists_in_arr(
|
||||||
|
<$interface as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
<$implementers as $crate::reflect::Implements<$behavior>>::NAMES,
|
||||||
|
);
|
||||||
|
if !is_present {
|
||||||
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
"Failed to implement interface `",
|
||||||
|
<$interface as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"` on `",
|
||||||
|
<$implementers as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"`: missing interface reference in implementer's `impl` attribute.",
|
||||||
|
);
|
||||||
|
::std::panic!("{}", MSG);
|
||||||
|
}
|
||||||
|
})* };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts that all [transitive interfaces][0] (the ones implemented by the
|
||||||
|
/// `$interface`) are also implemented by the `$implementor`.
|
||||||
|
///
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sel-FAHbhBHCAACGB35P
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_transitive_impls {
|
||||||
|
($behavior: ty, $interface: ty, $implementor: ty $(, $transitive: ty)* $(,)?) => {
|
||||||
|
const _: () = { $({
|
||||||
|
let is_present = $crate::reflect::str_exists_in_arr(
|
||||||
|
<$implementor as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
<$transitive as $crate::reflect::BaseSubTypes<$behavior>>::NAMES,
|
||||||
|
);
|
||||||
|
if !is_present {
|
||||||
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
"Failed to implement interface `",
|
||||||
|
<$interface as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"` on `",
|
||||||
|
<$implementor as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"`: missing `impl = ` for transitive interface `",
|
||||||
|
<$transitive as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"` on `",
|
||||||
|
<$implementor as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"`.",
|
||||||
|
);
|
||||||
|
::std::panic!("{}", MSG);
|
||||||
|
}
|
||||||
|
})* };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts validness of [`Field`] [`Arguments`] and its returned [`Type`].
|
||||||
|
///
|
||||||
|
/// This assertion is a combination of [`assert_field_type!`] and
|
||||||
|
/// [`assert_field_args!`].
|
||||||
|
///
|
||||||
|
/// See [spec][0] for assertion algorithm details.
|
||||||
|
///
|
||||||
|
/// [`Arguments`]: super::Arguments
|
||||||
|
/// [`Field`]: super::Field
|
||||||
|
/// [`Type`]: super::Type
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#IsValidImplementation()
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_field {
|
||||||
|
(
|
||||||
|
$base_ty: ty,
|
||||||
|
$impl_ty: ty,
|
||||||
|
$behavior: ty,
|
||||||
|
$field_name: expr $(,)?
|
||||||
|
) => {
|
||||||
|
$crate::reflect::assert_field_type!($base_ty, $impl_ty, $behavior, $field_name);
|
||||||
|
$crate::reflect::assert_field_args!($base_ty, $impl_ty, $behavior, $field_name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts validness of a [`Field`] type.
|
||||||
|
///
|
||||||
|
/// See [spec][0] for assertion algorithm details.
|
||||||
|
///
|
||||||
|
/// [`Field`]: super::Field
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#IsValidImplementationFieldType()
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_field_type {
|
||||||
|
(
|
||||||
|
$base_ty: ty,
|
||||||
|
$impl_ty: ty,
|
||||||
|
$behavior: ty,
|
||||||
|
$field_name: expr $(,)?
|
||||||
|
) => {
|
||||||
|
const _: () = {
|
||||||
|
const BASE_TY: $crate::reflect::Type =
|
||||||
|
<$base_ty as $crate::reflect::BaseType<$behavior>>::NAME;
|
||||||
|
const IMPL_TY: $crate::reflect::Type =
|
||||||
|
<$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME;
|
||||||
|
const ERR_PREFIX: &str = $crate::reflect::const_concat!(
|
||||||
|
"Failed to implement interface `",
|
||||||
|
BASE_TY,
|
||||||
|
"` on `",
|
||||||
|
IMPL_TY,
|
||||||
|
"`: ",
|
||||||
|
);
|
||||||
|
|
||||||
|
const FIELD_NAME: $crate::reflect::Name = $field_name;
|
||||||
|
const FIELD_NAME_HASH: $crate::reflect::FieldName =
|
||||||
|
$crate::reflect::fnv1a128(FIELD_NAME);
|
||||||
|
|
||||||
|
$crate::reflect::assert_has_field!(
|
||||||
|
FIELD_NAME, $base_ty, $behavior, ERR_PREFIX,
|
||||||
|
);
|
||||||
|
$crate::reflect::assert_has_field!(
|
||||||
|
FIELD_NAME, $impl_ty, $behavior, ERR_PREFIX,
|
||||||
|
);
|
||||||
|
|
||||||
|
const BASE_RETURN_WRAPPED_VAL: $crate::reflect::WrappedValue =
|
||||||
|
<$base_ty as $crate::reflect::Field<
|
||||||
|
FIELD_NAME_HASH, $behavior,
|
||||||
|
>>::WRAPPED_VALUE;
|
||||||
|
const IMPL_RETURN_WRAPPED_VAL: $crate::reflect::WrappedValue =
|
||||||
|
<$impl_ty as $crate::reflect::Field<
|
||||||
|
FIELD_NAME_HASH, $behavior,
|
||||||
|
>>::WRAPPED_VALUE;
|
||||||
|
|
||||||
|
const BASE_RETURN_TY: $crate::reflect::Type =
|
||||||
|
<$base_ty as $crate::reflect::Field<
|
||||||
|
FIELD_NAME_HASH, $behavior,
|
||||||
|
>>::TYPE;
|
||||||
|
const IMPL_RETURN_TY: $crate::reflect::Type =
|
||||||
|
<$impl_ty as $crate::reflect::Field<
|
||||||
|
FIELD_NAME_HASH, $behavior,
|
||||||
|
>>::TYPE;
|
||||||
|
|
||||||
|
const BASE_RETURN_SUB_TYPES: $crate::reflect::Types =
|
||||||
|
<$base_ty as $crate::reflect::Field<
|
||||||
|
FIELD_NAME_HASH, $behavior,
|
||||||
|
>>::SUB_TYPES;
|
||||||
|
|
||||||
|
let is_subtype = $crate::reflect::str_exists_in_arr(
|
||||||
|
IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES,
|
||||||
|
) && $crate::reflect::can_be_subtype(
|
||||||
|
BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL,
|
||||||
|
);
|
||||||
|
if !is_subtype {
|
||||||
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
ERR_PREFIX,
|
||||||
|
"Field `",
|
||||||
|
FIELD_NAME,
|
||||||
|
"`: implementor is expected to return a subtype of interface's return object: `",
|
||||||
|
$crate::reflect::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL),
|
||||||
|
"` is not a subtype of `",
|
||||||
|
$crate::reflect::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL),
|
||||||
|
"`.",
|
||||||
|
);
|
||||||
|
::std::panic!("{}", MSG);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts validness of a [`Field`] arguments.
|
||||||
|
///
|
||||||
|
/// See [spec][0] for assertion algorithm details.
|
||||||
|
///
|
||||||
|
/// [`Field`]: super::Field
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sel-IAHZhCHCDEEFAAADHD8Cxob
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_field_args {
|
||||||
|
(
|
||||||
|
$base_ty: ty,
|
||||||
|
$impl_ty: ty,
|
||||||
|
$behavior: ty,
|
||||||
|
$field_name: expr $(,)?
|
||||||
|
) => {
|
||||||
|
const _: () = {
|
||||||
|
const BASE_TY: $crate::reflect::Type =
|
||||||
|
<$base_ty as $crate::reflect::BaseType<$behavior>>::NAME;
|
||||||
|
const IMPL_TY: $crate::reflect::Type =
|
||||||
|
<$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME;
|
||||||
|
const ERR_PREFIX: &str = $crate::reflect::const_concat!(
|
||||||
|
"Failed to implement interface `",
|
||||||
|
BASE_TY,
|
||||||
|
"` on `",
|
||||||
|
IMPL_TY,
|
||||||
|
"`: ",
|
||||||
|
);
|
||||||
|
|
||||||
|
const FIELD_NAME: $crate::reflect::Name = $field_name;
|
||||||
|
const FIELD_NAME_HASH: $crate::reflect::FieldName =
|
||||||
|
$crate::reflect::fnv1a128(FIELD_NAME);
|
||||||
|
|
||||||
|
$crate::reflect::assert_has_field!(FIELD_NAME, $base_ty, $behavior, ERR_PREFIX);
|
||||||
|
$crate::reflect::assert_has_field!(FIELD_NAME, $impl_ty, $behavior, ERR_PREFIX);
|
||||||
|
|
||||||
|
const BASE_ARGS: ::juniper::reflect::Arguments =
|
||||||
|
<$base_ty as $crate::reflect::Field<FIELD_NAME_HASH, $behavior>>::ARGUMENTS;
|
||||||
|
const IMPL_ARGS: ::juniper::reflect::Arguments =
|
||||||
|
<$impl_ty as $crate::reflect::Field<FIELD_NAME_HASH, $behavior>>::ARGUMENTS;
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
cause: Cause,
|
||||||
|
base: ::juniper::reflect::Argument,
|
||||||
|
implementation: ::juniper::reflect::Argument,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Cause {
|
||||||
|
RequiredField,
|
||||||
|
AdditionalNonNullableField,
|
||||||
|
TypeMismatch,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn unwrap_error(v: ::std::result::Result<(), Error>) -> Error {
|
||||||
|
match v {
|
||||||
|
// Unfortunately, we cannot use `unreachable!()` here,
|
||||||
|
// as this branch will be executed either way.
|
||||||
|
Ok(()) => Error {
|
||||||
|
cause: Cause::RequiredField,
|
||||||
|
base: ("unreachable", "unreachable", 1),
|
||||||
|
implementation: ("unreachable", "unreachable", 1),
|
||||||
|
},
|
||||||
|
Err(e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn check() -> Result<(), Error> {
|
||||||
|
let mut base_i = 0;
|
||||||
|
while base_i < BASE_ARGS.len() {
|
||||||
|
let (base_name, base_type, base_wrap_val) = BASE_ARGS[base_i];
|
||||||
|
|
||||||
|
let mut impl_i = 0;
|
||||||
|
let mut was_found = false;
|
||||||
|
while impl_i < IMPL_ARGS.len() {
|
||||||
|
let (impl_name, impl_type, impl_wrap_val) = IMPL_ARGS[impl_i];
|
||||||
|
|
||||||
|
if $crate::reflect::str_eq(base_name, impl_name) {
|
||||||
|
if $crate::reflect::str_eq(base_type, impl_type)
|
||||||
|
&& base_wrap_val == impl_wrap_val
|
||||||
|
{
|
||||||
|
was_found = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return Err(Error {
|
||||||
|
cause: Cause::TypeMismatch,
|
||||||
|
base: (base_name, base_type, base_wrap_val),
|
||||||
|
implementation: (impl_name, impl_type, impl_wrap_val),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !was_found {
|
||||||
|
return Err(Error {
|
||||||
|
cause: Cause::RequiredField,
|
||||||
|
base: (base_name, base_type, base_wrap_val),
|
||||||
|
implementation: (base_name, base_type, base_wrap_val),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
base_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut impl_i = 0;
|
||||||
|
while impl_i < IMPL_ARGS.len() {
|
||||||
|
let (impl_name, impl_type, impl_wrapped_val) = IMPL_ARGS[impl_i];
|
||||||
|
impl_i += 1;
|
||||||
|
|
||||||
|
if impl_wrapped_val % 10 == 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut base_i = 0;
|
||||||
|
let mut was_found = false;
|
||||||
|
while base_i < BASE_ARGS.len() {
|
||||||
|
let (base_name, _, _) = BASE_ARGS[base_i];
|
||||||
|
if $crate::reflect::str_eq(base_name, impl_name) {
|
||||||
|
was_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
base_i += 1;
|
||||||
|
}
|
||||||
|
if !was_found {
|
||||||
|
return Err(Error {
|
||||||
|
cause: Cause::AdditionalNonNullableField,
|
||||||
|
base: (impl_name, impl_type, impl_wrapped_val),
|
||||||
|
implementation: (impl_name, impl_type, impl_wrapped_val),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
const RES: ::std::result::Result<(), Error> = check();
|
||||||
|
if RES.is_err() {
|
||||||
|
const ERROR: Error = unwrap_error(RES);
|
||||||
|
|
||||||
|
const BASE_ARG_NAME: $crate::reflect::Name = ERROR.base.0;
|
||||||
|
const IMPL_ARG_NAME: $crate::reflect::Name = ERROR.implementation.0;
|
||||||
|
|
||||||
|
const BASE_TYPE_FORMATTED: &str =
|
||||||
|
$crate::reflect::format_type!(ERROR.base.1, ERROR.base.2,);
|
||||||
|
const IMPL_TYPE_FORMATTED: &str = $crate::reflect::format_type!(
|
||||||
|
ERROR.implementation.1,
|
||||||
|
ERROR.implementation.2,
|
||||||
|
);
|
||||||
|
|
||||||
|
const MSG: &str = match ERROR.cause {
|
||||||
|
Cause::TypeMismatch => {
|
||||||
|
$crate::reflect::const_concat!(
|
||||||
|
"Argument `",
|
||||||
|
BASE_ARG_NAME,
|
||||||
|
"`: expected type `",
|
||||||
|
BASE_TYPE_FORMATTED,
|
||||||
|
"`, found: `",
|
||||||
|
IMPL_TYPE_FORMATTED,
|
||||||
|
"`.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Cause::RequiredField => {
|
||||||
|
$crate::reflect::const_concat!(
|
||||||
|
"Argument `",
|
||||||
|
BASE_ARG_NAME,
|
||||||
|
"` of type `",
|
||||||
|
BASE_TYPE_FORMATTED,
|
||||||
|
"` was expected, but not found.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Cause::AdditionalNonNullableField => {
|
||||||
|
$crate::reflect::const_concat!(
|
||||||
|
"Argument `",
|
||||||
|
IMPL_ARG_NAME,
|
||||||
|
"` of type `",
|
||||||
|
IMPL_TYPE_FORMATTED,
|
||||||
|
"` isn't present on the interface and so has to be nullable.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ERROR_MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG,
|
||||||
|
);
|
||||||
|
::std::panic!("{}", ERROR_MSG);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the given `$impl_ty` has the specified [`Field`].
|
||||||
|
///
|
||||||
|
/// [`Field`]: super::Field
|
||||||
|
/// [`fnv1a128`]: super::fnv1a128
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_assert_has_field {
|
||||||
|
(
|
||||||
|
$field_name: expr,
|
||||||
|
$impl_ty: ty,
|
||||||
|
$behavior: ty
|
||||||
|
$(, $prefix: expr)? $(,)?
|
||||||
|
) => {{
|
||||||
|
let exists = $crate::reflect::str_exists_in_arr(
|
||||||
|
$field_name,
|
||||||
|
<$impl_ty as $crate::reflect::Fields<$behavior>>::NAMES,
|
||||||
|
);
|
||||||
|
if !exists {
|
||||||
|
const MSG: &str = $crate::reflect::const_concat!(
|
||||||
|
$($prefix,)?
|
||||||
|
"Field `",
|
||||||
|
$field_name,
|
||||||
|
"` isn't implemented on `",
|
||||||
|
<$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME,
|
||||||
|
"`."
|
||||||
|
);
|
||||||
|
::std::panic!("{}", MSG);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Concatenates `const` [`str`](prim@str)s in a `const` context.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_const_concat {
|
||||||
|
($($s:expr),* $(,)?) => {{
|
||||||
|
const LEN: usize = 0 $(+ $s.as_bytes().len())*;
|
||||||
|
const CNT: usize = [$($s),*].len();
|
||||||
|
const fn concat(input: [&str; CNT]) -> [u8; LEN] {
|
||||||
|
let mut bytes = [0; LEN];
|
||||||
|
let (mut i, mut byte) = (0, 0);
|
||||||
|
while i < CNT {
|
||||||
|
let mut b = 0;
|
||||||
|
while b < input[i].len() {
|
||||||
|
bytes[byte] = input[i].as_bytes()[b];
|
||||||
|
byte += 1;
|
||||||
|
b += 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
const CON: [u8; LEN] = concat([$($s),*]);
|
||||||
|
|
||||||
|
// TODO: Use `.unwrap()` once it becomes `const`.
|
||||||
|
match ::std::str::from_utf8(&CON) {
|
||||||
|
::std::result::Result::Ok(s) => s,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats the given [`Type`] and [`WrappedValue`] into a human-readable
|
||||||
|
/// GraphQL type name string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use juniper::reflect::format_type;
|
||||||
|
/// #
|
||||||
|
/// assert_eq!(format_type!("String", 123), "[String]!");
|
||||||
|
/// assert_eq!(format_type!("🦀", 123), "[🦀]!");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`Type`]: super::Type
|
||||||
|
/// [`WrappedValue`]: super::WrappedValue
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! reflect_format_type {
|
||||||
|
($ty: expr, $wrapped_value: expr $(,)?) => {{
|
||||||
|
const TYPE: ($crate::reflect::Type, $crate::reflect::WrappedValue) =
|
||||||
|
($ty, $wrapped_value);
|
||||||
|
const RES_LEN: usize = $crate::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1);
|
||||||
|
|
||||||
|
const OPENING_BRACKET: &str = "[";
|
||||||
|
const CLOSING_BRACKET: &str = "]";
|
||||||
|
const BANG: &str = "!";
|
||||||
|
|
||||||
|
const fn format_type_arr() -> [u8; RES_LEN] {
|
||||||
|
let (ty, wrap_val) = TYPE;
|
||||||
|
let mut type_arr: [u8; RES_LEN] = [0; RES_LEN];
|
||||||
|
|
||||||
|
let mut current_start = 0;
|
||||||
|
let mut current_end = RES_LEN - 1;
|
||||||
|
let mut current_wrap_val = wrap_val;
|
||||||
|
let mut is_null = false;
|
||||||
|
while current_wrap_val % 10 != 0 {
|
||||||
|
match current_wrap_val % 10 {
|
||||||
|
2 => is_null = true, // Skips writing `BANG` later.
|
||||||
|
3 => {
|
||||||
|
// Write `OPENING_BRACKET` at `current_start`.
|
||||||
|
let mut i = 0;
|
||||||
|
while i < OPENING_BRACKET.as_bytes().len() {
|
||||||
|
type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
current_start += i;
|
||||||
|
if !is_null {
|
||||||
|
// Write `BANG` at `current_end`.
|
||||||
|
i = 0;
|
||||||
|
while i < BANG.as_bytes().len() {
|
||||||
|
type_arr[current_end - BANG.as_bytes().len() + i + 1] =
|
||||||
|
BANG.as_bytes()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
current_end -= i;
|
||||||
|
}
|
||||||
|
// Write `CLOSING_BRACKET` at `current_end`.
|
||||||
|
i = 0;
|
||||||
|
while i < CLOSING_BRACKET.as_bytes().len() {
|
||||||
|
type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] =
|
||||||
|
CLOSING_BRACKET.as_bytes()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
current_end -= i;
|
||||||
|
is_null = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_wrap_val /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes `Type` at `current_start`.
|
||||||
|
let mut i = 0;
|
||||||
|
while i < ty.as_bytes().len() {
|
||||||
|
type_arr[current_start + i] = ty.as_bytes()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
if !is_null {
|
||||||
|
// Writes `BANG` at `current_end`.
|
||||||
|
while i < BANG.as_bytes().len() {
|
||||||
|
type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_arr
|
||||||
|
}
|
||||||
|
|
||||||
|
const TYPE_ARR: [u8; RES_LEN] = format_type_arr();
|
||||||
|
|
||||||
|
// TODO: Use `.unwrap()` once it becomes `const`.
|
||||||
|
const TYPE_FORMATTED: &str = match ::std::str::from_utf8(TYPE_ARR.as_slice()) {
|
||||||
|
::std::result::Result::Ok(s) => s,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
TYPE_FORMATTED
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use {
|
||||||
|
reflect_assert_field as assert_field, reflect_assert_field_args as assert_field_args,
|
||||||
|
reflect_assert_field_type as assert_field_type,
|
||||||
|
reflect_assert_has_field as assert_has_field,
|
||||||
|
reflect_assert_implemented_for as assert_implemented_for,
|
||||||
|
reflect_assert_interfaces_impls as assert_interfaces_impls,
|
||||||
|
reflect_assert_transitive_impls as assert_transitive_impls,
|
||||||
|
reflect_const_concat as const_concat, reflect_format_type as format_type,
|
||||||
|
};
|
||||||
|
}
|
211
juniper/src/resolve/mod.rs
Normal file
211
juniper/src/resolve/mod.rs
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
use crate::{
|
||||||
|
behavior, graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{self, ParseError},
|
||||||
|
reflect, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, IntoFieldError,
|
||||||
|
Registry, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Type<TypeInfo: ?Sized, ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
fn meta<'r, 'ti: 'r>(
|
||||||
|
registry: &mut Registry<'r, ScalarValue>,
|
||||||
|
type_info: &'ti TypeInfo,
|
||||||
|
) -> MetaType<'r, ScalarValue>
|
||||||
|
where
|
||||||
|
ScalarValue: 'r; // TODO: remove?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TypeName<TypeInfo: ?Sized, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
fn type_name(type_info: &TypeInfo) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ConcreteTypeName<TypeInfo: ?Sized, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TypeInfo) -> &'i str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Value<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, ScalarValue>]>,
|
||||||
|
type_info: &TypeInfo,
|
||||||
|
executor: &Executor<Context, ScalarValue>,
|
||||||
|
) -> ExecutionResult<ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ValueAsync<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, ScalarValue>]>,
|
||||||
|
type_info: &'r TypeInfo,
|
||||||
|
executor: &'r Executor<Context, ScalarValue>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<ScalarValue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Resolvable<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
type Value;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self::Value, ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ConcreteValue<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, ScalarValue>]>,
|
||||||
|
type_info: &TypeInfo,
|
||||||
|
executor: &Executor<Context, ScalarValue>,
|
||||||
|
) -> ExecutionResult<ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ConcreteValueAsync<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, ScalarValue>]>,
|
||||||
|
type_info: &'r TypeInfo,
|
||||||
|
executor: &'r Executor<Context, ScalarValue>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<ScalarValue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Field<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<ScalarValue>,
|
||||||
|
type_info: &TypeInfo,
|
||||||
|
executor: &Executor<Context, ScalarValue>,
|
||||||
|
) -> ExecutionResult<ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StaticField<
|
||||||
|
const N: reflect::FieldName,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_static_field(
|
||||||
|
&self,
|
||||||
|
arguments: &Arguments<'_, ScalarValue>,
|
||||||
|
type_info: &TypeInfo,
|
||||||
|
executor: &Executor<'_, '_, Context, ScalarValue>,
|
||||||
|
) -> ExecutionResult<ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FieldAsync<
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<ScalarValue>,
|
||||||
|
type_info: &'r TypeInfo,
|
||||||
|
executor: &'r Executor<Context, ScalarValue>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<ScalarValue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StaticFieldAsync<
|
||||||
|
const N: reflect::FieldName,
|
||||||
|
TypeInfo: ?Sized,
|
||||||
|
Context: ?Sized,
|
||||||
|
ScalarValue,
|
||||||
|
Behavior: ?Sized = behavior::Standard,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn resolve_static_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
arguments: &'r Arguments<ScalarValue>,
|
||||||
|
type_info: &'r TypeInfo,
|
||||||
|
executor: &'r Executor<Context, ScalarValue>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<ScalarValue>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToInputValue<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<ScalarValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputValue<'input, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard>:
|
||||||
|
Sized
|
||||||
|
{
|
||||||
|
type Error: IntoFieldError<ScalarValue>;
|
||||||
|
|
||||||
|
fn try_from_input_value(
|
||||||
|
v: &'input graphql::InputValue<ScalarValue>,
|
||||||
|
) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from_input_value(&graphql::InputValue::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputValueOwned<ScalarValue, Behavior: ?Sized = behavior::Standard>:
|
||||||
|
for<'i> InputValue<'i, ScalarValue, Behavior>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH: ?Sized> InputValueOwned<SV, BH> for T where T: for<'i> InputValue<'i, SV, BH> {}
|
||||||
|
|
||||||
|
pub trait InputValueAs<'input, Wrapper, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard>
|
||||||
|
{
|
||||||
|
type Error: IntoFieldError<ScalarValue>;
|
||||||
|
|
||||||
|
fn try_from_input_value(
|
||||||
|
v: &'input graphql::InputValue<ScalarValue>,
|
||||||
|
) -> Result<Wrapper, Self::Error>;
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Wrapper, Self::Error> {
|
||||||
|
Self::try_from_input_value(&graphql::InputValue::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputValueAsRef<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
type Error: IntoFieldError<ScalarValue>;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &graphql::InputValue<ScalarValue>) -> Result<&Self, Self::Error>;
|
||||||
|
|
||||||
|
fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error>
|
||||||
|
where
|
||||||
|
ScalarValue: 'a,
|
||||||
|
{
|
||||||
|
Self::try_from_input_value(&graphql::InputValue::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ScalarToken<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
|
fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result<ScalarValue, ParseError>;
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
//! Types used to describe a `GraphQL` schema
|
//! Types used to describe a `GraphQL` schema
|
||||||
|
|
||||||
use juniper::IntoFieldError;
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::{Cow, ToOwned},
|
borrow::{Cow, ToOwned},
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -9,10 +8,11 @@ use std::{
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FromInputValue, InputValue, Type},
|
ast::{FromInputValue, InputValue, Type},
|
||||||
parser::{ParseError, ScalarToken},
|
parser::{ParseError, ScalarToken},
|
||||||
|
resolve,
|
||||||
schema::model::SchemaType,
|
schema::model::SchemaType,
|
||||||
types::base::TypeKind,
|
types::base::TypeKind,
|
||||||
value::{DefaultScalarValue, ParseScalarValue},
|
value::{DefaultScalarValue, ParseScalarValue},
|
||||||
FieldError,
|
FieldError, IntoFieldError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Whether an item is deprecated, with context.
|
/// Whether an item is deprecated, with context.
|
||||||
|
@ -28,16 +28,16 @@ impl DeprecationStatus {
|
||||||
/// If this deprecation status indicates the item is deprecated.
|
/// If this deprecation status indicates the item is deprecated.
|
||||||
pub fn is_deprecated(&self) -> bool {
|
pub fn is_deprecated(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DeprecationStatus::Current => false,
|
Self::Current => false,
|
||||||
DeprecationStatus::Deprecated(_) => true,
|
Self::Deprecated(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An optional reason for the deprecation, or none if `Current`.
|
/// An optional reason for the deprecation, or none if `Current`.
|
||||||
pub fn reason(&self) -> Option<&str> {
|
pub fn reason(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
DeprecationStatus::Current => None,
|
Self::Current => None,
|
||||||
DeprecationStatus::Deprecated(rsn) => rsn.as_deref(),
|
Self::Deprecated(rsn) => rsn.as_deref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,20 @@ pub struct ScalarMeta<'a, S> {
|
||||||
pub(crate) parse_fn: ScalarTokenParseFn<S>,
|
pub(crate) parse_fn: ScalarTokenParseFn<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual implementation is required here to omit redundant `S: Clone` trait
|
||||||
|
// bound, imposed by `#[derive(Clone)]`.
|
||||||
|
impl<'a, S> Clone for ScalarMeta<'a, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
name: self.name.clone(),
|
||||||
|
description: self.description.clone(),
|
||||||
|
specified_by_url: self.specified_by_url.clone(),
|
||||||
|
try_parse_fn: self.try_parse_fn,
|
||||||
|
parse_fn: self.parse_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shortcut for an [`InputValue`] parsing function.
|
/// Shortcut for an [`InputValue`] parsing function.
|
||||||
pub type InputValueParseFn<S> = for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>;
|
pub type InputValueParseFn<S> = for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>;
|
||||||
|
|
||||||
|
@ -61,7 +75,7 @@ pub type InputValueParseFn<S> = for<'b> fn(&'b InputValue<S>) -> Result<(), Fiel
|
||||||
pub type ScalarTokenParseFn<S> = for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError>;
|
pub type ScalarTokenParseFn<S> = for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError>;
|
||||||
|
|
||||||
/// List type metadata
|
/// List type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ListMeta<'a> {
|
pub struct ListMeta<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub of_type: Type<'a>,
|
pub of_type: Type<'a>,
|
||||||
|
@ -71,14 +85,14 @@ pub struct ListMeta<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nullable type metadata
|
/// Nullable type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NullableMeta<'a> {
|
pub struct NullableMeta<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub of_type: Type<'a>,
|
pub of_type: Type<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Object type metadata
|
/// Object type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ObjectMeta<'a, S> {
|
pub struct ObjectMeta<'a, S> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
|
@ -101,8 +115,21 @@ pub struct EnumMeta<'a, S> {
|
||||||
pub(crate) try_parse_fn: InputValueParseFn<S>,
|
pub(crate) try_parse_fn: InputValueParseFn<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual implementation is required here to omit redundant `S: Clone` trait
|
||||||
|
// bound, imposed by `#[derive(Clone)]`.
|
||||||
|
impl<'a, S> Clone for EnumMeta<'a, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
name: self.name.clone(),
|
||||||
|
description: self.description.clone(),
|
||||||
|
values: self.values.clone(),
|
||||||
|
try_parse_fn: self.try_parse_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Interface type metadata
|
/// Interface type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct InterfaceMeta<'a, S> {
|
pub struct InterfaceMeta<'a, S> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
|
@ -115,7 +142,7 @@ pub struct InterfaceMeta<'a, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Union type metadata
|
/// Union type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UnionMeta<'a> {
|
pub struct UnionMeta<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
|
@ -126,6 +153,7 @@ pub struct UnionMeta<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Input object metadata
|
/// Input object metadata
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct InputObjectMeta<'a, S> {
|
pub struct InputObjectMeta<'a, S> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
|
@ -140,14 +168,14 @@ pub struct InputObjectMeta<'a, S> {
|
||||||
///
|
///
|
||||||
/// After a type's `meta` method has been called but before it has returned, a placeholder type
|
/// After a type's `meta` method has been called but before it has returned, a placeholder type
|
||||||
/// is inserted into a registry to indicate existence.
|
/// is inserted into a registry to indicate existence.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlaceholderMeta<'a> {
|
pub struct PlaceholderMeta<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub of_type: Type<'a>,
|
pub of_type: Type<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic type metadata
|
/// Generic type metadata
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MetaType<'a, S = DefaultScalarValue> {
|
pub enum MetaType<'a, S = DefaultScalarValue> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
Scalar(ScalarMeta<'a, S>),
|
Scalar(ScalarMeta<'a, S>),
|
||||||
|
@ -170,7 +198,7 @@ pub enum MetaType<'a, S = DefaultScalarValue> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata for a field
|
/// Metadata for a field
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Field<'a, S> {
|
pub struct Field<'a, S> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: smartstring::alias::String,
|
pub name: smartstring::alias::String,
|
||||||
|
@ -193,7 +221,7 @@ impl<'a, S> Field<'a, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata for an argument to a field
|
/// Metadata for an argument to a field
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Argument<'a, S> {
|
pub struct Argument<'a, S> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -434,6 +462,29 @@ impl<'a, S> MetaType<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, S> From<MetaType<'a, S>> for Type<'a> {
|
||||||
|
fn from(meta: MetaType<'a, S>) -> Self {
|
||||||
|
match meta {
|
||||||
|
MetaType::Scalar(ScalarMeta { name, .. })
|
||||||
|
| MetaType::Object(ObjectMeta { name, .. })
|
||||||
|
| MetaType::Enum(EnumMeta { name, .. })
|
||||||
|
| MetaType::Interface(InterfaceMeta { name, .. })
|
||||||
|
| MetaType::Union(UnionMeta { name, .. })
|
||||||
|
| MetaType::InputObject(InputObjectMeta { name, .. }) => Type::NonNullNamed(name),
|
||||||
|
MetaType::List(ListMeta {
|
||||||
|
of_type,
|
||||||
|
expected_size,
|
||||||
|
}) => Type::NonNullList(Box::new(of_type), expected_size),
|
||||||
|
MetaType::Nullable(NullableMeta { of_type }) => match of_type {
|
||||||
|
Type::NonNullNamed(inner) => Type::Named(inner),
|
||||||
|
Type::NonNullList(inner, expected_size) => Type::List(inner, expected_size),
|
||||||
|
t => t,
|
||||||
|
},
|
||||||
|
MetaType::Placeholder(PlaceholderMeta { of_type }) => of_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, S> ScalarMeta<'a, S> {
|
impl<'a, S> ScalarMeta<'a, S> {
|
||||||
/// Builds a new [`ScalarMeta`] type with the specified `name`.
|
/// Builds a new [`ScalarMeta`] type with the specified `name`.
|
||||||
pub fn new<T>(name: Cow<'a, str>) -> Self
|
pub fn new<T>(name: Cow<'a, str>) -> Self
|
||||||
|
@ -450,6 +501,35 @@ impl<'a, S> ScalarMeta<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a new [`ScalarMeta`] information with the specified `name`.
|
||||||
|
pub fn new_reworked<T>(name: impl Into<Cow<'a, str>>) -> Self
|
||||||
|
where
|
||||||
|
T: resolve::InputValueOwned<S> + resolve::ScalarToken<S>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
description: None,
|
||||||
|
specified_by_url: None,
|
||||||
|
try_parse_fn: try_parse_fn_reworked::<T, S>,
|
||||||
|
parse_fn: <T as resolve::ScalarToken<S>>::parse_scalar_token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a new [`ScalarMeta`] information with the specified `name` for
|
||||||
|
/// the non-[`Sized`] `T`ype that may only be parsed as a reference.
|
||||||
|
pub fn new_unsized<T>(name: impl Into<Cow<'a, str>>) -> Self
|
||||||
|
where
|
||||||
|
T: resolve::InputValueAsRef<S> + resolve::ScalarToken<S> + ?Sized,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
description: None,
|
||||||
|
specified_by_url: None,
|
||||||
|
try_parse_fn: try_parse_unsized_fn::<T, S>,
|
||||||
|
parse_fn: <T as resolve::ScalarToken<S>>::parse_scalar_token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the `description` of this [`ScalarMeta`] type.
|
/// Sets the `description` of this [`ScalarMeta`] type.
|
||||||
///
|
///
|
||||||
/// Overwrites any previously set description.
|
/// Overwrites any previously set description.
|
||||||
|
@ -477,7 +557,7 @@ impl<'a, S> ScalarMeta<'a, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ListMeta<'a> {
|
impl<'a> ListMeta<'a> {
|
||||||
/// Build a new [`ListMeta`] type by wrapping the specified [`Type`].
|
/// Builds a new [`ListMeta`] type by wrapping the specified [`Type`].
|
||||||
///
|
///
|
||||||
/// Specifying `expected_size` will be used to ensure that values of this
|
/// Specifying `expected_size` will be used to ensure that values of this
|
||||||
/// type will always match it.
|
/// type will always match it.
|
||||||
|
@ -495,7 +575,7 @@ impl<'a> ListMeta<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NullableMeta<'a> {
|
impl<'a> NullableMeta<'a> {
|
||||||
/// Build a new [`NullableMeta`] type by wrapping the specified [`Type`].
|
/// Builds a new [`NullableMeta`] type by wrapping the specified [`Type`].
|
||||||
pub fn new(of_type: Type<'a>) -> Self {
|
pub fn new(of_type: Type<'a>) -> Self {
|
||||||
Self { of_type }
|
Self { of_type }
|
||||||
}
|
}
|
||||||
|
@ -563,6 +643,20 @@ impl<'a, S> EnumMeta<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a new [`EnumMeta`] information with the specified `name` and
|
||||||
|
/// possible `values`.
|
||||||
|
pub fn new_reworked<T>(name: impl Into<Cow<'a, str>>, values: &[EnumValue]) -> Self
|
||||||
|
where
|
||||||
|
T: resolve::InputValueOwned<S>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
description: None,
|
||||||
|
values: values.to_owned(),
|
||||||
|
try_parse_fn: try_parse_fn_reworked::<T, S>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the `description` of this [`EnumMeta`] type.
|
/// Sets the `description` of this [`EnumMeta`] type.
|
||||||
///
|
///
|
||||||
/// Overwrites any previously set description.
|
/// Overwrites any previously set description.
|
||||||
|
@ -663,6 +757,21 @@ impl<'a, S> InputObjectMeta<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a new [`InputObjectMeta`] information with the specified `name`
|
||||||
|
/// and its `fields`.
|
||||||
|
pub fn new_reworked<T>(name: impl Into<Cow<'a, str>>, fields: &[Argument<'a, S>]) -> Self
|
||||||
|
where
|
||||||
|
T: resolve::InputValueOwned<S>,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
description: None,
|
||||||
|
input_fields: fields.to_vec(),
|
||||||
|
try_parse_fn: try_parse_fn_reworked::<T, S>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the `description` of this [`InputObjectMeta`] type.
|
/// Set the `description` of this [`InputObjectMeta`] type.
|
||||||
///
|
///
|
||||||
/// Overwrites any previously set description.
|
/// Overwrites any previously set description.
|
||||||
|
@ -811,3 +920,21 @@ where
|
||||||
.map(drop)
|
.map(drop)
|
||||||
.map_err(T::Error::into_field_error)
|
.map_err(T::Error::into_field_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_parse_fn_reworked<T, SV>(v: &InputValue<SV>) -> Result<(), FieldError<SV>>
|
||||||
|
where
|
||||||
|
T: resolve::InputValueOwned<SV>,
|
||||||
|
{
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
.map(drop)
|
||||||
|
.map_err(T::Error::into_field_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_parse_unsized_fn<T, SV>(v: &InputValue<SV>) -> Result<(), FieldError<SV>>
|
||||||
|
where
|
||||||
|
T: resolve::InputValueAsRef<SV> + ?Sized,
|
||||||
|
{
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
.map(drop)
|
||||||
|
.map_err(T::Error::into_field_error)
|
||||||
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ impl<'a, S: ScalarValue + 'a> SchemaType<'a, S> {
|
||||||
self.description.as_deref()
|
self.description.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn types(&self) -> Vec<TypeType<S>> {
|
fn types(&self) -> Vec<TypeType<'_, S>> {
|
||||||
self.type_list()
|
self.type_list()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|t| {
|
.filter(|t| {
|
||||||
|
@ -156,21 +156,21 @@ impl<'a, S: ScalarValue + 'a> SchemaType<'a, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql(name = "queryType")]
|
#[graphql(name = "queryType")]
|
||||||
fn query_type_(&self) -> TypeType<S> {
|
fn query_type_(&self) -> TypeType<'_, S> {
|
||||||
self.query_type()
|
self.query_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql(name = "mutationType")]
|
#[graphql(name = "mutationType")]
|
||||||
fn mutation_type_(&self) -> Option<TypeType<S>> {
|
fn mutation_type_(&self) -> Option<TypeType<'_, S>> {
|
||||||
self.mutation_type()
|
self.mutation_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql(name = "subscriptionType")]
|
#[graphql(name = "subscriptionType")]
|
||||||
fn subscription_type_(&self) -> Option<TypeType<S>> {
|
fn subscription_type_(&self) -> Option<TypeType<'_, S>> {
|
||||||
self.subscription_type()
|
self.subscription_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn directives(&self) -> Vec<&DirectiveType<S>> {
|
fn directives(&self) -> Vec<&DirectiveType<'_, S>> {
|
||||||
self.directive_list()
|
self.directive_list()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> {
|
||||||
fn fields(
|
fn fields(
|
||||||
&self,
|
&self,
|
||||||
#[graphql(default = false)] include_deprecated: Option<bool>,
|
#[graphql(default = false)] include_deprecated: Option<bool>,
|
||||||
) -> Option<Vec<&Field<S>>> {
|
) -> Option<Vec<&Field<'_, S>>> {
|
||||||
match self {
|
match self {
|
||||||
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. }))
|
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. }))
|
||||||
| TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some(
|
| TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some(
|
||||||
|
@ -231,14 +231,14 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn of_type(&self) -> Option<&TypeType<S>> {
|
fn of_type(&self) -> Option<&TypeType<'_, S>> {
|
||||||
match self {
|
match self {
|
||||||
TypeType::Concrete(_) => None,
|
TypeType::Concrete(_) => None,
|
||||||
TypeType::List(l, _) | TypeType::NonNull(l) => Some(&**l),
|
TypeType::List(l, _) | TypeType::NonNull(l) => Some(&**l),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_fields(&self) -> Option<&[Argument<S>]> {
|
fn input_fields(&self) -> Option<&[Argument<'_, S>]> {
|
||||||
match self {
|
match self {
|
||||||
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta {
|
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta {
|
||||||
ref input_fields,
|
ref input_fields,
|
||||||
|
@ -343,7 +343,7 @@ impl<'a, S: ScalarValue + 'a> Field<'a, S> {
|
||||||
self.description.as_deref()
|
self.description.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args(&self) -> Vec<&Argument<S>> {
|
fn args(&self) -> Vec<&Argument<'_, S>> {
|
||||||
self.arguments
|
self.arguments
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(Vec::new, |v| v.iter().collect())
|
.map_or_else(Vec::new, |v| v.iter().collect())
|
||||||
|
@ -434,7 +434,7 @@ impl<'a, S: ScalarValue + 'a> DirectiveType<'a, S> {
|
||||||
self.is_repeatable
|
self.is_repeatable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args(&self) -> &[Argument<S>] {
|
fn args(&self) -> &[Argument<'_, S>] {
|
||||||
&self.arguments
|
&self.arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphql_vars,
|
graphql_value, graphql_vars,
|
||||||
introspection::IntrospectionFormat,
|
introspection::IntrospectionFormat,
|
||||||
schema::model::RootNode,
|
schema::model::RootNode,
|
||||||
tests::fixtures::starwars::schema::{Database, Query},
|
tests::fixtures::starwars::schema::{Database, Query},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::value::Value;
|
use crate::graphql::{self, Value};
|
||||||
|
|
||||||
// Sort a nested schema Value.
|
// Sort a nested schema Value.
|
||||||
// In particular, lists are sorted by the "name" key of children, if present.
|
// In particular, lists are sorted by the "name" key of children, if present.
|
||||||
|
@ -34,7 +34,7 @@ pub(super) fn sort_schema_value(value: &mut Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn schema_introspection_result() -> Value {
|
pub(crate) fn schema_introspection_result() -> Value {
|
||||||
let mut v = graphql_value!({
|
let mut v = graphql::value!({
|
||||||
"__schema": {
|
"__schema": {
|
||||||
"description": null,
|
"description": null,
|
||||||
"queryType": {
|
"queryType": {
|
||||||
|
@ -1451,7 +1451,7 @@ pub(crate) fn schema_introspection_result() -> Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn schema_introspection_result_without_descriptions() -> Value {
|
pub(crate) fn schema_introspection_result_without_descriptions() -> Value {
|
||||||
let mut v = graphql_value!({
|
let mut v = graphql::value!({
|
||||||
"__schema": {
|
"__schema": {
|
||||||
"queryType": {
|
"queryType": {
|
||||||
"name": "Query"
|
"name": "Query"
|
||||||
|
|
305
juniper/src/types/arc.rs
Normal file
305
juniper/src/types/arc.rs
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
//! GraphQL implementation for [`Arc`].
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::TypeName<TI, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::ConcreteTypeName<TI, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc<Self>, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Arc<Self>, Self::Error> {
|
||||||
|
T::try_from_input_value(v).map(Arc::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Arc<Self>, Self::Error> {
|
||||||
|
T::try_from_implicit_null().map(Arc::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ScalarToken<SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Arc<T>, TI, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Arc<T>, TI, CX, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::Scalar<'i, TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Arc<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
448
juniper/src/types/array.rs
Normal file
448
juniper/src/types/array.rs
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
//! GraphQL implementation for [array].
|
||||||
|
//!
|
||||||
|
//! [array]: prim@array
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
behavior,
|
||||||
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
graphql, reflect, resolve,
|
||||||
|
schema::meta::MetaType,
|
||||||
|
BoxFuture, FieldError, FieldResult, IntoFieldError, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::iter;
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH, const N: usize> resolve::Type<TI, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.wrap_list::<behavior::Coerce<T, BH>, _>(type_info, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH, const N: usize> resolve::Value<TI, CX, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
iter::resolve_list(self.iter(), selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH, const N: usize> resolve::ValueAsync<TI, CX, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + Sync,
|
||||||
|
TI: Sync + ?Sized,
|
||||||
|
CX: Sync + ?Sized,
|
||||||
|
SV: Send + Sync,
|
||||||
|
BH: ?Sized + 'static, // TODO: Lift `'static` bound if possible.
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
Box::pin(iter::resolve_list_async(
|
||||||
|
self.iter(),
|
||||||
|
selection_set,
|
||||||
|
type_info,
|
||||||
|
executor,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH, const N: usize> resolve::Resolvable<SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH, const N: usize> resolve::ToInputValue<SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
graphql::InputValue::list(self.iter().map(T::to_input_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH, const N: usize> resolve::InputValue<'i, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = TryFromInputValueError<T::Error>;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
struct PartiallyInitializedArray<T, const N: usize> {
|
||||||
|
arr: [MaybeUninit<T>; N],
|
||||||
|
init_len: usize,
|
||||||
|
no_drop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Drop for PartiallyInitializedArray<T, N> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.no_drop {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Dropping a `MaybeUninit` does nothing, thus we need to drop
|
||||||
|
// the initialized elements manually, otherwise we may introduce
|
||||||
|
// a memory/resource leak if `T: Drop`.
|
||||||
|
for elem in &mut self.arr[0..self.init_len] {
|
||||||
|
// SAFETY: This is safe, because `self.init_len` represents
|
||||||
|
// the number of the initialized elements exactly.
|
||||||
|
unsafe {
|
||||||
|
ptr::drop_in_place(elem.as_mut_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match v {
|
||||||
|
graphql::InputValue::List(ls) => {
|
||||||
|
if ls.len() != N {
|
||||||
|
return Err(TryFromInputValueError::WrongCount {
|
||||||
|
actual: ls.len(),
|
||||||
|
expected: N,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if N == 0 {
|
||||||
|
// TODO: Use `mem::transmute` instead of
|
||||||
|
// `mem::transmute_copy` below, once it's allowed
|
||||||
|
// for const generics:
|
||||||
|
// https://github.com/rust-lang/rust/issues/61956
|
||||||
|
// SAFETY: `mem::transmute_copy` is safe here, because we
|
||||||
|
// check `N` to be `0`. It's no-op, actually.
|
||||||
|
return Ok(unsafe { mem::transmute_copy::<[T; 0], Self>(&[]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: The reason we're using a wrapper struct implementing
|
||||||
|
// `Drop` here is to be panic safe:
|
||||||
|
// `T: resolve::InputValue` implementation is not
|
||||||
|
// controlled by us, so calling
|
||||||
|
// `T::try_from_input_value(&i.item)` below may cause a
|
||||||
|
// panic when our array is initialized only partially.
|
||||||
|
// In such situation we need to drop already initialized
|
||||||
|
// values to avoid possible memory/resource leaks if
|
||||||
|
// `T: Drop`.
|
||||||
|
let mut out = PartiallyInitializedArray::<T, N> {
|
||||||
|
// SAFETY: The `.assume_init()` here is safe, because the
|
||||||
|
// type we are claiming to have initialized here is
|
||||||
|
// a bunch of `MaybeUninit`s, which do not require
|
||||||
|
// any initialization.
|
||||||
|
arr: unsafe { MaybeUninit::uninit().assume_init() },
|
||||||
|
init_len: 0,
|
||||||
|
no_drop: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut items = ls.iter().map(|i| T::try_from_input_value(&i.item));
|
||||||
|
for elem in &mut out.arr[..] {
|
||||||
|
if let Some(i) = items
|
||||||
|
.next()
|
||||||
|
.transpose()
|
||||||
|
.map_err(TryFromInputValueError::Item)?
|
||||||
|
{
|
||||||
|
*elem = MaybeUninit::new(i);
|
||||||
|
out.init_len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not drop collected `items`, because we're going to return
|
||||||
|
// them.
|
||||||
|
out.no_drop = true;
|
||||||
|
|
||||||
|
// TODO: Use `mem::transmute` instead of `mem::transmute_copy`
|
||||||
|
// below, once it's allowed for const generics:
|
||||||
|
// https://github.com/rust-lang/rust/issues/61956
|
||||||
|
// SAFETY: `mem::transmute_copy` is safe here, because we have
|
||||||
|
// exactly `N` initialized `items`.
|
||||||
|
// Also, despite `mem::transmute_copy` copies the value,
|
||||||
|
// we won't have a double-free when `T: Drop` here,
|
||||||
|
// because original array elements are `MaybeUninit`, so
|
||||||
|
// do nothing on `Drop`.
|
||||||
|
Ok(unsafe { mem::transmute_copy::<_, Self>(&out.arr) })
|
||||||
|
}
|
||||||
|
// See "Input Coercion" on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
graphql::InputValue::Null => Err(TryFromInputValueError::IsNull),
|
||||||
|
other => T::try_from_input_value(other)
|
||||||
|
.map_err(TryFromInputValueError::Item)
|
||||||
|
.and_then(|e: T| {
|
||||||
|
// TODO: Use `mem::transmute` instead of
|
||||||
|
// `mem::transmute_copy` below, once it's allowed
|
||||||
|
// for const generics:
|
||||||
|
// https://github.com/rust-lang/rust/issues/61956
|
||||||
|
if N == 1 {
|
||||||
|
// SAFETY: `mem::transmute_copy` is safe here, because
|
||||||
|
// we check `N` to be `1`. Also, despite
|
||||||
|
// `mem::transmute_copy` copies the value, we
|
||||||
|
// won't have a double-free when `T: Drop` here,
|
||||||
|
// because original `e: T` value is wrapped into
|
||||||
|
// `mem::ManuallyDrop`, so does nothing on
|
||||||
|
// `Drop`.
|
||||||
|
Ok(unsafe { mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)]) })
|
||||||
|
} else {
|
||||||
|
Err(TryFromInputValueError::WrongCount {
|
||||||
|
actual: 1,
|
||||||
|
expected: N,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH, const N: usize> graphql::InputType<'i, TI, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH, const N: usize> graphql::OutputType<TI, CX, SV, BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH, const N: usize> reflect::BaseType<BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH, const N: usize> reflect::BaseSubTypes<BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH, const N: usize> reflect::WrappedType<BH> for [T; N]
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible errors of converting a [`graphql::InputValue`] into an exact-size
|
||||||
|
/// [array](prim@array).
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum TryFromInputValueError<E> {
|
||||||
|
/// [`graphql::InputValue`] cannot be [`Null`].
|
||||||
|
///
|
||||||
|
/// See ["Combining List and Non-Null" section of spec][0].
|
||||||
|
///
|
||||||
|
/// [`Null`]: [`InputValue::Null`]
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
IsNull,
|
||||||
|
|
||||||
|
/// Wrong count of items.
|
||||||
|
WrongCount {
|
||||||
|
/// Actual count of items.
|
||||||
|
actual: usize,
|
||||||
|
|
||||||
|
/// Expected count of items ([array](prim@array) size).
|
||||||
|
expected: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Error of converting a [`graphql::InputValue::List`]'s item.
|
||||||
|
Item(E),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, SV> IntoFieldError<SV> for TryFromInputValueError<E>
|
||||||
|
where
|
||||||
|
E: IntoFieldError<SV>,
|
||||||
|
{
|
||||||
|
fn into_field_error(self) -> FieldError<SV> {
|
||||||
|
const ERROR_PREFIX: &str = "Failed to convert into exact-size array";
|
||||||
|
match self {
|
||||||
|
Self::IsNull => format!("{ERROR_PREFIX}: Value cannot be `null`").into(),
|
||||||
|
Self::WrongCount { actual, expected } => {
|
||||||
|
format!("{ERROR_PREFIX}: wrong elements count: {actual} instead of {expected}",)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Self::Item(s) => s.into_field_error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See "Input Coercion" examples on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-List.Input-Coercion
|
||||||
|
#[cfg(test)]
|
||||||
|
mod coercion {
|
||||||
|
use crate::{graphql, resolve::InputValue as _, IntoFieldError as _};
|
||||||
|
|
||||||
|
use super::TryFromInputValueError;
|
||||||
|
|
||||||
|
type V = graphql::InputValue;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_null() {
|
||||||
|
let v: V = graphql::input_value!(null);
|
||||||
|
assert_eq!(
|
||||||
|
<[i32; 0]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[i32; 1]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[Option<i32>; 0]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[Option<i32>; 1]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(<Option<[i32; 0]>>::try_from_input_value(&v), Ok(None));
|
||||||
|
assert_eq!(<Option<[i32; 1]>>::try_from_input_value(&v), Ok(None));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<i32>; 0]>>::try_from_input_value(&v),
|
||||||
|
Ok(None),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<i32>; 1]>>::try_from_input_value(&v),
|
||||||
|
Ok(None),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[[i32; 1]; 1]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<[Option<i32>; 1]>; 1]>>::try_from_input_value(&v),
|
||||||
|
Ok(None),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_value() {
|
||||||
|
let v: V = graphql::input_value!(1);
|
||||||
|
assert_eq!(<[i32; 1]>::try_from_input_value(&v), Ok([1]));
|
||||||
|
assert_eq!(
|
||||||
|
<[i32; 0]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::WrongCount {
|
||||||
|
expected: 0,
|
||||||
|
actual: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert_eq!(<[Option<i32>; 1]>::try_from_input_value(&v), Ok([Some(1)]));
|
||||||
|
assert_eq!(<Option<[i32; 1]>>::try_from_input_value(&v), Ok(Some([1])));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<i32>; 1]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some(1)])),
|
||||||
|
);
|
||||||
|
assert_eq!(<[[i32; 1]; 1]>::try_from_input_value(&v), Ok([[1]]));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<[Option<i32>; 1]>; 1]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some([Some(1)])])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_list() {
|
||||||
|
let v: V = graphql::input_value!([1, 2, 3]);
|
||||||
|
assert_eq!(<[i32; 3]>::try_from_input_value(&v), Ok([1, 2, 3]));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[i32; 3]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([1, 2, 3])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[Option<i32>; 3]>::try_from_input_value(&v),
|
||||||
|
Ok([Some(1), Some(2), Some(3)]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<i32>; 3]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some(1), Some(2), Some(3)])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[[i32; 1]; 3]>::try_from_input_value(&v),
|
||||||
|
Ok([[1], [2], [3]]),
|
||||||
|
);
|
||||||
|
// Looks like the spec ambiguity.
|
||||||
|
// See: https://github.com/graphql/graphql-spec/pull/515
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<[Option<i32>; 1]>; 3]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some([Some(1)]), Some([Some(2)]), Some([Some(3)])])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_list_with_null() {
|
||||||
|
let v: V = graphql::input_value!([1, 2, null]);
|
||||||
|
assert_eq!(
|
||||||
|
<[i32; 3]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(
|
||||||
|
"Expected `Int`, found: null".into_field_error(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[i32; 3]>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(
|
||||||
|
"Expected `Int`, found: null".into_field_error(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[Option<i32>; 3]>::try_from_input_value(&v),
|
||||||
|
Ok([Some(1), Some(2), None]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<i32>; 3]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some(1), Some(2), None])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<[[i32; 1]; 3]>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(TryFromInputValueError::IsNull)),
|
||||||
|
);
|
||||||
|
// Looks like the spec ambiguity.
|
||||||
|
// See: https://github.com/graphql/graphql-spec/pull/515
|
||||||
|
assert_eq!(
|
||||||
|
<Option<[Option<[Option<i32>; 1]>; 3]>>::try_from_input_value(&v),
|
||||||
|
Ok(Some([Some([Some(1)]), Some([Some(2)]), None])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
ast::{Directive, FromInputValue, InputValue, Selection},
|
ast::{Directive, FromInputValue, InputValue, Selection},
|
||||||
executor::{ExecutionResult, Executor, Registry, Variables},
|
executor::{ExecutionResult, Executor, Registry, Variables},
|
||||||
parser::Spanning,
|
parser::Spanning,
|
||||||
|
resolve,
|
||||||
schema::meta::{Argument, MetaType},
|
schema::meta::{Argument, MetaType},
|
||||||
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
||||||
FieldResult, GraphQLEnum, IntoFieldError,
|
FieldResult, GraphQLEnum, IntoFieldError,
|
||||||
|
@ -98,6 +99,8 @@ impl<'a, S> Arguments<'a, S> {
|
||||||
Self { args }
|
Self { args }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: DEPRECATED!
|
||||||
|
///
|
||||||
/// Gets an argument by the given `name` and converts it into the desired
|
/// Gets an argument by the given `name` and converts it into the desired
|
||||||
/// type.
|
/// type.
|
||||||
///
|
///
|
||||||
|
@ -121,6 +124,38 @@ impl<'a, S> Arguments<'a, S> {
|
||||||
.transpose()
|
.transpose()
|
||||||
.map_err(IntoFieldError::into_field_error)
|
.map_err(IntoFieldError::into_field_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves an argument with the provided `name` as the specified type `T`.
|
||||||
|
///
|
||||||
|
/// If [`None`] argument is found, then `T` is
|
||||||
|
/// [tried to be resolved from implicit `null`][0].
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the [`resolve::InputValue`] conversion fails.
|
||||||
|
///
|
||||||
|
/// [0]: resolve::InputValue::try_from_implicit_null
|
||||||
|
pub fn resolve<'s, T, BH>(&'s self, name: &str) -> FieldResult<T, S>
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'s, S, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
self.args
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|args| args.get(name))
|
||||||
|
.map(<T as resolve::InputValue<'s, S, BH>>::try_from_input_value)
|
||||||
|
.transpose()
|
||||||
|
.map_err(IntoFieldError::into_field_error)?
|
||||||
|
.map_or_else(
|
||||||
|
|| {
|
||||||
|
<T as resolve::InputValue<'s, S, BH>>::try_from_implicit_null().map_err(|e| {
|
||||||
|
IntoFieldError::into_field_error(e)
|
||||||
|
.map_message(|m| format!("Missing argument `{name}`: {m}"))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Ok,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Primary trait used to resolve GraphQL values.
|
/// Primary trait used to resolve GraphQL values.
|
||||||
|
|
303
juniper/src/types/box.rs
Normal file
303
juniper/src/types/box.rs
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
//! GraphQL implementation for [`Box`].
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::TypeName<TI, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::ConcreteTypeName<TI, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box<Self>, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Box<Self>, Self::Error> {
|
||||||
|
T::try_from_input_value(v).map(Box::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Box<Self>, Self::Error> {
|
||||||
|
T::try_from_implicit_null().map(Box::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ScalarToken<SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Box<T>, TI, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Box<T>, TI, CX, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::Scalar<'i, TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Box<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
|
@ -183,7 +183,7 @@ where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
fn to_input_value(&self) -> InputValue<S> {
|
fn to_input_value(&self) -> InputValue<S> {
|
||||||
InputValue::list(self.iter().map(T::to_input_value).collect())
|
InputValue::list(self.iter().map(T::to_input_value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
fn to_input_value(&self) -> InputValue<S> {
|
fn to_input_value(&self) -> InputValue<S> {
|
||||||
InputValue::list(self.iter().map(T::to_input_value).collect())
|
InputValue::list(self.iter().map(T::to_input_value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +481,7 @@ where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
fn to_input_value(&self) -> InputValue<S> {
|
fn to_input_value(&self) -> InputValue<S> {
|
||||||
InputValue::list(self.iter().map(T::to_input_value).collect())
|
InputValue::list(self.iter().map(T::to_input_value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
304
juniper/src/types/cow.rs
Normal file
304
juniper/src/types/cow.rs
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
//! GraphQL implementation for [`Cow`].
|
||||||
|
|
||||||
|
use std::{borrow::Cow, ops::Deref};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'me, T, TI, SV, BH> resolve::Type<TI, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::TypeName<TI, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::ConcreteTypeName<TI, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::Resolvable<SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ToInputValue<SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: resolve::InputValueAs<'i, Self, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, Cow<'me, Self>, SV, BH> for T
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: resolve::InputValueAsRef<SV, BH> + ToOwned + 'me,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
Cow<'me, Self>: Deref<Target = Self>,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Cow<'me, Self>, Self::Error> {
|
||||||
|
T::try_from_input_value(v).map(Cow::Borrowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Cow<'me, Self>, Self::Error> {
|
||||||
|
T::try_from_implicit_null().map(Cow::Borrowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ScalarToken<SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ToOwned + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseType<BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseSubTypes<BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::WrappedType<BH> for Cow<'me, T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ToOwned + ?Sized + 'me,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: Deref<Target = T>,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
72
juniper/src/types/iter.rs
Normal file
72
juniper/src/types/iter.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
//! GraphQL implementation for [`Iterator`].
|
||||||
|
|
||||||
|
use crate::{graphql, resolve, ExecutionResult, Executor, Selection};
|
||||||
|
|
||||||
|
pub fn resolve_list<'t, T, TI, CX, SV, BH, I>(
|
||||||
|
iter: I,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'t T> + ExactSizeIterator,
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized + 't,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
let is_non_null = executor
|
||||||
|
.current_type_reworked()
|
||||||
|
.list_contents()
|
||||||
|
.ok_or("Iterating over non-list type")?
|
||||||
|
.is_non_null();
|
||||||
|
|
||||||
|
let mut values = Vec::with_capacity(iter.len());
|
||||||
|
for v in iter {
|
||||||
|
let val = v.resolve_value(selection_set, type_info, executor)?;
|
||||||
|
if is_non_null && val.is_null() {
|
||||||
|
return Err("Resolved `null` on non-null type".into());
|
||||||
|
}
|
||||||
|
values.push(val);
|
||||||
|
}
|
||||||
|
Ok(graphql::Value::list(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_list_async<'t, 'r, T, TI, CX, SV, BH, I>(
|
||||||
|
iter: I,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<'r, '_, CX, SV>,
|
||||||
|
) -> ExecutionResult<SV>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'t T> + ExactSizeIterator,
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized + 't,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
use futures::stream::{FuturesOrdered, StreamExt as _};
|
||||||
|
|
||||||
|
let is_non_null = executor
|
||||||
|
.current_type_reworked()
|
||||||
|
.list_contents()
|
||||||
|
.ok_or("Iterating over non-list type")?
|
||||||
|
.is_non_null();
|
||||||
|
|
||||||
|
let mut futs = iter
|
||||||
|
.map(|v| async move {
|
||||||
|
v.resolve_value_async(selection_set, type_info, executor)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.collect::<FuturesOrdered<_>>();
|
||||||
|
|
||||||
|
let mut values = Vec::with_capacity(futs.len());
|
||||||
|
while let Some(res) = futs.next().await {
|
||||||
|
let val = res?;
|
||||||
|
if is_non_null && val.is_null() {
|
||||||
|
return Err("Resolved `null` on non-null type".into());
|
||||||
|
}
|
||||||
|
values.push(val);
|
||||||
|
}
|
||||||
|
Ok(graphql::Value::list(values))
|
||||||
|
}
|
|
@ -1,10 +1,27 @@
|
||||||
|
mod arc;
|
||||||
|
pub mod array;
|
||||||
|
mod r#box;
|
||||||
|
mod cow;
|
||||||
|
pub mod iter;
|
||||||
|
mod nullable;
|
||||||
|
mod option;
|
||||||
|
mod rc;
|
||||||
|
mod r#ref;
|
||||||
|
mod ref_mut;
|
||||||
|
mod result;
|
||||||
|
mod slice;
|
||||||
|
mod r#str;
|
||||||
|
pub mod vec;
|
||||||
|
|
||||||
pub mod async_await;
|
pub mod async_await;
|
||||||
pub mod base;
|
pub mod base;
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
pub mod marker;
|
pub mod marker;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
pub mod nullable;
|
|
||||||
pub mod pointers;
|
pub mod pointers;
|
||||||
pub mod scalars;
|
pub mod scalars;
|
||||||
pub mod subscriptions;
|
pub mod subscriptions;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use self::nullable::Nullable;
|
||||||
|
|
|
@ -1,42 +1,53 @@
|
||||||
|
//! GraphQL implementation for [`Nullable`].
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FromInputValue, InputValue, Selection, ToInputValue},
|
ast::{FromInputValue, InputValue, ToInputValue},
|
||||||
|
behavior,
|
||||||
executor::{ExecutionResult, Executor, Registry},
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
graphql, reflect, resolve,
|
||||||
schema::meta::MetaType,
|
schema::meta::MetaType,
|
||||||
types::{
|
types::{
|
||||||
async_await::GraphQLValueAsync,
|
async_await::GraphQLValueAsync,
|
||||||
base::{GraphQLType, GraphQLValue},
|
base::{GraphQLType, GraphQLValue},
|
||||||
marker::IsInputType,
|
marker::IsInputType,
|
||||||
},
|
},
|
||||||
value::{ScalarValue, Value},
|
BoxFuture, FieldResult, ScalarValue, Selection,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `Nullable` can be used in situations where you need to distinguish between an implicitly and
|
/// [`Nullable`] wrapper allowing to distinguish between an implicit and
|
||||||
/// explicitly null input value.
|
/// explicit `null` input value.
|
||||||
///
|
///
|
||||||
/// The GraphQL spec states that these two field calls are similar, but are not identical:
|
/// [GraphQL spec states][0] that these two field calls are similar, but are not
|
||||||
|
/// identical:
|
||||||
///
|
///
|
||||||
/// ```graphql
|
/// > ```graphql
|
||||||
/// {
|
/// > {
|
||||||
/// field(arg: null)
|
/// > field(arg: null)
|
||||||
/// field
|
/// > field
|
||||||
/// }
|
/// > }
|
||||||
/// ```
|
/// > ```
|
||||||
|
/// > The first has explicitly provided `null` to the argument "arg", while the
|
||||||
|
/// > second has implicitly not provided a value to the argument "arg". These
|
||||||
|
/// > two forms may be interpreted differently. For example, a mutation
|
||||||
|
/// > representing deleting a field vs not altering a field, respectively.
|
||||||
///
|
///
|
||||||
/// The first has explicitly provided null to the argument “arg”, while the second has implicitly
|
/// In cases where there is no need to distinguish between the two types of
|
||||||
/// not provided a value to the argument “arg”. These two forms may be interpreted differently. For
|
/// `null`, it's better to simply use [`Option`].
|
||||||
/// example, a mutation representing deleting a field vs not altering a field, respectively.
|
|
||||||
///
|
///
|
||||||
/// In cases where you do not need to be able to distinguish between the two types of null, you
|
/// [0]: https://spec.graphql.org/October2021#example-1c7eb
|
||||||
/// should simply use `Option<T>`.
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum Nullable<T> {
|
pub enum Nullable<T> {
|
||||||
/// No value
|
/// No value specified.
|
||||||
ImplicitNull,
|
ImplicitNull,
|
||||||
|
|
||||||
/// No value, explicitly specified to be null
|
/// Value explicitly specified to be `null`.
|
||||||
ExplicitNull,
|
ExplicitNull,
|
||||||
|
|
||||||
/// Some value `T`
|
/// Explicitly specified non-`null` value of `T`.
|
||||||
Some(T),
|
Some(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,101 +60,134 @@ impl<T> Default for Nullable<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Nullable<T> {
|
impl<T> Nullable<T> {
|
||||||
/// Returns `true` if the nullable is a `ExplicitNull` value.
|
/// Indicates whether this [`Nullable`] represents an [`ExplicitNull`].
|
||||||
|
///
|
||||||
|
/// [`ExplicitNull`]: Nullable::ExplicitNull
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_explicit_null(&self) -> bool {
|
pub fn is_explicit_null(&self) -> bool {
|
||||||
matches!(self, Self::ExplicitNull)
|
matches!(self, Self::ExplicitNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the nullable is a `ImplicitNull` value.
|
/// Indicates whether this [`Nullable`] represents an [`ImplicitNull`].
|
||||||
|
///
|
||||||
|
/// [`ImplicitNull`]: Nullable::ImplicitNull
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_implicit_null(&self) -> bool {
|
pub fn is_implicit_null(&self) -> bool {
|
||||||
matches!(self, Self::ImplicitNull)
|
matches!(self, Self::ImplicitNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the nullable is a `Some` value.
|
/// Indicates whether this [`Nullable`] contains a non-`null` value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_some(&self) -> bool {
|
pub fn is_some(&self) -> bool {
|
||||||
matches!(self, Self::Some(_))
|
matches!(self, Self::Some(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the nullable is not a `Some` value.
|
/// Indicates whether this [`Nullable`] represents a `null`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_null(&self) -> bool {
|
pub fn is_null(&self) -> bool {
|
||||||
!matches!(self, Self::Some(_))
|
!matches!(self, Self::Some(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts from `&mut Nullable<T>` to `Nullable<&mut T>`.
|
/// Converts from `&Nullable<T>` to `Nullable<&T>`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_mut(&mut self) -> Nullable<&mut T> {
|
pub fn as_ref(&self) -> Nullable<&T> {
|
||||||
match *self {
|
match self {
|
||||||
Self::Some(ref mut x) => Nullable::Some(x),
|
Self::Some(x) => Nullable::Some(x),
|
||||||
Self::ImplicitNull => Nullable::ImplicitNull,
|
Self::ImplicitNull => Nullable::ImplicitNull,
|
||||||
Self::ExplicitNull => Nullable::ExplicitNull,
|
Self::ExplicitNull => Nullable::ExplicitNull,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the contained `Some` value, consuming the `self` value.
|
/// Converts from `&mut Nullable<T>` to `Nullable<&mut T>`.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_mut(&mut self) -> Nullable<&mut T> {
|
||||||
|
match self {
|
||||||
|
Self::Some(x) => Nullable::Some(x),
|
||||||
|
Self::ImplicitNull => Nullable::ImplicitNull,
|
||||||
|
Self::ExplicitNull => Nullable::ExplicitNull,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the contained non-`null` value, consuming the `self` value.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the value is not a `Some` with a custom panic message provided by `msg`.
|
/// With a custom `msg` if this [`Nullable`] represents a `null`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn expect(self, msg: &str) -> T {
|
pub fn expect(self, msg: &str) -> T {
|
||||||
self.some().expect(msg)
|
self.some().expect(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the contained `Some` value or a provided default.
|
/// Returns the contained non-`null` value or the provided `default` one.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unwrap_or(self, default: T) -> T {
|
pub fn unwrap_or(self, default: T) -> T {
|
||||||
self.some().unwrap_or(default)
|
self.some().unwrap_or(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the contained `Some` value or computes it from a closure.
|
/// Returns thecontained non-`null` value or computes it from the provided
|
||||||
|
/// `func`tion.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
|
pub fn unwrap_or_else<F: FnOnce() -> T>(self, func: F) -> T {
|
||||||
self.some().unwrap_or_else(f)
|
self.some().unwrap_or_else(func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a `Nullable<T>` to `Nullable<U>` by applying a function to a contained value.
|
/// Returns the contained non-`null` value or the [`Default`] one.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Nullable<U> {
|
pub fn unwrap_or_default(self) -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.some().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps this `Nullable<T>` to `Nullable<U>` by applying the provided
|
||||||
|
/// `func`tion to the contained non-`null` value.
|
||||||
|
#[inline]
|
||||||
|
pub fn map<U, F: FnOnce(T) -> U>(self, func: F) -> Nullable<U> {
|
||||||
match self {
|
match self {
|
||||||
Self::Some(x) => Nullable::Some(f(x)),
|
Self::Some(x) => Nullable::Some(func(x)),
|
||||||
Self::ImplicitNull => Nullable::ImplicitNull,
|
Self::ImplicitNull => Nullable::ImplicitNull,
|
||||||
Self::ExplicitNull => Nullable::ExplicitNull,
|
Self::ExplicitNull => Nullable::ExplicitNull,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a function to the contained value (if any), or returns the provided default (if
|
/// Applies the provided `func`tion to the contained non-`null` value (if
|
||||||
/// not).
|
/// any), or returns the provided `default` value (if not).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
|
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, func: F) -> U {
|
||||||
self.some().map_or(default, f)
|
self.some().map_or(default, func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a function to the contained value (if any), or computes a default (if not).
|
/// Applies the provided `func`tion to the contained non-`null` value (if
|
||||||
|
/// any), or computes the provided `default` one (if not).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
|
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, func: F) -> U {
|
||||||
self.some().map_or_else(default, f)
|
self.some().map_or_else(default, func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms the `Nullable<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and
|
/// Transforms this `Nullable<T>` into a `Result<T, E>`, mapping `Some(v)`
|
||||||
/// `ImplicitNull` or `ExplicitNull` to `Err(err)`.
|
/// to `Ok(v)` and [`ImplicitNull`] or [`ExplicitNull`] to `Err(err)`.
|
||||||
|
///
|
||||||
|
/// [`ExplicitNull`]: Nullable::ExplicitNull
|
||||||
|
/// [`ImplicitNull`]: Nullable::ImplicitNull
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
|
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
|
||||||
self.some().ok_or(err)
|
self.some().ok_or(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms the `Nullable<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and
|
/// Transforms this `Nullable<T>` into a `Result<T, E>`, mapping `Some(v)`
|
||||||
/// `ImplicitNull` or `ExplicitNull` to `Err(err())`.
|
/// to `Ok(v)` and [`ImplicitNull`] or [`ExplicitNull`] to `Err(err())`.
|
||||||
|
///
|
||||||
|
/// [`ExplicitNull`]: Nullable::ExplicitNull
|
||||||
|
/// [`ImplicitNull`]: Nullable::ImplicitNull
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
|
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
|
||||||
self.some().ok_or_else(err)
|
self.some().ok_or_else(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the nullable if it contains a value, otherwise returns `b`.
|
/// Returns this [`Nullable`] if it contains a non-`null` value, otherwise
|
||||||
|
/// returns the specified `b` [`Nullable`] value.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn or(self, b: Self) -> Self {
|
pub fn or(self, b: Self) -> Self {
|
||||||
|
@ -153,35 +197,43 @@ impl<T> Nullable<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the nullable if it contains a value, otherwise calls `f` and
|
/// Returns this [`Nullable`] if it contains a non-`null` value, otherwise
|
||||||
/// returns the result.
|
/// computes a [`Nullable`] value from the specified `func`tion.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
|
pub fn or_else<F: FnOnce() -> Nullable<T>>(self, func: F) -> Nullable<T> {
|
||||||
match self {
|
match self {
|
||||||
Self::Some(_) => self,
|
Self::Some(_) => self,
|
||||||
_ => f(),
|
_ => func(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces the actual value in the nullable by the value given in parameter, returning the
|
/// Replaces the contained non-`null` value in this [`Nullable`] by the
|
||||||
/// old value if present, leaving a `Some` in its place without deinitializing either one.
|
/// provided `value`, returning the old one if present, leaving a [`Some`]
|
||||||
|
/// in its place without deinitializing either one.
|
||||||
|
///
|
||||||
|
/// [`Some`]: Nullable::Some
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn replace(&mut self, value: T) -> Self {
|
pub fn replace(&mut self, value: T) -> Self {
|
||||||
std::mem::replace(self, Self::Some(value))
|
mem::replace(self, Self::Some(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts from `Nullable<T>` to `Option<T>`.
|
/// Converts this [`Nullable`] to [Option].
|
||||||
|
#[inline]
|
||||||
pub fn some(self) -> Option<T> {
|
pub fn some(self) -> Option<T> {
|
||||||
match self {
|
match self {
|
||||||
Self::Some(v) => Some(v),
|
Self::Some(v) => Some(v),
|
||||||
_ => None,
|
Self::ExplicitNull | Self::ImplicitNull => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts from `Nullable<T>` to `Option<Option<T>>`, mapping `Some(v)` to `Some(Some(v))`,
|
/// Converts this [`Nullable`] to `Option<Option<T>>`, mapping `Some(v)` to
|
||||||
/// `ExplicitNull` to `Some(None)`, and `ImplicitNull` to `None`.
|
/// `Some(Some(v))`, [`ExplicitNull`] to `Some(None)`, and [`ImplicitNull`]
|
||||||
|
/// to [`None`].
|
||||||
|
///
|
||||||
|
/// [`ExplicitNull`]: Nullable::ExplicitNull
|
||||||
|
/// [`ImplicitNull`]: Nullable::ImplicitNull
|
||||||
pub fn explicit(self) -> Option<Option<T>> {
|
pub fn explicit(self) -> Option<Option<T>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Some(v) => Some(Some(v)),
|
Self::Some(v) => Some(Some(v)),
|
||||||
|
@ -192,33 +244,188 @@ impl<T> Nullable<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> Nullable<&T> {
|
impl<T: Copy> Nullable<&T> {
|
||||||
/// Maps a `Nullable<&T>` to a `Nullable<T>` by copying the contents of the nullable.
|
/// Maps this `Nullable<&T>` to a `Nullable<T>` by [`Copy`]ing the contents
|
||||||
|
/// of this [`Nullable`].
|
||||||
pub fn copied(self) -> Nullable<T> {
|
pub fn copied(self) -> Nullable<T> {
|
||||||
self.map(|&t| t)
|
self.map(|t| *t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> Nullable<&mut T> {
|
impl<T: Copy> Nullable<&mut T> {
|
||||||
/// Maps a `Nullable<&mut T>` to a `Nullable<T>` by copying the contents of the nullable.
|
/// Maps this `Nullable<&mut T>` to a `Nullable<T>` by [`Copy`]ing the
|
||||||
|
/// contents of this [`Nullable`].
|
||||||
pub fn copied(self) -> Nullable<T> {
|
pub fn copied(self) -> Nullable<T> {
|
||||||
self.map(|&mut t| t)
|
self.map(|t| *t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Nullable<&T> {
|
impl<T: Clone> Nullable<&T> {
|
||||||
/// Maps a `Nullable<&T>` to a `Nullable<T>` by cloning the contents of the nullable.
|
/// Maps this `Nullable<&T>` to a `Nullable<T>` by [`Clone`]ing the contents
|
||||||
|
/// of this [`Nullable`].
|
||||||
|
pub fn cloned(self) -> Nullable<T> {
|
||||||
|
self.map(T::clone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Nullable<&mut T> {
|
||||||
|
/// Maps this `Nullable<&mut T>` to a `Nullable<T>` by [`Clone`]ing the
|
||||||
|
/// contents of this [`Nullable`].
|
||||||
pub fn cloned(self) -> Nullable<T> {
|
pub fn cloned(self) -> Nullable<T> {
|
||||||
self.map(|t| t.clone())
|
self.map(|t| t.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Nullable<&mut T> {
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Nullable<T>
|
||||||
/// Maps a `Nullable<&mut T>` to a `Nullable<T>` by cloning the contents of the nullable.
|
where
|
||||||
pub fn cloned(self) -> Nullable<T> {
|
T: resolve::Type<TI, SV, BH>,
|
||||||
self.map(|t| t.clone())
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.wrap_nullable::<behavior::Coerce<T, BH>, _>(type_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
match self {
|
||||||
|
Self::Some(v) => v.resolve_value(selection_set, type_info, executor),
|
||||||
|
Self::ImplicitNull | Self::ExplicitNull => Ok(graphql::Value::Null),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: Send,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
match self {
|
||||||
|
Self::Some(v) => v.resolve_value_async(selection_set, type_info, executor),
|
||||||
|
Self::ImplicitNull | Self::ExplicitNull => Box::pin(future::ok(graphql::Value::Null)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
match self {
|
||||||
|
Self::Some(v) => v.to_input_value(),
|
||||||
|
Self::ImplicitNull | Self::ExplicitNull => graphql::InputValue::Null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
if v.is_null() {
|
||||||
|
Ok(Self::ExplicitNull)
|
||||||
|
} else {
|
||||||
|
T::try_from_input_value(v).map(Self::Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self::ImplicitNull)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Nullable<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
impl<S, T> GraphQLType<S> for Nullable<T>
|
impl<S, T> GraphQLType<S> for Nullable<T>
|
||||||
where
|
where
|
||||||
T: GraphQLType<S>,
|
T: GraphQLType<S>,
|
||||||
|
@ -256,7 +463,7 @@ where
|
||||||
) -> ExecutionResult<S> {
|
) -> ExecutionResult<S> {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Some(ref obj) => executor.resolve(info, obj),
|
Self::Some(ref obj) => executor.resolve(info, obj),
|
||||||
_ => Ok(Value::null()),
|
_ => Ok(graphql::Value::null()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,11 +480,11 @@ where
|
||||||
info: &'a Self::TypeInfo,
|
info: &'a Self::TypeInfo,
|
||||||
_: Option<&'a [Selection<S>]>,
|
_: Option<&'a [Selection<S>]>,
|
||||||
executor: &'a Executor<Self::Context, S>,
|
executor: &'a Executor<Self::Context, S>,
|
||||||
) -> crate::BoxFuture<'a, ExecutionResult<S>> {
|
) -> BoxFuture<'a, ExecutionResult<S>> {
|
||||||
let f = async move {
|
let f = async move {
|
||||||
let value = match self {
|
let value = match self {
|
||||||
Self::Some(obj) => executor.resolve_into_value_async(info, obj).await,
|
Self::Some(obj) => executor.resolve_into_value_async(info, obj).await,
|
||||||
_ => Value::null(),
|
_ => graphql::Value::null(),
|
||||||
};
|
};
|
||||||
Ok(value)
|
Ok(value)
|
||||||
};
|
};
|
||||||
|
|
175
juniper/src/types/option.rs
Normal file
175
juniper/src/types/option.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
//! GraphQL implementation for [`Option`].
|
||||||
|
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
behavior,
|
||||||
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
graphql, reflect, resolve,
|
||||||
|
schema::meta::MetaType,
|
||||||
|
BoxFuture, FieldResult, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.wrap_nullable::<behavior::Coerce<T, BH>, _>(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
match self {
|
||||||
|
Some(v) => v.resolve_value(selection_set, type_info, executor),
|
||||||
|
None => Ok(graphql::Value::Null),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: Send,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
match self {
|
||||||
|
Some(v) => v.resolve_value_async(selection_set, type_info, executor),
|
||||||
|
None => Box::pin(future::ok(graphql::Value::Null)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
match self {
|
||||||
|
Some(v) => v.to_input_value(),
|
||||||
|
None => graphql::InputValue::Null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
if v.is_null() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
T::try_from_input_value(v).map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Option<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod coercion {
|
||||||
|
use crate::{graphql, resolve::InputValue as _};
|
||||||
|
|
||||||
|
type V = graphql::InputValue;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_null() {
|
||||||
|
let v: V = graphql::input_value!(null);
|
||||||
|
assert_eq!(<Option<i32>>::try_from_input_value(&v), Ok(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_value() {
|
||||||
|
let v: V = graphql::input_value!(1);
|
||||||
|
assert_eq!(<Option<i32>>::try_from_input_value(&v), Ok(Some(1)));
|
||||||
|
}
|
||||||
|
}
|
305
juniper/src/types/rc.rs
Normal file
305
juniper/src/types/rc.rs
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
//! GraphQL implementation for [`Rc`].
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::TypeName<TI, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, BH> resolve::ConcreteTypeName<TI, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc<Self>, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Rc<Self>, Self::Error> {
|
||||||
|
T::try_from_input_value(v).map(Rc::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Rc<Self>, Self::Error> {
|
||||||
|
T::try_from_implicit_null().map(Rc::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ScalarToken<SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Rc<T>, TI, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Rc<T>, TI, CX, SV, BH> for T
|
||||||
|
where
|
||||||
|
T: graphql::Scalar<'i, TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Rc<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
284
juniper/src/types/ref.rs
Normal file
284
juniper/src/types/ref.rs
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
//! GraphQL implementation for [reference].
|
||||||
|
//!
|
||||||
|
//! [reference]: primitive@std::reference
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'me, T, TI, SV, BH> resolve::Type<TI, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::TypeName<TI, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::ConcreteTypeName<TI, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::Resolvable<SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ToInputValue<SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, &'me Self, SV, BH> for T
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: resolve::InputValueAsRef<SV, BH> + ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<&'me Self, Self::Error> {
|
||||||
|
T::try_from_input_value(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_implicit_null() -> Result<&'me Self, Self::Error> {
|
||||||
|
T::try_from_implicit_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ScalarToken<SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for &'me T
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized + 'me,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {
|
||||||
|
T::assert_scalar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseType<BH> for &'me T
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseSubTypes<BH> for &'me T
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::WrappedType<BH> for &'me T
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
221
juniper/src/types/ref_mut.rs
Normal file
221
juniper/src/types/ref_mut.rs
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
//! GraphQL implementation for mutable [reference].
|
||||||
|
//!
|
||||||
|
//! [reference]: primitive@std::reference
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, Registry,
|
||||||
|
Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'me, T, TI, SV, BH> resolve::Type<TI, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
T::meta(registry, type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::TypeName<TI, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::TypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn type_name(type_info: &TI) -> &str {
|
||||||
|
T::type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, BH> resolve::ConcreteTypeName<TI, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteTypeName<TI, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str {
|
||||||
|
(**self).concrete_type_name(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_value(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_value_async(selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::Resolvable<SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValue<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value(
|
||||||
|
&self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_concrete_value(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ConcreteValueAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_concrete_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
type_name: &str,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::Field<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::Field<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field(
|
||||||
|
&self,
|
||||||
|
field_name: &str,
|
||||||
|
arguments: &Arguments<SV>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
(**self).resolve_field(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::FieldAsync<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_field_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
field_name: &'r str,
|
||||||
|
arguments: &'r Arguments<SV>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
(**self).resolve_field_async(field_name, arguments, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ToInputValue<SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
(**self).to_input_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, SV, BH> resolve::ScalarToken<SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: resolve::ScalarToken<SV, BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
T::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH> + ?Sized,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseType<BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::BaseSubTypes<BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, T, BH> reflect::WrappedType<BH> for &'me mut T
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH> + ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
39
juniper/src/types/result.rs
Normal file
39
juniper/src/types/result.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//! GraphQL implementation for [`Result`].
|
||||||
|
|
||||||
|
use crate::{reflect, resolve, FieldResult, IntoFieldError};
|
||||||
|
|
||||||
|
impl<T, E, SV, BH> resolve::Resolvable<SV, BH> for Result<T, E>
|
||||||
|
where
|
||||||
|
E: IntoFieldError<SV>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self::Value, SV> {
|
||||||
|
self.map_err(IntoFieldError::into_field_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, BH> reflect::BaseType<BH> for Result<T, E>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, BH> reflect::BaseSubTypes<BH> for Result<T, E>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, BH> reflect::WrappedType<BH> for Result<T, E>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||||
|
}
|
235
juniper/src/types/slice.rs
Normal file
235
juniper/src/types/slice.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
//! GraphQL implementation for [slice].
|
||||||
|
//!
|
||||||
|
//! [slice]: prim@slice
|
||||||
|
|
||||||
|
use std::{borrow::Cow, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
behavior,
|
||||||
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
graphql, reflect, resolve,
|
||||||
|
schema::meta::MetaType,
|
||||||
|
BoxFuture, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{iter, vec::TryFromInputValueError};
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.wrap_list::<behavior::Coerce<T, BH>, _>(type_info, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
iter::resolve_list(self.iter(), selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + Sync,
|
||||||
|
TI: Sync + ?Sized,
|
||||||
|
CX: Sync + ?Sized,
|
||||||
|
SV: Send + Sync,
|
||||||
|
BH: ?Sized + 'static, // TODO: Lift `'static` bound if possible.
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
Box::pin(iter::resolve_list_async(
|
||||||
|
self.iter(),
|
||||||
|
selection_set,
|
||||||
|
type_info,
|
||||||
|
executor,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
graphql::InputValue::list(self.iter().map(T::to_input_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, Cow<'me, Self>, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
Vec<T>: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: ToOwned,
|
||||||
|
{
|
||||||
|
type Error = <Vec<T> as resolve::InputValue<'i, SV, BH>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Cow<'me, Self>, Self::Error> {
|
||||||
|
<Vec<T> as resolve::InputValue<'i, SV, BH>>::try_from_input_value(v)
|
||||||
|
.map(|v| Cow::Owned(v.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box<Self>, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
Vec<T>: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = <Vec<T> as resolve::InputValue<'i, SV, BH>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Box<Self>, Self::Error> {
|
||||||
|
<Vec<T> as resolve::InputValue<'i, SV, BH>>::try_from_input_value(v)
|
||||||
|
.map(Vec::into_boxed_slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc<Self>, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = TryFromInputValueError<T::Error>;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Rc<Self>, Self::Error> {
|
||||||
|
// We don't want to reuse `Vec<T>` implementation in the same way we do
|
||||||
|
// for `Box<[T]>`, because `impl From<Vec<T>> for Rc<[T]>` reallocates.
|
||||||
|
match v {
|
||||||
|
graphql::InputValue::List(l) => l
|
||||||
|
.iter()
|
||||||
|
.map(|i| T::try_from_input_value(&i.item).map_err(TryFromInputValueError::Item))
|
||||||
|
.collect(),
|
||||||
|
// See "Input Coercion" on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
graphql::InputValue::Null => Err(TryFromInputValueError::IsNull),
|
||||||
|
// TODO: Use `.into_iter()` after upgrade to 2021 Rust edition.
|
||||||
|
other => T::try_from_input_value(other)
|
||||||
|
.map(|e| std::iter::once(e).collect())
|
||||||
|
.map_err(TryFromInputValueError::Item),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc<Self>, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = TryFromInputValueError<T::Error>;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Arc<Self>, Self::Error> {
|
||||||
|
// We don't want to reuse `Vec<T>` implementation in the same way we do
|
||||||
|
// for `Box<[T]>`, because `impl From<Vec<T>> for Arc<[T]>` reallocates.
|
||||||
|
match v {
|
||||||
|
graphql::InputValue::List(l) => l
|
||||||
|
.iter()
|
||||||
|
.map(|i| T::try_from_input_value(&i.item).map_err(TryFromInputValueError::Item))
|
||||||
|
.collect(),
|
||||||
|
// See "Input Coercion" on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
graphql::InputValue::Null => Err(TryFromInputValueError::IsNull),
|
||||||
|
// TODO: Use `.into_iter()` after upgrade to 2021 Rust edition.
|
||||||
|
other => T::try_from_input_value(other)
|
||||||
|
.map(|e| std::iter::once(e).collect())
|
||||||
|
.map_err(TryFromInputValueError::Item),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Box<Self>, TI, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Rc<Self>, TI, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Arc<Self>, TI, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for [T]
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for [T]
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for [T]
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for [T]
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE);
|
||||||
|
}
|
273
juniper/src/types/str.rs
Normal file
273
juniper/src/types/str.rs
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
//! GraphQL implementation for [`str`].
|
||||||
|
//!
|
||||||
|
//! [`str`]: primitive@std::str
|
||||||
|
|
||||||
|
use std::{borrow::Cow, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
graphql,
|
||||||
|
meta::MetaType,
|
||||||
|
parser::{ParseError, ScalarToken},
|
||||||
|
reflect, resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<TI: ?Sized, SV: ScalarValue> resolve::Type<TI, SV> for str {
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.register_scalar_unsized::<Self, _>(type_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TI: ?Sized> resolve::TypeName<TI> for str {
|
||||||
|
fn type_name(_: &TI) -> &'static str {
|
||||||
|
<Self as reflect::BaseType>::NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TI, CX, SV> resolve::Value<TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: From<String>,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
_: Option<&[Selection<'_, SV>]>,
|
||||||
|
_: &TI,
|
||||||
|
_: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
// TODO: Remove redundant `.to_owned()` allocation by allowing
|
||||||
|
// `ScalarValue` creation from reference?
|
||||||
|
Ok(graphql::Value::scalar(self.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TI, CX, SV> resolve::ValueAsync<TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
SV: From<String> + Send,
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
_: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
_: &'r TI,
|
||||||
|
_: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
// TODO: Remove redundant `.to_owned()` allocation by allowing
|
||||||
|
// `ScalarValue` creation from reference?
|
||||||
|
Box::pin(future::ok(graphql::Value::scalar(self.to_owned())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SV> resolve::ToInputValue<SV> for str
|
||||||
|
where
|
||||||
|
SV: From<String>,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
// TODO: Remove redundant `.to_owned()` allocation by allowing
|
||||||
|
// `ScalarValue` creation from reference?
|
||||||
|
graphql::InputValue::scalar(self.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SV: ScalarValue> resolve::InputValueAsRef<SV> for str {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &graphql::InputValue<SV>) -> Result<&Self, Self::Error> {
|
||||||
|
v.as_string_value()
|
||||||
|
.ok_or_else(|| format!("Expected `String`, found: {v}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, SV> resolve::InputValueAs<'i, Cow<'me, Self>, SV> for str
|
||||||
|
where
|
||||||
|
'i: 'me,
|
||||||
|
SV: 'i,
|
||||||
|
Self: resolve::InputValueAsRef<SV>,
|
||||||
|
{
|
||||||
|
type Error = <Self as resolve::InputValueAsRef<SV>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Cow<'me, Self>, Self::Error> {
|
||||||
|
<Self as resolve::InputValueAsRef<SV>>::try_from_input_value(v).map(Cow::Borrowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, SV> resolve::InputValueAs<'i, Box<Self>, SV> for str
|
||||||
|
where
|
||||||
|
SV: 'i,
|
||||||
|
Self: resolve::InputValueAsRef<SV>,
|
||||||
|
{
|
||||||
|
type Error = <Self as resolve::InputValueAsRef<SV>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Box<Self>, Self::Error> {
|
||||||
|
<Self as resolve::InputValueAsRef<SV>>::try_from_input_value(v).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, SV> resolve::InputValueAs<'i, Rc<Self>, SV> for str
|
||||||
|
where
|
||||||
|
SV: 'i,
|
||||||
|
Self: resolve::InputValueAsRef<SV>,
|
||||||
|
{
|
||||||
|
type Error = <Self as resolve::InputValueAsRef<SV>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Rc<Self>, Self::Error> {
|
||||||
|
<Self as resolve::InputValueAsRef<SV>>::try_from_input_value(v).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, SV> resolve::InputValueAs<'i, Arc<Self>, SV> for str
|
||||||
|
where
|
||||||
|
SV: 'i,
|
||||||
|
Self: resolve::InputValueAsRef<SV>,
|
||||||
|
{
|
||||||
|
type Error = <Self as resolve::InputValueAsRef<SV>>::Error;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Arc<Self>, Self::Error> {
|
||||||
|
<Self as resolve::InputValueAsRef<SV>>::try_from_input_value(v).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SV> resolve::ScalarToken<SV> for str
|
||||||
|
where
|
||||||
|
String: resolve::ScalarToken<SV>,
|
||||||
|
{
|
||||||
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError> {
|
||||||
|
<String as resolve::ScalarToken<SV>>::parse_scalar_token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, TI, SV> graphql::InputTypeAs<'i, &'me Self, TI, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::Type<TI, SV>
|
||||||
|
+ resolve::ToInputValue<SV>
|
||||||
|
+ resolve::InputValueAs<'i, &'me Self, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, TI, SV> graphql::InputTypeAs<'i, Cow<'me, Self>, TI, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::Type<TI, SV>
|
||||||
|
+ resolve::ToInputValue<SV>
|
||||||
|
+ resolve::InputValueAs<'i, Cow<'me, Self>, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, SV> graphql::InputTypeAs<'i, Box<Self>, TI, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::Type<TI, SV>
|
||||||
|
+ resolve::ToInputValue<SV>
|
||||||
|
+ resolve::InputValueAs<'i, Box<Self>, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, SV> graphql::InputTypeAs<'i, Rc<Self>, TI, SV> for str
|
||||||
|
where
|
||||||
|
Self:
|
||||||
|
graphql::Type<TI, SV> + resolve::ToInputValue<SV> + resolve::InputValueAs<'i, Rc<Self>, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, SV> graphql::InputTypeAs<'i, Arc<Self>, TI, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::Type<TI, SV>
|
||||||
|
+ resolve::ToInputValue<SV>
|
||||||
|
+ resolve::InputValueAs<'i, Arc<Self>, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TI, CX, SV> graphql::OutputType<TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::Type<TI, SV> + resolve::Value<TI, CX, SV> + resolve::ValueAsync<TI, CX, SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, TI, CX, SV> graphql::ScalarAs<'i, &'me Self, TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::InputTypeAs<'i, &'me Self, TI, SV>
|
||||||
|
+ graphql::OutputType<TI, CX, SV>
|
||||||
|
+ resolve::ScalarToken<SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'me, 'i, TI, CX, SV> graphql::ScalarAs<'i, Cow<'me, Self>, TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::InputTypeAs<'i, Cow<'me, Self>, TI, SV>
|
||||||
|
+ graphql::OutputType<TI, CX, SV>
|
||||||
|
+ resolve::ScalarToken<SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Box<Self>, TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::InputTypeAs<'i, Box<Self>, TI, SV>
|
||||||
|
+ graphql::OutputType<TI, CX, SV>
|
||||||
|
+ resolve::ScalarToken<SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Rc<Self>, TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::InputTypeAs<'i, Rc<Self>, TI, SV>
|
||||||
|
+ graphql::OutputType<TI, CX, SV>
|
||||||
|
+ resolve::ScalarToken<SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Arc<Self>, TI, CX, SV> for str
|
||||||
|
where
|
||||||
|
Self: graphql::InputTypeAs<'i, Arc<Self>, TI, SV>
|
||||||
|
+ graphql::OutputType<TI, CX, SV>
|
||||||
|
+ resolve::ScalarToken<SV>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
{
|
||||||
|
fn assert_scalar() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl reflect::BaseType for str {
|
||||||
|
const NAME: reflect::Type = <String as reflect::BaseType>::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl reflect::BaseSubTypes for str {
|
||||||
|
const NAMES: reflect::Types = &[<Self as reflect::BaseType>::NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl reflect::WrappedType for str {
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::SINGULAR;
|
||||||
|
}
|
313
juniper/src/types/vec.rs
Normal file
313
juniper/src/types/vec.rs
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
//! GraphQL implementation for [`Vec`].
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
behavior,
|
||||||
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
graphql, reflect, resolve,
|
||||||
|
schema::meta::MetaType,
|
||||||
|
BoxFuture, FieldError, FieldResult, IntoFieldError, Selection,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::iter;
|
||||||
|
|
||||||
|
impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: resolve::Type<TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
|
||||||
|
where
|
||||||
|
SV: 'r,
|
||||||
|
{
|
||||||
|
registry.wrap_list::<behavior::Coerce<T, BH>, _>(type_info, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: resolve::Value<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
selection_set: Option<&[Selection<'_, SV>]>,
|
||||||
|
type_info: &TI,
|
||||||
|
executor: &Executor<CX, SV>,
|
||||||
|
) -> ExecutionResult<SV> {
|
||||||
|
iter::resolve_list(self.iter(), selection_set, type_info, executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: resolve::ValueAsync<TI, CX, SV, BH> + Sync,
|
||||||
|
TI: Sync + ?Sized,
|
||||||
|
CX: Sync + ?Sized,
|
||||||
|
SV: Send + Sync,
|
||||||
|
BH: ?Sized + 'static, // TODO: Lift `'static` bound if possible.
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'r>(
|
||||||
|
&'r self,
|
||||||
|
selection_set: Option<&'r [Selection<'_, SV>]>,
|
||||||
|
type_info: &'r TI,
|
||||||
|
executor: &'r Executor<CX, SV>,
|
||||||
|
) -> BoxFuture<'r, ExecutionResult<SV>> {
|
||||||
|
Box::pin(iter::resolve_list_async(
|
||||||
|
self.iter(),
|
||||||
|
selection_set,
|
||||||
|
type_info,
|
||||||
|
executor,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::Resolvable<SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> FieldResult<Self, SV> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: resolve::ToInputValue<SV, BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||||
|
graphql::InputValue::list(self.iter().map(T::to_input_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: resolve::InputValue<'i, SV, BH>,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
type Error = TryFromInputValueError<T::Error>;
|
||||||
|
|
||||||
|
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||||
|
match v {
|
||||||
|
graphql::InputValue::List(l) => l
|
||||||
|
.iter()
|
||||||
|
.map(|i| T::try_from_input_value(&i.item).map_err(TryFromInputValueError::Item))
|
||||||
|
.collect(),
|
||||||
|
// See "Input Coercion" on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
graphql::InputValue::Null => Err(TryFromInputValueError::IsNull),
|
||||||
|
other => T::try_from_input_value(other)
|
||||||
|
.map(|e| vec![e])
|
||||||
|
.map_err(TryFromInputValueError::Item),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: graphql::InputType<'i, TI, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
SV: 'i,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
T::assert_input_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: graphql::OutputType<TI, CX, SV, BH>,
|
||||||
|
TI: ?Sized,
|
||||||
|
CX: ?Sized,
|
||||||
|
BH: ?Sized,
|
||||||
|
Self: resolve::ValueAsync<TI, CX, SV, BH>,
|
||||||
|
{
|
||||||
|
fn assert_output_type() {
|
||||||
|
T::assert_output_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseType<BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAME: reflect::Type = T::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::BaseSubTypes<BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: reflect::BaseSubTypes<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const NAMES: reflect::Types = T::NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, BH> reflect::WrappedType<BH> for Vec<T>
|
||||||
|
where
|
||||||
|
T: reflect::WrappedType<BH>,
|
||||||
|
BH: ?Sized,
|
||||||
|
{
|
||||||
|
const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible errors of converting a [`graphql::InputValue`] into a [`Vec`].
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum TryFromInputValueError<E> {
|
||||||
|
/// [`graphql::InputValue`] cannot be [`Null`].
|
||||||
|
///
|
||||||
|
/// See ["Combining List and Non-Null" section of spec][0].
|
||||||
|
///
|
||||||
|
/// [`Null`]: [`InputValue::Null`]
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
|
||||||
|
IsNull,
|
||||||
|
|
||||||
|
/// Error of converting a [`graphql::InputValue::List`]'s item.
|
||||||
|
Item(E),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, SV> IntoFieldError<SV> for TryFromInputValueError<E>
|
||||||
|
where
|
||||||
|
E: IntoFieldError<SV>,
|
||||||
|
{
|
||||||
|
fn into_field_error(self) -> FieldError<SV> {
|
||||||
|
match self {
|
||||||
|
Self::IsNull => "Failed to convert into `Vec`: Value cannot be `null`".into(),
|
||||||
|
Self::Item(e) => e.into_field_error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See "Input Coercion" examples on List types:
|
||||||
|
// https://spec.graphql.org/October2021#sec-List.Input-Coercion
|
||||||
|
#[cfg(test)]
|
||||||
|
mod coercion {
|
||||||
|
use crate::{graphql, resolve::InputValue as _, IntoFieldError as _};
|
||||||
|
|
||||||
|
use super::TryFromInputValueError;
|
||||||
|
|
||||||
|
type V = graphql::InputValue;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_null() {
|
||||||
|
let v: V = graphql::input_value!(null);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<i32>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Option<i32>>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(<Option<Vec<i32>>>::try_from_input_value(&v), Ok(None));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<i32>>>>::try_from_input_value(&v),
|
||||||
|
Ok(None),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::IsNull),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<Vec<Option<i32>>>>>>::try_from_input_value(&v),
|
||||||
|
Ok(None),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_value() {
|
||||||
|
let v: V = graphql::input_value!(1);
|
||||||
|
assert_eq!(<Vec<i32>>::try_from_input_value(&v), Ok(vec![1]));
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Option<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(vec![Some(1)]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![1])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<i32>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![Some(1)])),
|
||||||
|
);
|
||||||
|
assert_eq!(<Vec<Vec<i32>>>::try_from_input_value(&v), Ok(vec![vec![1]]));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<Vec<Option<i32>>>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![Some(vec![Some(1)])])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_list() {
|
||||||
|
let v: V = graphql::input_value!([1, 2, 3]);
|
||||||
|
assert_eq!(<Vec<i32>>::try_from_input_value(&v), Ok(vec![1, 2, 3]));
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![1, 2, 3])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Option<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(vec![Some(1), Some(2), Some(3)]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<i32>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![Some(1), Some(2), Some(3)])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(vec![vec![1], vec![2], vec![3]]),
|
||||||
|
);
|
||||||
|
// Looks like the spec ambiguity.
|
||||||
|
// See: https://github.com/graphql/graphql-spec/pull/515
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<Vec<Option<i32>>>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![
|
||||||
|
Some(vec![Some(1)]),
|
||||||
|
Some(vec![Some(2)]),
|
||||||
|
Some(vec![Some(3)]),
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_list_with_null() {
|
||||||
|
let v: V = graphql::input_value!([1, 2, null]);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<i32>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(
|
||||||
|
"Expected `Int`, found: null".into_field_error(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(
|
||||||
|
"Expected `Int`, found: null".into_field_error(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Option<i32>>>::try_from_input_value(&v),
|
||||||
|
Ok(vec![Some(1), Some(2), None]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<i32>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![Some(1), Some(2), None])),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
<Vec<Vec<i32>>>::try_from_input_value(&v),
|
||||||
|
Err(TryFromInputValueError::Item(TryFromInputValueError::IsNull)),
|
||||||
|
);
|
||||||
|
// Looks like the spec ambiguity.
|
||||||
|
// See: https://github.com/graphql/graphql-spec/pull/515
|
||||||
|
assert_eq!(
|
||||||
|
<Option<Vec<Option<Vec<Option<i32>>>>>>::try_from_input_value(&v),
|
||||||
|
Ok(Some(vec![Some(vec![Some(1)]), Some(vec![Some(2)]), None])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use std::{any::TypeId, borrow::Cow, fmt, mem};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{InputValue, ToInputValue},
|
ast::{InputValue, ToInputValue},
|
||||||
parser::Spanning,
|
parser::Spanning,
|
||||||
|
resolve,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -190,6 +191,27 @@ impl<S: Clone> ToInputValue<S> for Value<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: Clone> resolve::ToInputValue<S> for Value<S> {
|
||||||
|
fn to_input_value(&self) -> InputValue<S> {
|
||||||
|
// TODO: Simplify recursive calls syntax, once old `ToInputValue` trait
|
||||||
|
// is removed.
|
||||||
|
match self {
|
||||||
|
Self::Null => InputValue::Null,
|
||||||
|
Self::Scalar(s) => InputValue::Scalar(s.clone()),
|
||||||
|
Self::List(l) => InputValue::list(
|
||||||
|
l.iter()
|
||||||
|
.map(<Self as resolve::ToInputValue<S>>::to_input_value),
|
||||||
|
),
|
||||||
|
Self::Object(o) => InputValue::object(o.iter().map(|(k, v)| {
|
||||||
|
(
|
||||||
|
k.clone(),
|
||||||
|
<Self as resolve::ToInputValue<S>>::to_input_value(v),
|
||||||
|
)
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: ScalarValue> fmt::Display for Value<S> {
|
impl<S: ScalarValue> fmt::Display for Value<S> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -31,5 +31,5 @@ url = "2.0"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
derive_more = "0.99.7"
|
derive_more = "0.99.7"
|
||||||
futures = "0.3.22"
|
futures = "0.3.22"
|
||||||
juniper = { path = "../juniper" }
|
#juniper = { path = "../juniper" }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
|
60
juniper_codegen/src/common/behavior.rs
Normal file
60
juniper_codegen/src/common/behavior.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//! Common functions, definitions and extensions for parsing and code generation
|
||||||
|
//! related to [`Behaviour`] type parameter.
|
||||||
|
//!
|
||||||
|
//! [`Behaviour`]: juniper::behavior
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
parse_quote,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::common::SpanContainer;
|
||||||
|
|
||||||
|
/// [`Behaviour`] parametrization of the code generation.
|
||||||
|
///
|
||||||
|
/// [`Behaviour`]: juniper::behavior
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub(crate) enum Type {
|
||||||
|
/// [`behavior::Standard`] should be used in the generated code.
|
||||||
|
///
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
#[default]
|
||||||
|
Standard,
|
||||||
|
|
||||||
|
/// Concrete custom Rust type should be used as [`Behaviour`] in the
|
||||||
|
/// generated code.
|
||||||
|
///
|
||||||
|
/// [`Behaviour`]: juniper::behavior
|
||||||
|
Custom(syn::Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Type {
|
||||||
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
|
input.parse::<syn::Type>().map(Self::Custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Type {
|
||||||
|
fn to_tokens(&self, into: &mut TokenStream) {
|
||||||
|
self.ty().to_tokens(into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
/// Returns a Rust type representing this [`Type`].
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn ty(&self) -> syn::Type {
|
||||||
|
match self {
|
||||||
|
Self::Standard => parse_quote! { ::juniper::behavior::Standard },
|
||||||
|
Self::Custom(ty) => ty.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<SpanContainer<Self>>> for Type {
|
||||||
|
fn from(attr: Option<SpanContainer<Self>>) -> Self {
|
||||||
|
attr.map(SpanContainer::into_inner).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
default, diagnostic, filter_attrs,
|
behavior, default, diagnostic, filter_attrs,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _, TypeExt as _,
|
ParseBufferExt as _, TypeExt as _,
|
||||||
|
@ -52,6 +52,19 @@ pub(crate) struct Attr {
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Required-Arguments
|
/// [2]: https://spec.graphql.org/October2021#sec-Required-Arguments
|
||||||
pub(crate) default: Option<SpanContainer<default::Value>>,
|
pub(crate) default: Option<SpanContainer<default::Value>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] this
|
||||||
|
/// [GraphQL argument][1] implementation is parametrized with, to [coerce]
|
||||||
|
/// in the generated code from.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified marker indicating that this method argument doesn't
|
/// Explicitly specified marker indicating that this method argument doesn't
|
||||||
/// represent a [GraphQL argument][1], but is a [`Context`] being injected
|
/// represent a [GraphQL argument][1], but is a [`Context`] being injected
|
||||||
/// into a [GraphQL field][2] resolving function.
|
/// into a [GraphQL field][2] resolving function.
|
||||||
|
@ -103,6 +116,13 @@ impl Parse for Attr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(val.span()), val))
|
.replace(SpanContainer::new(ident.span(), Some(val.span()), val))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"ctx" | "context" | "Context" => {
|
"ctx" | "context" | "Context" => {
|
||||||
let span = ident.span();
|
let span = ident.span();
|
||||||
out.context
|
out.context
|
||||||
|
@ -133,6 +153,7 @@ impl Attr {
|
||||||
name: try_merge_opt!(name: self, another),
|
name: try_merge_opt!(name: self, another),
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
default: try_merge_opt!(default: self, another),
|
default: try_merge_opt!(default: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
executor: try_merge_opt!(executor: self, another),
|
executor: try_merge_opt!(executor: self, another),
|
||||||
})
|
})
|
||||||
|
@ -229,6 +250,14 @@ pub(crate) struct OnField {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Required-Arguments
|
/// [2]: https://spec.graphql.org/October2021#sec-Required-Arguments
|
||||||
pub(crate) default: Option<default::Value>,
|
pub(crate) default: Option<default::Value>,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization of this [GraphQL field argument][1]
|
||||||
|
/// implementation to [coerce] from in the generated code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
pub(crate) behavior: behavior::Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible kinds of Rust method arguments for code generation.
|
/// Possible kinds of Rust method arguments for code generation.
|
||||||
|
@ -433,6 +462,7 @@ impl OnMethod {
|
||||||
ty: argument.ty.as_ref().clone(),
|
ty: argument.ty.as_ref().clone(),
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
default: attr.default.map(SpanContainer::into_inner),
|
default: attr.default.map(SpanContainer::into_inner),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
deprecation, filter_attrs,
|
behavior, deprecation, filter_attrs,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _,
|
ParseBufferExt as _,
|
||||||
|
@ -56,6 +56,19 @@ pub(crate) struct Attr {
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Deprecation
|
/// [2]: https://spec.graphql.org/October2021#sec-Deprecation
|
||||||
pub(crate) deprecated: Option<SpanContainer<deprecation::Directive>>,
|
pub(crate) deprecated: Option<SpanContainer<deprecation::Directive>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] this
|
||||||
|
/// [GraphQL field][1] implementation is parametrized with, to [coerce] in
|
||||||
|
/// the generated code from.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified marker indicating that this method (or struct
|
/// Explicitly specified marker indicating that this method (or struct
|
||||||
/// field) should be omitted by code generation and not considered as the
|
/// field) should be omitted by code generation and not considered as the
|
||||||
/// [GraphQL field][1] definition.
|
/// [GraphQL field][1] definition.
|
||||||
|
@ -94,6 +107,13 @@ impl Parse for Attr {
|
||||||
))
|
))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"ignore" | "skip" => out
|
"ignore" | "skip" => out
|
||||||
.ignore
|
.ignore
|
||||||
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
||||||
|
@ -116,6 +136,7 @@ impl Attr {
|
||||||
name: try_merge_opt!(name: self, another),
|
name: try_merge_opt!(name: self, another),
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
deprecated: try_merge_opt!(deprecated: self, another),
|
deprecated: try_merge_opt!(deprecated: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
ignore: try_merge_opt!(ignore: self, another),
|
ignore: try_merge_opt!(ignore: self, another),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -184,6 +205,14 @@ pub(crate) struct Definition {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
pub(crate) ident: syn::Ident,
|
pub(crate) ident: syn::Ident,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization of this [GraphQL field][1] implementation
|
||||||
|
/// to [coerce] from in the generated code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
pub(crate) behavior: behavior::Type,
|
||||||
|
|
||||||
/// Rust [`MethodArgument`]s required to call the method representing this
|
/// Rust [`MethodArgument`]s required to call the method representing this
|
||||||
/// [GraphQL field][1].
|
/// [GraphQL field][1].
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,7 +1,81 @@
|
||||||
//! Common code generated parts, used by this crate.
|
//! Common code generated parts, used by this crate.
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::parse_quote;
|
||||||
|
|
||||||
|
use crate::common::behavior;
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Resolvable`] trait for the
|
||||||
|
/// provided [`syn::Type`] with its [`syn::Generics`].
|
||||||
|
///
|
||||||
|
/// [`resolve::Resolvable`]: juniper::resolve::Resolvable
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
pub(crate) fn impl_resolvable(
|
||||||
|
bh: &behavior::Type,
|
||||||
|
(ty, generics): (syn::Type, syn::Generics),
|
||||||
|
) -> TokenStream {
|
||||||
|
let (sv, generics) = mix_scalar_value(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Resolvable<#sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn into_value(self) -> ::juniper::FieldResult<Self, #sv> {
|
||||||
|
::juniper::FieldResult::Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mixes a type info [`syn::GenericParam`] into the provided [`syn::Generics`]
|
||||||
|
/// and returns its [`syn::Ident`].
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn mix_type_info(mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||||
|
let ty = parse_quote! { __TypeInfo };
|
||||||
|
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mixes a context [`syn::GenericParam`] into the provided [`syn::Generics`]
|
||||||
|
/// and returns its [`syn::Ident`].
|
||||||
|
pub(crate) fn mix_context(mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||||
|
let ty = parse_quote! { __Context };
|
||||||
|
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mixes a [`ScalarValue`] [`syn::GenericParam`] into the provided
|
||||||
|
/// [`syn::Generics`] and returns it.
|
||||||
|
///
|
||||||
|
/// [`ScalarValue`]: juniper::ScalarValue
|
||||||
|
pub(crate) fn mix_scalar_value(mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||||
|
let sv = parse_quote! { __ScalarValue };
|
||||||
|
generics.params.push(parse_quote! { #sv });
|
||||||
|
(sv, generics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the provided
|
||||||
|
/// [`syn::Generics`] and returns it.
|
||||||
|
///
|
||||||
|
/// [`InputValue`]: juniper::resolve::InputValue
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn mix_input_lifetime(
|
||||||
|
mut generics: syn::Generics,
|
||||||
|
sv: impl ToTokens,
|
||||||
|
) -> (syn::GenericParam, syn::Generics) {
|
||||||
|
let lt: syn::GenericParam = parse_quote! { '__inp };
|
||||||
|
generics.params.push(lt.clone());
|
||||||
|
generics
|
||||||
|
.make_where_clause()
|
||||||
|
.predicates
|
||||||
|
.push(parse_quote! { #sv: #lt });
|
||||||
|
(lt, generics)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate the code resolving some [GraphQL type][1] in a synchronous manner.
|
/// Generate the code resolving some [GraphQL type][1] in a synchronous manner.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Common functions, definitions and extensions for code generation, used by this crate.
|
//! Common functions, definitions and extensions for code generation, used by this crate.
|
||||||
|
|
||||||
|
pub(crate) mod behavior;
|
||||||
pub(crate) mod default;
|
pub(crate) mod default;
|
||||||
pub(crate) mod deprecation;
|
pub(crate) mod deprecation;
|
||||||
mod description;
|
mod description;
|
||||||
|
|
|
@ -105,15 +105,37 @@ pub(crate) trait TypeExt {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn unreferenced(&self) -> &Self;
|
fn unreferenced(&self) -> &Self;
|
||||||
|
|
||||||
/// Iterates mutably over all the lifetime parameters of this [`syn::Type`]
|
/// Iterates mutably over all the lifetime parameters (even elided) of this
|
||||||
/// with the given `func`tion.
|
/// [`syn::Type`] with the given `func`tion.
|
||||||
fn lifetimes_iter_mut<F: FnMut(&mut syn::Lifetime)>(&mut self, func: &mut F);
|
fn lifetimes_iter_mut<F: FnMut(MaybeElidedLifetimeMut<'_>)>(&mut self, func: &mut F);
|
||||||
|
|
||||||
|
/// Iterates mutably over all the named lifetime parameters (no elided) of
|
||||||
|
/// this [`syn::Type`] with the given `func`tion.
|
||||||
|
fn named_lifetimes_iter_mut<F: FnMut(&mut syn::Lifetime)>(&mut self, func: &mut F) {
|
||||||
|
self.lifetimes_iter_mut(&mut |lt| {
|
||||||
|
if let MaybeElidedLifetimeMut::Named(lt) = lt {
|
||||||
|
func(lt);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Anonymizes all the lifetime parameters of this [`syn::Type`] (except
|
/// Anonymizes all the lifetime parameters of this [`syn::Type`] (except
|
||||||
/// the `'static` ones), making it suitable for using in contexts with
|
/// the `'static` ones), making it suitable for using in contexts with
|
||||||
/// inferring.
|
/// inferring.
|
||||||
fn lifetimes_anonymized(&mut self);
|
fn lifetimes_anonymized(&mut self);
|
||||||
|
|
||||||
|
/// Anonymizes all the lifetime parameters of this [`syn::Type`] (except
|
||||||
|
/// the `'static` ones), making it suitable for using in contexts with
|
||||||
|
/// inferring, and returns it as a new type, leaving the original one
|
||||||
|
/// unchanged.
|
||||||
|
fn to_anonymized_lifetimes(&self) -> syn::Type;
|
||||||
|
|
||||||
|
/// Lifts all the lifetimes of this [`syn::Type`] (even elided, but except
|
||||||
|
/// `'static`) to a `for<>` quantifier, preserving the type speciality as
|
||||||
|
/// much as possible, and returns it as a new type, leaving the original one
|
||||||
|
/// unchanged.
|
||||||
|
fn to_hrtb_lifetimes(&self) -> (Option<syn::BoundLifetimes>, syn::Type);
|
||||||
|
|
||||||
/// Returns the topmost [`syn::Ident`] of this [`syn::TypePath`], if any.
|
/// Returns the topmost [`syn::Ident`] of this [`syn::TypePath`], if any.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn topmost_ident(&self) -> Option<&syn::Ident>;
|
fn topmost_ident(&self) -> Option<&syn::Ident>;
|
||||||
|
@ -135,16 +157,16 @@ impl TypeExt for syn::Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lifetimes_iter_mut<F: FnMut(&mut syn::Lifetime)>(&mut self, func: &mut F) {
|
fn lifetimes_iter_mut<F: FnMut(MaybeElidedLifetimeMut<'_>)>(&mut self, func: &mut F) {
|
||||||
use syn::{GenericArgument as GA, Type as T};
|
use syn::{GenericArgument as GA, Type as T};
|
||||||
|
|
||||||
fn iter_path<F: FnMut(&mut syn::Lifetime)>(path: &mut syn::Path, func: &mut F) {
|
fn iter_path<F: FnMut(MaybeElidedLifetimeMut<'_>)>(path: &mut syn::Path, func: &mut F) {
|
||||||
for seg in path.segments.iter_mut() {
|
for seg in path.segments.iter_mut() {
|
||||||
match &mut seg.arguments {
|
match &mut seg.arguments {
|
||||||
syn::PathArguments::AngleBracketed(angle) => {
|
syn::PathArguments::AngleBracketed(angle) => {
|
||||||
for arg in angle.args.iter_mut() {
|
for arg in angle.args.iter_mut() {
|
||||||
match arg {
|
match arg {
|
||||||
GA::Lifetime(lt) => func(lt),
|
GA::Lifetime(lt) => func(lt.into()),
|
||||||
GA::Type(ty) => ty.lifetimes_iter_mut(func),
|
GA::Type(ty) => ty.lifetimes_iter_mut(func),
|
||||||
GA::Binding(b) => b.ty.lifetimes_iter_mut(func),
|
GA::Binding(b) => b.ty.lifetimes_iter_mut(func),
|
||||||
GA::Constraint(_) | GA::Const(_) => {}
|
GA::Constraint(_) | GA::Const(_) => {}
|
||||||
|
@ -181,7 +203,7 @@ impl TypeExt for syn::Type {
|
||||||
| T::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
|
| T::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
|
||||||
for bound in bounds.iter_mut() {
|
for bound in bounds.iter_mut() {
|
||||||
match bound {
|
match bound {
|
||||||
syn::TypeParamBound::Lifetime(lt) => func(lt),
|
syn::TypeParamBound::Lifetime(lt) => func(lt.into()),
|
||||||
syn::TypeParamBound::Trait(bound) => {
|
syn::TypeParamBound::Trait(bound) => {
|
||||||
if bound.lifetimes.is_some() {
|
if bound.lifetimes.is_some() {
|
||||||
todo!("Iterating over HRTB lifetimes in trait is not yet supported")
|
todo!("Iterating over HRTB lifetimes in trait is not yet supported")
|
||||||
|
@ -193,9 +215,7 @@ impl TypeExt for syn::Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
T::Reference(ref_ty) => {
|
T::Reference(ref_ty) => {
|
||||||
if let Some(lt) = ref_ty.lifetime.as_mut() {
|
func((&mut ref_ty.lifetime).into());
|
||||||
func(lt)
|
|
||||||
}
|
|
||||||
(*ref_ty.elem).lifetimes_iter_mut(func)
|
(*ref_ty.elem).lifetimes_iter_mut(func)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,13 +233,48 @@ impl TypeExt for syn::Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lifetimes_anonymized(&mut self) {
|
fn lifetimes_anonymized(&mut self) {
|
||||||
self.lifetimes_iter_mut(&mut |lt| {
|
self.named_lifetimes_iter_mut(&mut |lt| {
|
||||||
if lt.ident != "_" && lt.ident != "static" {
|
if lt.ident != "_" && lt.ident != "static" {
|
||||||
lt.ident = syn::Ident::new("_", Span::call_site());
|
lt.ident = syn::Ident::new("_", Span::call_site());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_anonymized_lifetimes(&self) -> syn::Type {
|
||||||
|
let mut ty = self.clone();
|
||||||
|
ty.lifetimes_anonymized();
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_hrtb_lifetimes(&self) -> (Option<syn::BoundLifetimes>, syn::Type) {
|
||||||
|
let mut ty = self.clone();
|
||||||
|
let mut lts = vec![];
|
||||||
|
|
||||||
|
let anon_ident = &syn::Ident::new("_", Span::call_site());
|
||||||
|
|
||||||
|
ty.lifetimes_iter_mut(&mut |mut lt_mut| {
|
||||||
|
let ident = match <_mut {
|
||||||
|
MaybeElidedLifetimeMut::Elided(v) => {
|
||||||
|
v.as_ref().map(|l| &l.ident).unwrap_or(anon_ident)
|
||||||
|
}
|
||||||
|
MaybeElidedLifetimeMut::Named(l) => &l.ident,
|
||||||
|
};
|
||||||
|
if ident != "static" {
|
||||||
|
let new_lt = syn::Lifetime::new(&format!("'__fa_f_{ident}"), Span::call_site());
|
||||||
|
if !lts.contains(&new_lt) {
|
||||||
|
lts.push(new_lt.clone());
|
||||||
|
}
|
||||||
|
lt_mut.set(new_lt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let for_ = (!lts.is_empty()).then(|| {
|
||||||
|
parse_quote! {
|
||||||
|
for< #( #lts ),* >}
|
||||||
|
});
|
||||||
|
(for_, ty)
|
||||||
|
}
|
||||||
|
|
||||||
fn topmost_ident(&self) -> Option<&syn::Ident> {
|
fn topmost_ident(&self) -> Option<&syn::Ident> {
|
||||||
match self.unparenthesized() {
|
match self.unparenthesized() {
|
||||||
syn::Type::Path(p) => Some(&p.path),
|
syn::Type::Path(p) => Some(&p.path),
|
||||||
|
@ -239,6 +294,38 @@ impl TypeExt for syn::Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mutable reference to a place that may containing a [`syn::Lifetime`].
|
||||||
|
pub(crate) enum MaybeElidedLifetimeMut<'a> {
|
||||||
|
/// [`syn::Lifetime`] may be elided.
|
||||||
|
Elided(&'a mut Option<syn::Lifetime>),
|
||||||
|
|
||||||
|
/// [`syn::Lifetime`] is always present.
|
||||||
|
Named(&'a mut syn::Lifetime),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MaybeElidedLifetimeMut<'a> {
|
||||||
|
/// Assigns the provided [`syn::Lifetime`] to the place pointed by this
|
||||||
|
/// [`MaybeElidedLifetimeMut`] reference.
|
||||||
|
fn set(&mut self, lt: syn::Lifetime) {
|
||||||
|
match self {
|
||||||
|
Self::Elided(v) => **v = Some(lt),
|
||||||
|
Self::Named(l) => **l = lt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut Option<syn::Lifetime>> for MaybeElidedLifetimeMut<'a> {
|
||||||
|
fn from(lt: &'a mut Option<syn::Lifetime>) -> Self {
|
||||||
|
Self::Elided(lt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut syn::Lifetime> for MaybeElidedLifetimeMut<'a> {
|
||||||
|
fn from(lt: &'a mut syn::Lifetime) -> Self {
|
||||||
|
Self::Named(lt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extension of [`syn::Generics`] providing common function widely used by this crate for parsing.
|
/// Extension of [`syn::Generics`] providing common function widely used by this crate for parsing.
|
||||||
pub(crate) trait GenericsExt {
|
pub(crate) trait GenericsExt {
|
||||||
/// Removes all default types out of type parameters and const parameters in these
|
/// Removes all default types out of type parameters and const parameters in these
|
||||||
|
@ -354,3 +441,43 @@ impl<'a> VisitMut for ReplaceWithDefaults<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_type_ext_to_hrtb_lifetimes {
|
||||||
|
use quote::quote;
|
||||||
|
use syn::parse_quote;
|
||||||
|
|
||||||
|
use super::TypeExt as _;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
for (input, expected) in [
|
||||||
|
(parse_quote! { &'static T }, quote! { &'static T }),
|
||||||
|
(parse_quote! { &T }, quote! { for<'__fa_f__>: &'__fa_f__ T }),
|
||||||
|
(
|
||||||
|
parse_quote! { &'a mut T },
|
||||||
|
quote! { for<'__fa_f_a>: &'__fa_f_a mut T },
|
||||||
|
),
|
||||||
|
(
|
||||||
|
parse_quote! { &str },
|
||||||
|
quote! { for<'__fa_f__>: &'__fa_f__ str },
|
||||||
|
),
|
||||||
|
(
|
||||||
|
parse_quote! { &Cow<'static, str> },
|
||||||
|
quote! { for<'__fa_f__>: &'__fa_f__ Cow<'static, str> },
|
||||||
|
),
|
||||||
|
(
|
||||||
|
parse_quote! { &Cow<'a, str> },
|
||||||
|
quote! { for<'__fa_f__, '__fa_f_a>: &'__fa_f__ Cow<'__fa_f_a, str> },
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let (actual_for, actual_ty) = syn::Type::to_hrtb_lifetimes(&input);
|
||||||
|
let actual_for = actual_for.map(|for_| quote! { #for_: });
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
quote! { #actual_for #actual_ty }.to_string(),
|
||||||
|
expected.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -63,15 +63,17 @@ pub(crate) enum Type {
|
||||||
/// [`ScalarValue`]: juniper::ScalarValue
|
/// [`ScalarValue`]: juniper::ScalarValue
|
||||||
Concrete(syn::Type),
|
Concrete(syn::Type),
|
||||||
|
|
||||||
/// One of type parameters of the original type is specified as [`ScalarValue`].
|
/// One of type parameters of the original type is specified as
|
||||||
|
/// [`ScalarValue`].
|
||||||
///
|
///
|
||||||
/// The original type is the type that the code is generated for.
|
/// The original type is the type that the code is generated for.
|
||||||
///
|
///
|
||||||
/// [`ScalarValue`]: juniper::ScalarValue
|
/// [`ScalarValue`]: juniper::ScalarValue
|
||||||
ExplicitGeneric(syn::Ident),
|
ExplicitGeneric(syn::Ident),
|
||||||
|
|
||||||
/// [`ScalarValue`] parametrization is assumed to be generic and is not specified
|
/// [`ScalarValue`] parametrization is assumed to be generic and is not
|
||||||
/// explicitly, or specified as bound predicate (like `S: ScalarValue + Send + Sync`).
|
/// specified explicitly, or specified as bound predicate (like
|
||||||
|
/// `S: ScalarValue + Send + Sync`).
|
||||||
///
|
///
|
||||||
/// [`ScalarValue`]: juniper::ScalarValue
|
/// [`ScalarValue`]: juniper::ScalarValue
|
||||||
ImplicitGeneric(Option<syn::PredicateType>),
|
ImplicitGeneric(Option<syn::PredicateType>),
|
||||||
|
|
|
@ -87,6 +87,7 @@ pub(crate) fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
values,
|
values,
|
||||||
has_ignored_variants,
|
has_ignored_variants,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
deprecation, filter_attrs,
|
behavior, deprecation, filter_attrs, gen,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _,
|
ParseBufferExt as _,
|
||||||
|
@ -65,6 +65,17 @@ struct ContainerAttr {
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||||
|
/// [GraphQL enum][0] implementation with.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified [`rename::Policy`] for all [values][1] of this
|
/// Explicitly specified [`rename::Policy`] for all [values][1] of this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -118,6 +129,13 @@ impl Parse for ContainerAttr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"rename_all" => {
|
"rename_all" => {
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
let val = input.parse::<syn::LitStr>()?;
|
let val = input.parse::<syn::LitStr>()?;
|
||||||
|
@ -151,6 +169,7 @@ impl ContainerAttr {
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
scalar: try_merge_opt!(scalar: self, another),
|
scalar: try_merge_opt!(scalar: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
rename_values: try_merge_opt!(rename_values: self, another),
|
rename_values: try_merge_opt!(rename_values: self, another),
|
||||||
is_internal: self.is_internal || another.is_internal,
|
is_internal: self.is_internal || another.is_internal,
|
||||||
})
|
})
|
||||||
|
@ -364,6 +383,13 @@ struct Definition {
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
scalar: scalar::Type,
|
scalar: scalar::Type,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization to generate code with for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// [Values][1] of this [GraphQL enum][0].
|
/// [Values][1] of this [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
@ -386,6 +412,18 @@ impl ToTokens for Definition {
|
||||||
self.impl_from_input_value_tokens().to_tokens(into);
|
self.impl_from_input_value_tokens().to_tokens(into);
|
||||||
self.impl_to_input_value_tokens().to_tokens(into);
|
self.impl_to_input_value_tokens().to_tokens(into);
|
||||||
self.impl_reflection_traits_tokens().to_tokens(into);
|
self.impl_reflection_traits_tokens().to_tokens(into);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
self.impl_resolve_type().to_tokens(into);
|
||||||
|
self.impl_resolve_type_name().to_tokens(into);
|
||||||
|
self.impl_resolve_value().to_tokens(into);
|
||||||
|
self.impl_resolve_value_async().to_tokens(into);
|
||||||
|
gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into);
|
||||||
|
self.impl_resolve_to_input_value().to_tokens(into);
|
||||||
|
self.impl_resolve_input_value().to_tokens(into);
|
||||||
|
self.impl_graphql_input_type().to_tokens(into);
|
||||||
|
self.impl_graphql_output_type().to_tokens(into);
|
||||||
|
self.impl_graphql_enum().to_tokens(into);
|
||||||
|
self.impl_reflect().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +454,98 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`graphql::InputType`] trait for
|
||||||
|
/// this [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`graphql::InputType`]: juniper::graphql::InputType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
#[must_use]
|
||||||
|
fn impl_graphql_input_type(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::resolve::Type<#inf, #sv, #bh>
|
||||||
|
+ ::juniper::resolve::ToInputValue<#sv, #bh>
|
||||||
|
+ ::juniper::resolve::InputValue<#lt, #sv, #bh>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn assert_input_type() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`graphql::OutputType`] trait for
|
||||||
|
/// this [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`graphql::OutputType`]: juniper::graphql::OutputType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
#[must_use]
|
||||||
|
fn impl_graphql_output_type(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::resolve::Type<#inf, #sv, #bh>
|
||||||
|
+ ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
+ ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn assert_output_type() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`graphql::Enum`] trait for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`graphql::Enum`]: juniper::graphql::Enum
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
#[must_use]
|
||||||
|
fn impl_graphql_enum(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>
|
||||||
|
+ ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::graphql::Enum<#lt, #inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn assert_enum() {
|
||||||
|
<Self as ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>>
|
||||||
|
::assert_input_type();
|
||||||
|
<Self as ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh>>
|
||||||
|
::assert_output_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLType`] trait for this
|
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -470,6 +600,86 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Type`] trait for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Type`]: juniper::resolve::Type
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_type(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
let preds = &mut generics.make_where_clause().predicates;
|
||||||
|
preds.push(parse_quote! { #sv: Clone });
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
::juniper::behavior::Coerce<Self>:
|
||||||
|
::juniper::resolve::TypeName<#inf>
|
||||||
|
+ ::juniper::resolve::InputValueOwned<#sv>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let description = &self.description;
|
||||||
|
|
||||||
|
let values_meta = self.values.iter().map(|v| {
|
||||||
|
let v_name = &v.name;
|
||||||
|
let v_description = &v.description;
|
||||||
|
let v_deprecation = &v.deprecated;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
::juniper::meta::EnumValue::new(#v_name)
|
||||||
|
#v_description
|
||||||
|
#v_deprecation
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn meta<'__r, '__ti: '__r>(
|
||||||
|
registry: &mut ::juniper::Registry<'__r, #sv>,
|
||||||
|
type_info: &'__ti #inf,
|
||||||
|
) -> ::juniper::meta::MetaType<'__r, #sv>
|
||||||
|
where
|
||||||
|
#sv: '__r,
|
||||||
|
{
|
||||||
|
let values = [#( #values_meta ),*];
|
||||||
|
|
||||||
|
registry.register_enum_with::<
|
||||||
|
::juniper::behavior::Coerce<Self>, _,
|
||||||
|
>(&values, type_info, |meta| {
|
||||||
|
meta #description
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::TypeName`] trait for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_type_name(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn type_name(_: &#inf) -> &'static str {
|
||||||
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -528,6 +738,64 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Value`] trait for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Value`]: juniper::resolve::Value
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#sv: From<String>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let variant_arms = self.values.iter().map(|v| {
|
||||||
|
let v_ident = &v.ident;
|
||||||
|
let v_name = &v.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#v_ident => ::std::result::Result::Ok(
|
||||||
|
::juniper::Value::<#sv>::scalar(
|
||||||
|
::std::string::String::from(#v_name),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let ignored_arm = self.has_ignored_variants.then(|| {
|
||||||
|
let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
_ => ::std::result::Result::Err(
|
||||||
|
::juniper::FieldError::<#sv>::from(#err_msg),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
_: Option<&[::juniper::Selection<'_, #sv>]>,
|
||||||
|
_: &#inf,
|
||||||
|
_: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::ExecutionResult<#sv> {
|
||||||
|
match self {
|
||||||
|
#( #variant_arms )*
|
||||||
|
#ignored_arm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -559,6 +827,48 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::ValueAsync`] trait for
|
||||||
|
/// this [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::ValueAsync`]: juniper::resolve::ValueAsync
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_value_async(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
let preds = &mut generics.make_where_clause().predicates;
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
Self: ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
});
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
#sv: Send
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn resolve_value_async<'__r>(
|
||||||
|
&'__r self,
|
||||||
|
sel_set: Option<&'__r [::juniper::Selection<'_, #sv>]>,
|
||||||
|
type_info: &'__r #inf,
|
||||||
|
executor: &'__r ::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::BoxFuture<
|
||||||
|
'__r, ::juniper::ExecutionResult<#sv>,
|
||||||
|
> {
|
||||||
|
let v =
|
||||||
|
<Self as ::juniper::resolve::Value<#inf, #cx, #sv, #bh>>
|
||||||
|
::resolve_value(self, sel_set, type_info, executor);
|
||||||
|
::std::boxed::Box::pin(::juniper::futures::future::ready(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`FromInputValue`] trait for this
|
/// Returns generated code implementing [`FromInputValue`] trait for this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -598,6 +908,55 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::InputValue`] trait for
|
||||||
|
/// this [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_input_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#sv: ::juniper::ScalarValue
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let variant_arms = self.values.iter().map(|v| {
|
||||||
|
let v_ident = &v.ident;
|
||||||
|
let v_name = &v.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
::std::option::Option::Some(#v_name) =>
|
||||||
|
::std::result::Result::Ok(Self::#v_ident),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
type Error = ::std::string::String;
|
||||||
|
|
||||||
|
fn try_from_input_value(
|
||||||
|
input: &#lt ::juniper::graphql::InputValue<#sv>,
|
||||||
|
) -> ::std::result::Result<Self, Self::Error> {
|
||||||
|
match input
|
||||||
|
.as_enum_value()
|
||||||
|
.or_else(|| input.as_string_value())
|
||||||
|
{
|
||||||
|
#( #variant_arms )*
|
||||||
|
_ => ::std::result::Result::Err(
|
||||||
|
::std::format!("Unknown enum value: {}", input),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`ToInputValue`] trait for this
|
/// Returns generated code implementing [`ToInputValue`] trait for this
|
||||||
/// [GraphQL enum][0].
|
/// [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -643,6 +1002,53 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::ToInputValue`] trait for
|
||||||
|
/// this [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_resolve_to_input_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#sv: From<String>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let variant_arms = self.values.iter().map(|v| {
|
||||||
|
let v_ident = &v.ident;
|
||||||
|
let v_name = &v.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#v_ident => ::juniper::graphql::InputValue::<#sv>::scalar(
|
||||||
|
::std::string::String::from(#v_name),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let ignored_arm = self.has_ignored_variants.then(|| {
|
||||||
|
let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
_ => ::std::panic!(#err_msg),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> {
|
||||||
|
match self {
|
||||||
|
#( #variant_arms )*
|
||||||
|
#ignored_arm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and
|
/// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and
|
||||||
/// [`WrappedType`] traits for this [GraphQL enum][0].
|
/// [`WrappedType`] traits for this [GraphQL enum][0].
|
||||||
///
|
///
|
||||||
|
@ -684,6 +1090,47 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
|
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||||
|
/// [GraphQL enum][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||||
|
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||||
|
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Enums
|
||||||
|
fn impl_reflect(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAME: ::juniper::reflect::Type = #name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types =
|
||||||
|
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::WrappedType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const VALUE: ::juniper::reflect::WrappedValue =
|
||||||
|
::juniper::reflect::wrap::SINGULAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
|
/// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
|
||||||
/// similar) implementation of this enum.
|
/// similar) implementation of this enum.
|
||||||
///
|
///
|
||||||
|
@ -742,4 +1189,16 @@ impl Definition {
|
||||||
|
|
||||||
generics
|
generics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
|
let generics = self.generics.clone();
|
||||||
|
let ty = {
|
||||||
|
let ident = &self.ident;
|
||||||
|
let (_, ty_gen, _) = generics.split_for_impl();
|
||||||
|
parse_quote! { #ident #ty_gen }
|
||||||
|
};
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,13 +95,13 @@ fn parse_field(
|
||||||
renaming: rename::Policy,
|
renaming: rename::Policy,
|
||||||
is_internal: bool,
|
is_internal: bool,
|
||||||
) -> Option<FieldDefinition> {
|
) -> Option<FieldDefinition> {
|
||||||
let field_attr = FieldAttr::from_attrs("graphql", &f.attrs)
|
let attr = FieldAttr::from_attrs("graphql", &f.attrs)
|
||||||
.map_err(|e| proc_macro_error::emit_error!(e))
|
.map_err(|e| proc_macro_error::emit_error!(e))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let ident = f.ident.as_ref().or_else(|| err_unnamed_field(f))?;
|
let ident = f.ident.as_ref().or_else(|| err_unnamed_field(f))?;
|
||||||
|
|
||||||
let name = field_attr
|
let name = attr
|
||||||
.name
|
.name
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|| renaming.apply(&ident.unraw().to_string()),
|
|| renaming.apply(&ident.unraw().to_string()),
|
||||||
|
@ -114,10 +115,11 @@ fn parse_field(
|
||||||
Some(FieldDefinition {
|
Some(FieldDefinition {
|
||||||
ident: ident.clone(),
|
ident: ident.clone(),
|
||||||
ty: f.ty.clone(),
|
ty: f.ty.clone(),
|
||||||
default: field_attr.default.map(SpanContainer::into_inner),
|
default: attr.default.map(SpanContainer::into_inner),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
name,
|
name,
|
||||||
description: field_attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
ignored: field_attr.ignore.is_some(),
|
ignored: attr.ignore.is_some(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
default, filter_attrs,
|
behavior, default, filter_attrs, gen,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _,
|
ParseBufferExt as _,
|
||||||
|
@ -66,6 +66,17 @@ struct ContainerAttr {
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||||
|
/// [GraphQL input object][0] implementation with.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified [`rename::Policy`] for all fields of this
|
/// Explicitly specified [`rename::Policy`] for all fields of this
|
||||||
/// [GraphQL input object][0].
|
/// [GraphQL input object][0].
|
||||||
///
|
///
|
||||||
|
@ -118,6 +129,13 @@ impl Parse for ContainerAttr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"rename_all" => {
|
"rename_all" => {
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
let val = input.parse::<syn::LitStr>()?;
|
let val = input.parse::<syn::LitStr>()?;
|
||||||
|
@ -151,6 +169,7 @@ impl ContainerAttr {
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
scalar: try_merge_opt!(scalar: self, another),
|
scalar: try_merge_opt!(scalar: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
rename_fields: try_merge_opt!(rename_fields: self, another),
|
rename_fields: try_merge_opt!(rename_fields: self, another),
|
||||||
is_internal: self.is_internal || another.is_internal,
|
is_internal: self.is_internal || another.is_internal,
|
||||||
})
|
})
|
||||||
|
@ -185,6 +204,16 @@ struct FieldAttr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||||
name: Option<SpanContainer<String>>,
|
name: Option<SpanContainer<String>>,
|
||||||
|
|
||||||
|
/// Explicitly specified [description][2] of this
|
||||||
|
/// [GraphQL input object field][1].
|
||||||
|
///
|
||||||
|
/// If [`None`], then Rust doc comment will be used as the [description][2],
|
||||||
|
/// if any.
|
||||||
|
///
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||||
|
/// [2]: https://spec.graphql.org/October2021#sec-Descriptions
|
||||||
|
description: Option<SpanContainer<Description>>,
|
||||||
|
|
||||||
/// Explicitly specified [default value][2] of this
|
/// Explicitly specified [default value][2] of this
|
||||||
/// [GraphQL input object field][1] to be used used in case a field value is
|
/// [GraphQL input object field][1] to be used used in case a field value is
|
||||||
/// not provided.
|
/// not provided.
|
||||||
|
@ -195,15 +224,18 @@ struct FieldAttr {
|
||||||
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
||||||
default: Option<SpanContainer<default::Value>>,
|
default: Option<SpanContainer<default::Value>>,
|
||||||
|
|
||||||
/// Explicitly specified [description][2] of this
|
/// Explicitly specified type of the custom [`Behavior`] this
|
||||||
/// [GraphQL input object field][1].
|
/// [GraphQL input object field][1] implementation is parametrized with, to
|
||||||
|
/// [coerce] in the generated code from.
|
||||||
///
|
///
|
||||||
/// If [`None`], then Rust doc comment will be used as the [description][2],
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
/// if any.
|
/// code.
|
||||||
///
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||||
/// [2]: https://spec.graphql.org/October2021#sec-Descriptions
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
description: Option<SpanContainer<Description>>,
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified marker for the Rust struct field to be ignored and
|
/// Explicitly specified marker for the Rust struct field to be ignored and
|
||||||
/// not included into the code generated for a [GraphQL input object][0]
|
/// not included into the code generated for a [GraphQL input object][0]
|
||||||
|
@ -234,17 +266,24 @@ impl Parse for FieldAttr {
|
||||||
))
|
))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"desc" | "description" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let desc = input.parse::<Description>()?;
|
||||||
|
out.description
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(desc.span()), desc))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"default" => {
|
"default" => {
|
||||||
let val = input.parse::<default::Value>()?;
|
let val = input.parse::<default::Value>()?;
|
||||||
out.default
|
out.default
|
||||||
.replace(SpanContainer::new(ident.span(), Some(val.span()), val))
|
.replace(SpanContainer::new(ident.span(), Some(val.span()), val))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
"desc" | "description" => {
|
"behave" | "behavior" => {
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
let desc = input.parse::<Description>()?;
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
out.description
|
out.behavior
|
||||||
.replace(SpanContainer::new(ident.span(), Some(desc.span()), desc))
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
"ignore" | "skip" => out
|
"ignore" | "skip" => out
|
||||||
|
@ -267,8 +306,9 @@ impl FieldAttr {
|
||||||
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: try_merge_opt!(name: self, another),
|
name: try_merge_opt!(name: self, another),
|
||||||
default: try_merge_opt!(default: self, another),
|
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
|
default: try_merge_opt!(default: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
ignore: try_merge_opt!(ignore: self, another),
|
ignore: try_merge_opt!(ignore: self, another),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -314,6 +354,14 @@ struct FieldDefinition {
|
||||||
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
||||||
default: Option<default::Value>,
|
default: Option<default::Value>,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization of this [GraphQL input object field][1]
|
||||||
|
/// implementation to [coerce] from in the generated code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// Name of this [GraphQL input object field][1] in GraphQL schema.
|
/// Name of this [GraphQL input object field][1] in GraphQL schema.
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||||
|
@ -338,6 +386,14 @@ struct FieldDefinition {
|
||||||
ignored: bool,
|
ignored: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FieldDefinition {
|
||||||
|
/// Indicates whether this [`FieldDefinition`] uses [`Default::default()`]
|
||||||
|
/// ans its [`FieldDefinition::default`] value.
|
||||||
|
fn needs_default_trait_bound(&self) -> bool {
|
||||||
|
matches!(self.default, Some(default::Value::Default))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Representation of [GraphQL input object][0] for code generation.
|
/// Representation of [GraphQL input object][0] for code generation.
|
||||||
///
|
///
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
@ -383,6 +439,13 @@ struct Definition {
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
scalar: scalar::Type,
|
scalar: scalar::Type,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization to generate code with for this
|
||||||
|
/// [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// [Fields][1] of this [GraphQL input object][0].
|
/// [Fields][1] of this [GraphQL input object][0].
|
||||||
///
|
///
|
||||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
@ -399,6 +462,14 @@ impl ToTokens for Definition {
|
||||||
self.impl_from_input_value_tokens().to_tokens(into);
|
self.impl_from_input_value_tokens().to_tokens(into);
|
||||||
self.impl_to_input_value_tokens().to_tokens(into);
|
self.impl_to_input_value_tokens().to_tokens(into);
|
||||||
self.impl_reflection_traits_tokens().to_tokens(into);
|
self.impl_reflection_traits_tokens().to_tokens(into);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
self.impl_resolve_type().to_tokens(into);
|
||||||
|
self.impl_resolve_type_name().to_tokens(into);
|
||||||
|
self.impl_resolve_to_input_value().to_tokens(into);
|
||||||
|
self.impl_resolve_input_value().to_tokens(into);
|
||||||
|
self.impl_graphql_input_type().to_tokens(into);
|
||||||
|
self.impl_graphql_input_object().to_tokens(into);
|
||||||
|
self.impl_reflect().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,6 +511,88 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`graphql::InputType`] trait for
|
||||||
|
/// [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`graphql::InputType`]: juniper::graphql::InputType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
#[must_use]
|
||||||
|
fn impl_graphql_input_type(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::resolve::Type<#inf, #sv, #bh>
|
||||||
|
+ ::juniper::resolve::ToInputValue<#sv, #bh>
|
||||||
|
+ ::juniper::resolve::InputValue<#lt, #sv, #bh>
|
||||||
|
});
|
||||||
|
for f in self.fields.iter().filter(|f| !f.ignored) {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#field_ty:
|
||||||
|
::juniper::graphql::InputType<#lt, #inf, #sv, #field_bh>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let fields_assertions = self.fields.iter().filter_map(|f| {
|
||||||
|
(!f.ignored).then(|| {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
<#field_ty as
|
||||||
|
::juniper::graphql::InputType<#lt, #inf, #sv, #field_bh>>
|
||||||
|
::assert_input_type();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn assert_input_type() {
|
||||||
|
#( #fields_assertions )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`graphql::InputObject`] trait for
|
||||||
|
/// this [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`graphql::InputObject`]: juniper::graphql::InputObject
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
#[must_use]
|
||||||
|
fn impl_graphql_input_object(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::graphql::InputObject<#lt, #inf, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn assert_input_object() {
|
||||||
|
<Self as ::juniper::graphql::InputType<#lt, #inf, #sv, #bh>>
|
||||||
|
::assert_input_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLType`] trait for this
|
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||||
/// [GraphQL input object][0].
|
/// [GraphQL input object][0].
|
||||||
///
|
///
|
||||||
|
@ -500,6 +653,119 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Type`] trait for this
|
||||||
|
/// [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Type`]: juniper::resolve::Type
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
fn impl_resolve_type(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
let preds = &mut generics.make_where_clause().predicates;
|
||||||
|
preds.push(parse_quote! { #sv: Clone });
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
::juniper::behavior::Coerce<Self>:
|
||||||
|
::juniper::resolve::TypeName<#inf>
|
||||||
|
+ ::juniper::resolve::InputValueOwned<#sv>
|
||||||
|
});
|
||||||
|
for f in self.fields.iter().filter(|f| !f.ignored) {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
::juniper::behavior::Coerce<#field_ty>:
|
||||||
|
::juniper::resolve::Type<#inf, #sv>
|
||||||
|
+ ::juniper::resolve::InputValueOwned<#sv>
|
||||||
|
});
|
||||||
|
if f.default.is_some() {
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
#field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if f.needs_default_trait_bound() {
|
||||||
|
preds.push(parse_quote! {
|
||||||
|
#field_ty: ::std::default::Default
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let description = &self.description;
|
||||||
|
|
||||||
|
let fields_meta = self.fields.iter().filter_map(|f| {
|
||||||
|
(!f.ignored).then(|| {
|
||||||
|
let f_ty = &f.ty;
|
||||||
|
let f_bh = &f.behavior;
|
||||||
|
let f_name = &f.name;
|
||||||
|
let f_description = &f.description;
|
||||||
|
let f_default = f.default.as_ref().map(|expr| {
|
||||||
|
quote! {
|
||||||
|
.default_value(
|
||||||
|
<#f_ty as
|
||||||
|
::juniper::resolve::ToInputValue<#sv, #f_bh>>
|
||||||
|
::to_input_value(&{ #expr }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
registry.arg_reworked::<
|
||||||
|
::juniper::behavior::Coerce<#f_ty>, _,
|
||||||
|
>(#f_name, type_info)
|
||||||
|
#f_description
|
||||||
|
#f_default
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn meta<'__r, '__ti: '__r>(
|
||||||
|
registry: &mut ::juniper::Registry<'__r, #sv>,
|
||||||
|
type_info: &'__ti #inf,
|
||||||
|
) -> ::juniper::meta::MetaType<'__r, #sv>
|
||||||
|
where
|
||||||
|
#sv: '__r,
|
||||||
|
{
|
||||||
|
let fields = [#( #fields_meta ),*];
|
||||||
|
|
||||||
|
registry.register_input_object_with::<
|
||||||
|
::juniper::behavior::Coerce<Self>, _,
|
||||||
|
>(&fields, type_info, |meta| {
|
||||||
|
meta #description
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::TypeName`] trait for this
|
||||||
|
/// [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
fn impl_resolve_type_name(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn type_name(_: &#inf) -> &'static str {
|
||||||
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
||||||
/// [GraphQL input object][0].
|
/// [GraphQL input object][0].
|
||||||
///
|
///
|
||||||
|
@ -631,6 +897,96 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::InputValue`] trait for
|
||||||
|
/// this [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
fn impl_resolve_input_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#sv: ::juniper::ScalarValue
|
||||||
|
});
|
||||||
|
for f in self.fields.iter().filter(|f| !f.ignored) {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#field_ty: ::juniper::resolve::InputValue<#lt, #sv, #field_bh>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for f in self.fields.iter().filter(|f| f.needs_default_trait_bound()) {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#field_ty: ::std::default::Default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let fields = self.fields.iter().map(|f| {
|
||||||
|
let field = &f.ident;
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
|
||||||
|
let constructor = if f.ignored {
|
||||||
|
let expr = f.default.clone().unwrap_or_default();
|
||||||
|
|
||||||
|
quote! { #expr }
|
||||||
|
} else {
|
||||||
|
let name = &f.name;
|
||||||
|
|
||||||
|
let fallback = f.default.as_ref().map_or_else(
|
||||||
|
|| {
|
||||||
|
quote! {
|
||||||
|
<#field_ty as ::juniper::resolve::InputValue<#lt, #sv, #field_bh>>
|
||||||
|
::try_from_implicit_null()
|
||||||
|
.map_err(::juniper::IntoFieldError::<#sv>::into_field_error)?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|expr| quote! { #expr },
|
||||||
|
);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match obj.get(#name) {
|
||||||
|
::std::option::Option::Some(v) => {
|
||||||
|
<#field_ty as ::juniper::resolve::InputValue<#lt, #sv, #field_bh>>
|
||||||
|
::try_from_input_value(v)
|
||||||
|
.map_err(::juniper::IntoFieldError::<#sv>::into_field_error)?
|
||||||
|
}
|
||||||
|
::std::option::Option::None => { #fallback }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! { #field: { #constructor }, }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
type Error = ::juniper::FieldError<#sv>;
|
||||||
|
|
||||||
|
fn try_from_input_value(
|
||||||
|
input: &#lt ::juniper::graphql::InputValue<#sv>,
|
||||||
|
) -> ::std::result::Result<Self, Self::Error> {
|
||||||
|
let obj = input
|
||||||
|
.to_object_value()
|
||||||
|
.ok_or_else(|| ::std::format!(
|
||||||
|
"Expected input object, found: {}", input,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
::std::result::Result::Ok(Self {
|
||||||
|
#( #fields )*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`ToInputValue`] trait for this
|
/// Returns generated code implementing [`ToInputValue`] trait for this
|
||||||
/// [GraphQL input object][0].
|
/// [GraphQL input object][0].
|
||||||
///
|
///
|
||||||
|
@ -663,11 +1019,52 @@ impl Definition {
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
fn to_input_value(&self) -> ::juniper::InputValue<#scalar> {
|
fn to_input_value(&self) -> ::juniper::InputValue<#scalar> {
|
||||||
::juniper::InputValue::object(
|
::juniper::InputValue::object([#( #fields ),*])
|
||||||
#[allow(deprecated)]
|
}
|
||||||
::std::array::IntoIter::new([#( #fields ),*])
|
}
|
||||||
.collect()
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::ToInputValue`] trait for
|
||||||
|
/// this [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
fn impl_resolve_to_input_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (sv, mut generics) = gen::mix_scalar_value(generics);
|
||||||
|
for f in self.fields.iter().filter(|f| !f.ignored) {
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let fields = self.fields.iter().filter_map(|f| {
|
||||||
|
(!f.ignored).then(|| {
|
||||||
|
let field = &f.ident;
|
||||||
|
let field_ty = &f.ty;
|
||||||
|
let field_bh = &f.behavior;
|
||||||
|
let name = &f.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
(#name, <#field_ty as
|
||||||
|
::juniper::resolve::ToInputValue<#sv, #field_bh>>
|
||||||
|
::to_input_value(&self.#field))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> {
|
||||||
|
::juniper::InputValue::object([#( #fields ),*])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -716,6 +1113,47 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
|
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||||
|
/// [GraphQL input object][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||||
|
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||||
|
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||||
|
fn impl_reflect(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAME: ::juniper::reflect::Type = #name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types =
|
||||||
|
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::WrappedType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const VALUE: ::juniper::reflect::WrappedValue =
|
||||||
|
::juniper::reflect::wrap::SINGULAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
|
/// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
|
||||||
/// similar) implementation of this struct.
|
/// similar) implementation of this struct.
|
||||||
///
|
///
|
||||||
|
@ -775,4 +1213,16 @@ impl Definition {
|
||||||
|
|
||||||
generics
|
generics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
|
let generics = self.generics.clone();
|
||||||
|
let ty = {
|
||||||
|
let ident = &self.ident;
|
||||||
|
let (_, ty_gen, _) = generics.split_for_impl();
|
||||||
|
parse_quote! { #ident #ty_gen }
|
||||||
|
};
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ fn expand_on_trait(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
implemented_for: attr
|
implemented_for: attr
|
||||||
.implemented_for
|
.implemented_for
|
||||||
|
@ -207,6 +208,7 @@ fn parse_trait_method(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
||||||
ident: method_ident.clone(),
|
ident: method_ident.clone(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
arguments: Some(arguments),
|
arguments: Some(arguments),
|
||||||
has_receiver: method.sig.receiver().is_some(),
|
has_receiver: method.sig.receiver().is_some(),
|
||||||
is_async: method.sig.asyncness.is_some(),
|
is_async: method.sig.asyncness.is_some(),
|
||||||
|
@ -301,6 +303,7 @@ fn expand_on_derive_input(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
implemented_for: attr
|
implemented_for: attr
|
||||||
.implemented_for
|
.implemented_for
|
||||||
|
@ -372,6 +375,7 @@ fn parse_struct_field(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
||||||
ident: field_ident.clone(),
|
ident: field_ident.clone(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
arguments: None,
|
arguments: None,
|
||||||
has_receiver: false,
|
has_receiver: false,
|
||||||
is_async: false,
|
is_async: false,
|
||||||
|
|
|
@ -93,6 +93,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
implemented_for: attr
|
implemented_for: attr
|
||||||
.implemented_for
|
.implemented_for
|
||||||
|
@ -149,6 +150,7 @@ fn parse_field(field: &syn::Field, renaming: &rename::Policy) -> Option<field::D
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
||||||
ident: field_ident.clone(),
|
ident: field_ident.clone(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
arguments: None,
|
arguments: None,
|
||||||
has_receiver: false,
|
has_receiver: false,
|
||||||
is_async: false,
|
is_async: false,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
field, filter_attrs, gen,
|
behavior, field, filter_attrs, gen,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
GenericsExt as _, ParseBufferExt as _,
|
GenericsExt as _, ParseBufferExt as _,
|
||||||
|
@ -116,6 +116,17 @@ struct Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||||
|
/// [GraphQL interface][0] implementation with.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified marker indicating that the Rust trait should be
|
/// Explicitly specified marker indicating that the Rust trait should be
|
||||||
/// transformed into [`async_trait`].
|
/// transformed into [`async_trait`].
|
||||||
///
|
///
|
||||||
|
@ -175,6 +186,13 @@ impl Parse for Attr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"for" | "implementers" => {
|
"for" | "implementers" => {
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
for impler in input.parse_maybe_wrapped_and_punctuated::<
|
for impler in input.parse_maybe_wrapped_and_punctuated::<
|
||||||
|
@ -245,6 +263,7 @@ impl Attr {
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
scalar: try_merge_opt!(scalar: self, another),
|
scalar: try_merge_opt!(scalar: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
implemented_for: try_merge_hashset!(implemented_for: self, another => span_joined),
|
implemented_for: try_merge_hashset!(implemented_for: self, another => span_joined),
|
||||||
implements: try_merge_hashset!(implements: self, another => span_joined),
|
implements: try_merge_hashset!(implements: self, another => span_joined),
|
||||||
r#enum: try_merge_opt!(r#enum: self, another),
|
r#enum: try_merge_opt!(r#enum: self, another),
|
||||||
|
@ -324,6 +343,13 @@ struct Definition {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
scalar: scalar::Type,
|
scalar: scalar::Type,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization to generate code with for this
|
||||||
|
/// [GraphQL interface][0].
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// Defined [GraphQL fields][2] of this [GraphQL interface][1].
|
/// Defined [GraphQL fields][2] of this [GraphQL interface][1].
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
@ -368,6 +394,10 @@ impl ToTokens for Definition {
|
||||||
self.impl_field_meta_tokens().to_tokens(into);
|
self.impl_field_meta_tokens().to_tokens(into);
|
||||||
self.impl_field_tokens().to_tokens(into);
|
self.impl_field_tokens().to_tokens(into);
|
||||||
self.impl_async_field_tokens().to_tokens(into);
|
self.impl_async_field_tokens().to_tokens(into);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
self.impl_resolve_value().to_tokens(into);
|
||||||
|
gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into);
|
||||||
|
self.impl_reflect().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,6 +852,36 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Value`] trait for this
|
||||||
|
/// [GraphQL interface][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Value`]: juniper::resolve::Value
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
fn impl_resolve_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
_: Option<&[::juniper::Selection<'_, #sv>]>,
|
||||||
|
_: &#inf,
|
||||||
|
_: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::ExecutionResult<#sv> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
||||||
/// [GraphQL interface][1].
|
/// [GraphQL interface][1].
|
||||||
///
|
///
|
||||||
|
@ -953,6 +1013,69 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
|
/// [`reflect::BaseSubTypes`], [`reflect::WrappedType`] and
|
||||||
|
/// [`reflect::Fields`] traits for this [GraphQL interface][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||||
|
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||||
|
/// [`reflect::Fields`]: juniper::reflect::Fields
|
||||||
|
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||||
|
fn impl_reflect(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
let implers = &self.implemented_for;
|
||||||
|
let interfaces = &self.implements;
|
||||||
|
let fields = self.fields.iter().map(|f| &f.name);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAME: ::juniper::reflect::Type = #name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types = &[
|
||||||
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||||
|
#( <#implers as ::juniper::reflect::BaseType<#bh>>::NAME ),*
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::Implements<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types = &[#(
|
||||||
|
<#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||||
|
),*];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::WrappedType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const VALUE: ::juniper::reflect::WrappedValue =
|
||||||
|
::juniper::reflect::wrap::SINGULAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::Fields<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Names = &[#( #fields ),*];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`FieldMeta`] for each field of this
|
/// Returns generated code implementing [`FieldMeta`] for each field of this
|
||||||
/// [GraphQL interface][1].
|
/// [GraphQL interface][1].
|
||||||
///
|
///
|
||||||
|
@ -1373,6 +1496,20 @@ impl Definition {
|
||||||
generics
|
generics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
|
let generics = self.generics.clone();
|
||||||
|
|
||||||
|
let ty = {
|
||||||
|
let ident = &self.enum_alias_ident;
|
||||||
|
let (_, ty_gen, _) = generics.split_for_impl();
|
||||||
|
parse_quote! { #ident #ty_gen }
|
||||||
|
};
|
||||||
|
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
|
|
||||||
/// Indicates whether this enum has non-exhaustive phantom variant to hold
|
/// Indicates whether this enum has non-exhaustive phantom variant to hold
|
||||||
/// type parameters.
|
/// type parameters.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -117,6 +117,7 @@ where
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
interfaces: attr
|
interfaces: attr
|
||||||
.interfaces
|
.interfaces
|
||||||
|
@ -218,6 +219,7 @@ fn parse_field(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
||||||
ident: method_ident.clone(),
|
ident: method_ident.clone(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
arguments: Some(arguments),
|
arguments: Some(arguments),
|
||||||
has_receiver: method.sig.receiver().is_some(),
|
has_receiver: method.sig.receiver().is_some(),
|
||||||
is_async: method.sig.asyncness.is_some(),
|
is_async: method.sig.asyncness.is_some(),
|
||||||
|
|
|
@ -94,6 +94,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
|
||||||
.map(SpanContainer::into_inner)
|
.map(SpanContainer::into_inner)
|
||||||
.unwrap_or_else(|| parse_quote! { () }),
|
.unwrap_or_else(|| parse_quote! { () }),
|
||||||
scalar,
|
scalar,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
fields,
|
fields,
|
||||||
interfaces: attr
|
interfaces: attr
|
||||||
.interfaces
|
.interfaces
|
||||||
|
@ -143,6 +144,7 @@ fn parse_field(field: &syn::Field, renaming: &rename::Policy) -> Option<field::D
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
deprecated: attr.deprecated.map(SpanContainer::into_inner),
|
||||||
ident: field_ident.clone(),
|
ident: field_ident.clone(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
arguments: None,
|
arguments: None,
|
||||||
has_receiver: false,
|
has_receiver: false,
|
||||||
is_async: false,
|
is_async: false,
|
||||||
|
|
|
@ -18,10 +18,10 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
field, filter_attrs, gen,
|
behavior, field, filter_attrs, gen,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
GenericsExt as _, ParseBufferExt as _, TypeExt,
|
GenericsExt as _, ParseBufferExt as _, TypeExt as _,
|
||||||
},
|
},
|
||||||
rename, scalar, Description, SpanContainer,
|
rename, scalar, Description, SpanContainer,
|
||||||
};
|
};
|
||||||
|
@ -71,6 +71,17 @@ pub(crate) struct Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
pub(crate) scalar: Option<SpanContainer<scalar::AttrValue>>,
|
pub(crate) scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||||
|
/// [GraphQL object][0] implementation with.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified [GraphQL interfaces][2] this [GraphQL object][1]
|
/// Explicitly specified [GraphQL interfaces][2] this [GraphQL object][1]
|
||||||
/// type implements.
|
/// type implements.
|
||||||
///
|
///
|
||||||
|
@ -130,6 +141,13 @@ impl Parse for Attr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"impl" | "implements" | "interfaces" => {
|
"impl" | "implements" | "interfaces" => {
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
for iface in input.parse_maybe_wrapped_and_punctuated::<
|
for iface in input.parse_maybe_wrapped_and_punctuated::<
|
||||||
|
@ -175,6 +193,7 @@ impl Attr {
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
scalar: try_merge_opt!(scalar: self, another),
|
scalar: try_merge_opt!(scalar: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
interfaces: try_merge_hashset!(interfaces: self, another => span_joined),
|
interfaces: try_merge_hashset!(interfaces: self, another => span_joined),
|
||||||
rename_fields: try_merge_opt!(rename_fields: self, another),
|
rename_fields: try_merge_opt!(rename_fields: self, another),
|
||||||
is_internal: self.is_internal || another.is_internal,
|
is_internal: self.is_internal || another.is_internal,
|
||||||
|
@ -240,6 +259,13 @@ pub(crate) struct Definition<Operation: ?Sized> {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
pub(crate) scalar: scalar::Type,
|
pub(crate) scalar: scalar::Type,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization to generate code with for this
|
||||||
|
/// [GraphQL object][0].
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
pub(crate) behavior: behavior::Type,
|
||||||
|
|
||||||
/// Defined [GraphQL fields][2] of this [GraphQL object][1].
|
/// Defined [GraphQL fields][2] of this [GraphQL object][1].
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
/// [1]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
@ -297,7 +323,7 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
||||||
// Modify lifetime names to omit "lifetime name `'a` shadows a
|
// Modify lifetime names to omit "lifetime name `'a` shadows a
|
||||||
// lifetime name that is already in scope" error.
|
// lifetime name that is already in scope" error.
|
||||||
let mut ty = self.ty.clone();
|
let mut ty = self.ty.clone();
|
||||||
ty.lifetimes_iter_mut(&mut |lt| {
|
ty.named_lifetimes_iter_mut(&mut |lt| {
|
||||||
let ident = lt.ident.unraw();
|
let ident = lt.ident.unraw();
|
||||||
lt.ident = format_ident!("__fa__{ident}");
|
lt.ident = format_ident!("__fa__{ident}");
|
||||||
lifetimes.push(lt.clone());
|
lifetimes.push(lt.clone());
|
||||||
|
@ -324,6 +350,12 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
||||||
(quote! { #impl_generics }, where_clause.cloned())
|
(quote! { #impl_generics }, where_clause.cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
|
(self.ty.clone(), self.generics.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`marker::IsOutputType`] trait for
|
/// Returns generated code implementing [`marker::IsOutputType`] trait for
|
||||||
/// this [GraphQL object][1].
|
/// this [GraphQL object][1].
|
||||||
///
|
///
|
||||||
|
@ -418,6 +450,274 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
|
/// [`reflect::BaseSubTypes`], [`reflect::Implements`],
|
||||||
|
/// [`reflect::WrappedType`] and [`reflect::Fields`] traits for this
|
||||||
|
/// [GraphQL object][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||||
|
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||||
|
/// [`reflect::Fields`]: juniper::reflect::Fields
|
||||||
|
/// [`reflect::Implements`]: juniper::reflect::Implements
|
||||||
|
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn impl_reflect(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
let interfaces = self.interfaces.iter();
|
||||||
|
let fields = self.fields.iter().map(|f| &f.name);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAME: ::juniper::reflect::Type = #name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types =
|
||||||
|
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::Implements<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types = &[#(
|
||||||
|
<#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||||
|
),*];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::WrappedType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const VALUE: ::juniper::reflect::WrappedValue =
|
||||||
|
::juniper::reflect::wrap::SINGULAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::Fields<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Names = &[#( #fields ),*];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::Field`] trait for each
|
||||||
|
/// [field][1] of this [GraphQL object][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::Field`]: juniper::reflect::Field
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn impl_reflect_field(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| {
|
||||||
|
let (f_name, f_ty, f_bh) = (&field.name, &field.ty, &field.behavior);
|
||||||
|
|
||||||
|
let arguments = field.arguments.as_ref();
|
||||||
|
let arguments = arguments
|
||||||
|
.iter()
|
||||||
|
.flat_map(|vec| vec.iter().filter_map(field::MethodArgument::as_regular))
|
||||||
|
.map(|arg| {
|
||||||
|
let (a_name, a_ty, a_bh) = (&arg.name, &arg.ty, &arg.behavior);
|
||||||
|
|
||||||
|
quote! {(
|
||||||
|
#a_name,
|
||||||
|
<#a_ty as ::juniper::reflect::BaseType<#a_bh>>
|
||||||
|
::NAME,
|
||||||
|
<#a_ty as ::juniper::reflect::WrappedType<#a_bh>>
|
||||||
|
::VALUE,
|
||||||
|
)}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::Field<
|
||||||
|
{ ::juniper::reflect::fnv1a128(#f_name) }, #bh,
|
||||||
|
> for #ty #where_clause {
|
||||||
|
const TYPE: ::juniper::reflect::Type =
|
||||||
|
<#f_ty as ::juniper::reflect::BaseType<#f_bh>>
|
||||||
|
::NAME;
|
||||||
|
|
||||||
|
const SUB_TYPES: ::juniper::reflect::Types =
|
||||||
|
<#f_ty as ::juniper::reflect::BaseSubTypes<#f_bh>>
|
||||||
|
::NAMES;
|
||||||
|
|
||||||
|
const WRAPPED_VALUE: juniper::reflect::WrappedValue =
|
||||||
|
<#f_ty as ::juniper::reflect::WrappedType<#f_bh>>
|
||||||
|
::VALUE;
|
||||||
|
|
||||||
|
const ARGUMENTS: &'static [(
|
||||||
|
::juniper::reflect::Name,
|
||||||
|
::juniper::reflect::Type,
|
||||||
|
::juniper::reflect::WrappedValue,
|
||||||
|
)] = &[#( #arguments ),*];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Value`] trait for this
|
||||||
|
/// [GraphQL object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Value`]: juniper::resolve::Value
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
pub(crate) fn impl_resolve_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
_: Option<&[::juniper::Selection<'_, #sv>]>,
|
||||||
|
_: &#inf,
|
||||||
|
_: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::ExecutionResult<#sv> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::StaticField`] trait for
|
||||||
|
/// each [field][1] of this [GraphQL object][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::StaticField`]: juniper::resolve::StaticField
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||||
|
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn impl_resolve_static_field(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| {
|
||||||
|
let mut generics = generics.clone();
|
||||||
|
let (f_name, f_bh) = (&field.name, &field.behavior);
|
||||||
|
let (f_ident, f_ty) = (&field.ident, &field.ty);
|
||||||
|
|
||||||
|
let body = if !field.is_async {
|
||||||
|
let (f_for_ty, f_hrtb_ty) = f_ty.to_hrtb_lifetimes();
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#f_for_ty #f_hrtb_ty:
|
||||||
|
::juniper::resolve::Resolvable<#sv, #f_bh>
|
||||||
|
});
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#f_for_ty <#f_hrtb_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>>::Value:
|
||||||
|
::juniper::resolve::Value<#inf, #cx, #sv, #f_bh>
|
||||||
|
});
|
||||||
|
|
||||||
|
let val = if field.is_method() {
|
||||||
|
let f_anon_ty = f_ty.to_anonymized_lifetimes();
|
||||||
|
let args = field
|
||||||
|
.arguments
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|arg| match arg {
|
||||||
|
field::MethodArgument::Regular(arg) => {
|
||||||
|
let a_name = &arg.name;
|
||||||
|
let (a_ty, a_bh) = (&arg.ty, &arg.behavior);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#a_ty: ::juniper::resolve::InputValueOwned<#sv, #a_bh>
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
args.resolve::<#a_ty, #a_bh>(#a_name)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field::MethodArgument::Context(cx_ty) => {
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
#cx: ::juniper::Extract<#cx_ty>
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
<#cx as ::juniper::Extract<#cx_ty>>
|
||||||
|
::extract(executor.context())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field::MethodArgument::Executor => {
|
||||||
|
quote! {
|
||||||
|
executor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let rcv = field.has_receiver.then(|| {
|
||||||
|
quote! { self, }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
<#f_anon_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>>
|
||||||
|
::into_value(Self::#f_ident(#rcv #( #args ),*))?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
self.#f_ident
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
executor.resolve_value::<#f_bh, _, _>(&#val, type_info)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
::std::panic!(
|
||||||
|
"Tried to resolve async field `{}` on type `{}` with a sync resolver",
|
||||||
|
#f_name,
|
||||||
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::StaticField<
|
||||||
|
{ ::juniper::reflect::fnv1a128(#f_name) },
|
||||||
|
#inf, #cx, #sv, #bh,
|
||||||
|
> for #ty #where_clause {
|
||||||
|
fn resolve_static_field(
|
||||||
|
&self,
|
||||||
|
args: &::juniper::Arguments<'_, #sv>,
|
||||||
|
type_info: &#inf,
|
||||||
|
executor: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::ExecutionResult<#sv> {
|
||||||
|
#body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLType`] trait for this
|
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||||
/// [GraphQL object][1].
|
/// [GraphQL object][1].
|
||||||
///
|
///
|
||||||
|
@ -496,6 +796,12 @@ impl ToTokens for Definition<Query> {
|
||||||
self.impl_field_meta_tokens().to_tokens(into);
|
self.impl_field_meta_tokens().to_tokens(into);
|
||||||
self.impl_field_tokens().to_tokens(into);
|
self.impl_field_tokens().to_tokens(into);
|
||||||
self.impl_async_field_tokens().to_tokens(into);
|
self.impl_async_field_tokens().to_tokens(into);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
self.impl_resolve_value().to_tokens(into);
|
||||||
|
gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into);
|
||||||
|
self.impl_resolve_static_field().to_tokens(into);
|
||||||
|
self.impl_reflect().to_tokens(into);
|
||||||
|
self.impl_reflect_field().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use syn::{parse_quote, spanned::Spanned};
|
||||||
|
|
||||||
use crate::common::{diagnostic, parse, scalar, SpanContainer};
|
use crate::common::{diagnostic, parse, scalar, SpanContainer};
|
||||||
|
|
||||||
use super::{derive::parse_derived_methods, Attr, Definition, Methods, ParseToken, TypeOrIdent};
|
use super::{derive::parse_derived_methods, Attr, Definition, Methods, ParseToken};
|
||||||
|
|
||||||
/// [`diagnostic::Scope`] of errors for `#[graphql_scalar]` macro.
|
/// [`diagnostic::Scope`] of errors for `#[graphql_scalar]` macro.
|
||||||
const ERR: diagnostic::Scope = diagnostic::Scope::ScalarAttr;
|
const ERR: diagnostic::Scope = diagnostic::Scope::ScalarAttr;
|
||||||
|
@ -47,7 +47,7 @@ fn expand_on_type_alias(
|
||||||
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
||||||
|
|
||||||
let def = Definition {
|
let def = Definition {
|
||||||
ty: TypeOrIdent::Type(ast.ty.clone()),
|
ident: ast.ident.clone(),
|
||||||
where_clause: attr
|
where_clause: attr
|
||||||
.where_clause
|
.where_clause
|
||||||
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
||||||
|
@ -60,6 +60,8 @@ fn expand_on_type_alias(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
||||||
scalar,
|
scalar,
|
||||||
|
scalar_value: attr.scalar.as_deref().into(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
@ -74,11 +76,12 @@ fn expand_on_derive_input(
|
||||||
ast: syn::DeriveInput,
|
ast: syn::DeriveInput,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
|
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
|
||||||
|
|
||||||
let methods = parse_derived_methods(&ast, &attr)?;
|
let methods = parse_derived_methods(&ast, &attr)?;
|
||||||
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
||||||
|
|
||||||
let def = Definition {
|
let def = Definition {
|
||||||
ty: TypeOrIdent::Ident(ast.ident.clone()),
|
ident: ast.ident.clone(),
|
||||||
where_clause: attr
|
where_clause: attr
|
||||||
.where_clause
|
.where_clause
|
||||||
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
||||||
|
@ -91,6 +94,8 @@ fn expand_on_derive_input(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
||||||
scalar,
|
scalar,
|
||||||
|
scalar_value: attr.scalar.as_deref().into(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use syn::{parse_quote, spanned::Spanned};
|
||||||
|
|
||||||
use crate::common::{diagnostic, scalar, SpanContainer};
|
use crate::common::{diagnostic, scalar, SpanContainer};
|
||||||
|
|
||||||
use super::{Attr, Definition, Field, Methods, ParseToken, TypeOrIdent};
|
use super::{Attr, Definition, Field, Methods, ParseToken};
|
||||||
|
|
||||||
/// [`diagnostic::Scope`] of errors for `#[derive(GraphQLScalar)]` macro.
|
/// [`diagnostic::Scope`] of errors for `#[derive(GraphQLScalar)]` macro.
|
||||||
const ERR: diagnostic::Scope = diagnostic::Scope::ScalarDerive;
|
const ERR: diagnostic::Scope = diagnostic::Scope::ScalarDerive;
|
||||||
|
@ -15,23 +15,28 @@ const ERR: diagnostic::Scope = diagnostic::Scope::ScalarDerive;
|
||||||
pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
||||||
let ast = syn::parse2::<syn::DeriveInput>(input)?;
|
let ast = syn::parse2::<syn::DeriveInput>(input)?;
|
||||||
let attr = Attr::from_attrs("graphql", &ast.attrs)?;
|
let attr = Attr::from_attrs("graphql", &ast.attrs)?;
|
||||||
|
|
||||||
let methods = parse_derived_methods(&ast, &attr)?;
|
let methods = parse_derived_methods(&ast, &attr)?;
|
||||||
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
|
||||||
|
|
||||||
|
let name = attr
|
||||||
|
.name
|
||||||
|
.map(SpanContainer::into_inner)
|
||||||
|
.unwrap_or_else(|| ast.ident.to_string());
|
||||||
|
|
||||||
Ok(Definition {
|
Ok(Definition {
|
||||||
ty: TypeOrIdent::Ident(ast.ident.clone()),
|
ident: ast.ident,
|
||||||
where_clause: attr
|
where_clause: attr
|
||||||
.where_clause
|
.where_clause
|
||||||
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
.map_or_else(Vec::new, |cl| cl.into_inner()),
|
||||||
generics: ast.generics.clone(),
|
generics: ast.generics,
|
||||||
methods,
|
methods,
|
||||||
name: attr
|
name,
|
||||||
.name
|
|
||||||
.map(SpanContainer::into_inner)
|
|
||||||
.unwrap_or_else(|| ast.ident.to_string()),
|
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
specified_by_url: attr.specified_by_url.map(SpanContainer::into_inner),
|
||||||
scalar,
|
scalar,
|
||||||
|
scalar_value: attr.scalar.as_deref().into(),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
}
|
}
|
||||||
.to_token_stream())
|
.to_token_stream())
|
||||||
}
|
}
|
||||||
|
@ -81,7 +86,8 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn:
|
||||||
.first()
|
.first()
|
||||||
.filter(|_| fields.unnamed.len() == 1)
|
.filter(|_| fields.unnamed.len() == 1)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(Field::Unnamed)
|
.map(Field::try_from)
|
||||||
|
.transpose()?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ERR.custom_error(
|
ERR.custom_error(
|
||||||
ast.span(),
|
ast.span(),
|
||||||
|
@ -94,7 +100,8 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn:
|
||||||
.first()
|
.first()
|
||||||
.filter(|_| fields.named.len() == 1)
|
.filter(|_| fields.named.len() == 1)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(Field::Named)
|
.map(Field::try_from)
|
||||||
|
.transpose()?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ERR.custom_error(
|
ERR.custom_error(
|
||||||
ast.span(),
|
ast.span(),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -93,6 +93,7 @@ fn expand_on_trait(
|
||||||
description: attr.description.map(SpanContainer::into_inner),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics.clone(),
|
generics: ast.generics.clone(),
|
||||||
variants,
|
variants,
|
||||||
};
|
};
|
||||||
|
@ -210,6 +211,7 @@ fn parse_variant_from_trait_method(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
context: method_context_ty,
|
context: method_context_ty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ fn expand_enum(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
||||||
.map(SpanContainer::into_inner)
|
.map(SpanContainer::into_inner)
|
||||||
.unwrap_or_else(|| parse_quote! { () }),
|
.unwrap_or_else(|| parse_quote! { () }),
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics,
|
generics: ast.generics,
|
||||||
variants,
|
variants,
|
||||||
})
|
})
|
||||||
|
@ -163,6 +164,7 @@ fn parse_variant_from_enum_variant(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
context: None,
|
context: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -214,6 +216,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
||||||
.map(SpanContainer::into_inner)
|
.map(SpanContainer::into_inner)
|
||||||
.unwrap_or_else(|| parse_quote! { () }),
|
.unwrap_or_else(|| parse_quote! { () }),
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics,
|
generics: ast.generics,
|
||||||
variants,
|
variants,
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
filter_attrs, gen,
|
behavior, filter_attrs, gen,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _,
|
ParseBufferExt as _,
|
||||||
|
@ -74,6 +74,17 @@ struct Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||||
|
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||||
|
/// [GraphQL union][0] implementation with.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified external resolver functions for [GraphQL union][1]
|
/// Explicitly specified external resolver functions for [GraphQL union][1]
|
||||||
/// variants.
|
/// variants.
|
||||||
///
|
///
|
||||||
|
@ -128,6 +139,13 @@ impl Parse for Attr {
|
||||||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||||
.none_or_else(|_| err::dup_arg(&ident))?
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
}
|
}
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"on" => {
|
"on" => {
|
||||||
let ty = input.parse::<syn::Type>()?;
|
let ty = input.parse::<syn::Type>()?;
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
|
@ -160,6 +178,7 @@ impl Attr {
|
||||||
description: try_merge_opt!(description: self, another),
|
description: try_merge_opt!(description: self, another),
|
||||||
context: try_merge_opt!(context: self, another),
|
context: try_merge_opt!(context: self, another),
|
||||||
scalar: try_merge_opt!(scalar: self, another),
|
scalar: try_merge_opt!(scalar: self, another),
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
external_resolvers: try_merge_hashmap!(
|
external_resolvers: try_merge_hashmap!(
|
||||||
external_resolvers: self, another => span_joined
|
external_resolvers: self, another => span_joined
|
||||||
),
|
),
|
||||||
|
@ -188,6 +207,19 @@ impl Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct VariantAttr {
|
struct VariantAttr {
|
||||||
|
/// Explicitly specified type of the custom [`Behavior`] this
|
||||||
|
/// [GraphQL union][0] member implementation is parametrized with, to
|
||||||
|
/// [coerce] in the generated code from.
|
||||||
|
///
|
||||||
|
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||||
|
/// code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
behavior: Option<SpanContainer<behavior::Type>>,
|
||||||
|
|
||||||
/// Explicitly specified marker for the variant/field being ignored and not
|
/// Explicitly specified marker for the variant/field being ignored and not
|
||||||
/// included into [GraphQL union][1].
|
/// included into [GraphQL union][1].
|
||||||
///
|
///
|
||||||
|
@ -210,6 +242,13 @@ impl Parse for VariantAttr {
|
||||||
while !input.is_empty() {
|
while !input.is_empty() {
|
||||||
let ident = input.parse::<syn::Ident>()?;
|
let ident = input.parse::<syn::Ident>()?;
|
||||||
match ident.to_string().as_str() {
|
match ident.to_string().as_str() {
|
||||||
|
"behave" | "behavior" => {
|
||||||
|
input.parse::<token::Eq>()?;
|
||||||
|
let bh = input.parse::<behavior::Type>()?;
|
||||||
|
out.behavior
|
||||||
|
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||||
|
.none_or_else(|_| err::dup_arg(&ident))?
|
||||||
|
}
|
||||||
"ignore" | "skip" => out
|
"ignore" | "skip" => out
|
||||||
.ignore
|
.ignore
|
||||||
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
||||||
|
@ -236,6 +275,7 @@ impl VariantAttr {
|
||||||
/// duplicates, if any.
|
/// duplicates, if any.
|
||||||
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
ignore: try_merge_opt!(ignore: self, another),
|
ignore: try_merge_opt!(ignore: self, another),
|
||||||
external_resolver: try_merge_opt!(external_resolver: self, another),
|
external_resolver: try_merge_opt!(external_resolver: self, another),
|
||||||
})
|
})
|
||||||
|
@ -301,6 +341,13 @@ struct Definition {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
scalar: scalar::Type,
|
scalar: scalar::Type,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization to generate code with for this
|
||||||
|
/// [GraphQL union][0].
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// Variants definitions of this [GraphQL union][1].
|
/// Variants definitions of this [GraphQL union][1].
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
@ -315,6 +362,10 @@ impl ToTokens for Definition {
|
||||||
self.impl_graphql_value_tokens().to_tokens(into);
|
self.impl_graphql_value_tokens().to_tokens(into);
|
||||||
self.impl_graphql_value_async_tokens().to_tokens(into);
|
self.impl_graphql_value_async_tokens().to_tokens(into);
|
||||||
self.impl_reflection_traits_tokens().to_tokens(into);
|
self.impl_reflection_traits_tokens().to_tokens(into);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
self.impl_resolve_value().to_tokens(into);
|
||||||
|
gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into);
|
||||||
|
self.impl_reflect().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,6 +611,36 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Value`] trait for this
|
||||||
|
/// [GraphQL union][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Value`]: juniper::resolve::Value
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
fn impl_resolve_value(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (inf, generics) = gen::mix_type_info(generics);
|
||||||
|
let (cx, generics) = gen::mix_context(generics);
|
||||||
|
let (sv, generics) = gen::mix_scalar_value(generics);
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
fn resolve_value(
|
||||||
|
&self,
|
||||||
|
_: Option<&[::juniper::Selection<'_, #sv>]>,
|
||||||
|
_: &#inf,
|
||||||
|
_: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||||
|
) -> ::juniper::ExecutionResult<#sv> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
/// Returns generated code implementing [`GraphQLValueAsync`] trait for this
|
||||||
/// [GraphQL union][1].
|
/// [GraphQL union][1].
|
||||||
///
|
///
|
||||||
|
@ -645,6 +726,70 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
|
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||||
|
/// [GraphQL union][0].
|
||||||
|
///
|
||||||
|
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||||
|
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||||
|
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
fn impl_reflect(&self) -> TokenStream {
|
||||||
|
let bh = &self.behavior;
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
|
||||||
|
let member_names = self.variants.iter().map(|m| {
|
||||||
|
let m_ty = &m.ty;
|
||||||
|
let m_bh = &m.behavior;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
<#m_ty as ::juniper::reflect::BaseType<#m_bh>>::NAME
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAME: ::juniper::reflect::Type = #name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const NAMES: ::juniper::reflect::Types = &[
|
||||||
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||||
|
#( #member_names ),*
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_gens ::juniper::reflect::WrappedType<#bh>
|
||||||
|
for #ty #where_clause
|
||||||
|
{
|
||||||
|
const VALUE: ::juniper::reflect::WrappedValue =
|
||||||
|
::juniper::reflect::wrap::SINGULAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
|
let generics = self.generics.clone();
|
||||||
|
let ty = {
|
||||||
|
let ident = &self.ty;
|
||||||
|
let (_, ty_gen, _) = generics.split_for_impl();
|
||||||
|
parse_quote! { #ident #ty_gen }
|
||||||
|
};
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Definition of [GraphQL union][1] variant for code generation.
|
/// Definition of [GraphQL union][1] variant for code generation.
|
||||||
|
@ -667,6 +812,14 @@ struct VariantDefinition {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
resolver_check: syn::Expr,
|
resolver_check: syn::Expr,
|
||||||
|
|
||||||
|
/// [`Behavior`] parametrization of this [GraphQL union][0] member
|
||||||
|
/// implementation to [coerce] from in the generated code.
|
||||||
|
///
|
||||||
|
/// [`Behavior`]: juniper::behavior
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
|
/// [coerce]: juniper::behavior::Coerce
|
||||||
|
behavior: behavior::Type,
|
||||||
|
|
||||||
/// Rust type of [`Context`] that this [GraphQL union][1] variant requires
|
/// Rust type of [`Context`] that this [GraphQL union][1] variant requires
|
||||||
/// for resolution.
|
/// for resolution.
|
||||||
///
|
///
|
||||||
|
@ -783,6 +936,7 @@ fn emerge_union_variants_from_attr(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: behavior::Type::default(), // TODO: remove at all
|
||||||
context: None,
|
context: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue