Re-enable marks-based static checks in code generated by macros (#751)
- add associated type to IntoResolvable and IntoFieldResult traits allowing to name the GraphQLType being resolved - relax Sized requirement on some IsInputType and IsOutputType impls
This commit is contained in:
parent
2ab00f55d6
commit
a684e1d91c
12 changed files with 136 additions and 44 deletions
|
@ -1,3 +1,12 @@
|
|||
error[E0277]: the trait bound `ObjectA: juniper::marker::IsInputType<__S>` is not satisfied
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `juniper::marker::IsInputType<__S>` is not implemented for `ObjectA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjectA: juniper::FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
error[E0277]: the trait bound `Obj: juniper::marker::IsInputType<juniper::DefaultScalarValue>` is not satisfied
|
||||
--> $DIR/impl_argument_no_object.rs:8:1
|
||||
|
|
||||
8 | #[juniper::graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `juniper::marker::IsInputType<juniper::DefaultScalarValue>` is not implemented for `Obj`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `Obj: juniper::FromInputValue` is not satisfied
|
||||
--> $DIR/impl_argument_no_object.rs:8:1
|
||||
|
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use fnv::FnvHashMap;
|
||||
|
||||
use juniper::{
|
||||
DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue, InputValue,
|
||||
ToInputValue,
|
||||
marker, DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue,
|
||||
InputValue, ToInputValue,
|
||||
};
|
||||
|
||||
#[derive(GraphQLInputObject, Debug, PartialEq)]
|
||||
|
@ -50,6 +50,8 @@ struct OverrideDocComment {
|
|||
#[derive(Debug, PartialEq)]
|
||||
struct Fake;
|
||||
|
||||
impl<'a> marker::IsInputType<DefaultScalarValue> for &'a Fake {}
|
||||
|
||||
impl<'a> FromInputValue for &'a Fake {
|
||||
fn from_input_value(_v: &InputValue) -> Option<&'a Fake> {
|
||||
None
|
||||
|
|
|
@ -3,7 +3,7 @@ use juniper::DefaultScalarValue;
|
|||
use juniper::Object;
|
||||
|
||||
#[cfg(test)]
|
||||
use juniper::{self, execute, EmptyMutation, EmptySubscription, RootNode, Value, Variables};
|
||||
use juniper::{execute, EmptyMutation, EmptySubscription, FieldError, RootNode, Value, Variables};
|
||||
|
||||
pub struct MyObject;
|
||||
|
||||
|
@ -84,3 +84,16 @@ where
|
|||
|
||||
f((type_info, fields));
|
||||
}
|
||||
|
||||
mod fallible {
|
||||
use super::*;
|
||||
|
||||
struct Obj;
|
||||
|
||||
#[juniper::graphql_object]
|
||||
impl Obj {
|
||||
fn test(&self, arg: String) -> Result<String, FieldError> {
|
||||
Ok(arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -253,6 +253,8 @@ where
|
|||
T: GraphQLValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
type Type;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S>;
|
||||
}
|
||||
|
@ -263,6 +265,8 @@ where
|
|||
S: ScalarValue,
|
||||
T::Context: FromContext<C>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
|
||||
Ok(Some((FromContext::from(ctx), self)))
|
||||
}
|
||||
|
@ -274,6 +278,8 @@ where
|
|||
T: GraphQLValue<S>,
|
||||
T::Context: FromContext<C>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
|
||||
self.map(|v: T| Some((<T::Context as FromContext<C>>::from(ctx), v)))
|
||||
.map_err(IntoFieldError::into_field_error)
|
||||
|
@ -285,6 +291,8 @@ where
|
|||
S: ScalarValue,
|
||||
T: GraphQLValue<S>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
|
||||
Ok(Some(self))
|
||||
}
|
||||
|
@ -295,6 +303,8 @@ where
|
|||
S: ScalarValue,
|
||||
T: GraphQLValue<S>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
|
||||
Ok(self.map(|(ctx, v)| (ctx, Some(v))))
|
||||
}
|
||||
|
@ -305,6 +315,8 @@ where
|
|||
S: ScalarValue,
|
||||
T: GraphQLValue<S>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
|
||||
self.map(Some)
|
||||
}
|
||||
|
@ -316,6 +328,8 @@ where
|
|||
S: ScalarValue,
|
||||
T: GraphQLValue<S>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
|
||||
self.map(|o| o.map(|(ctx, v)| (ctx, Some(v))))
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ pub use crate::{
|
|||
types::{
|
||||
async_await::{GraphQLTypeAsync, GraphQLValueAsync},
|
||||
base::{Arguments, GraphQLType, GraphQLValue, TypeKind},
|
||||
marker::{self, GraphQLUnion},
|
||||
marker::{self, GraphQLUnion, IsOutputType},
|
||||
scalars::{EmptyMutation, EmptySubscription, ID},
|
||||
subscriptions::{
|
||||
ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue,
|
||||
|
|
|
@ -178,6 +178,10 @@ macro_rules! graphql_interface {
|
|||
}
|
||||
);
|
||||
|
||||
$crate::__juniper_impl_trait!(
|
||||
impl<$($scalar)* $(, $lifetimes)* > IsOutputType for $name { }
|
||||
);
|
||||
|
||||
$crate::__juniper_impl_trait!(
|
||||
impl<$($scalar)* $(, $lifetimes)* > GraphQLValue for $name {
|
||||
type Context = $ctx;
|
||||
|
|
|
@ -7,27 +7,36 @@ use futures::Stream;
|
|||
|
||||
use crate::{FieldError, GraphQLValue, ScalarValue};
|
||||
|
||||
/// Trait for converting `T` to `Ok(T)` if T is not Result.
|
||||
/// This is useful in subscription macros when user can provide type alias for
|
||||
/// Stream or Result<Stream, _> and then a function on Stream should be called.
|
||||
/// Trait for wrapping [`Stream`] into [`Ok`] if it's not [`Result`].
|
||||
///
|
||||
/// Used in subscription macros when user can provide type alias for [`Stream`] or
|
||||
/// `Result<Stream, _>` and then a function on [`Stream`] should be called.
|
||||
pub trait IntoFieldResult<T, S> {
|
||||
/// Turn current type into a generic result
|
||||
/// Type of items yielded by this [`Stream`].
|
||||
type Item;
|
||||
|
||||
/// Turns current [`Stream`] type into a generic [`Result`].
|
||||
fn into_result(self) -> Result<T, FieldError<S>>;
|
||||
}
|
||||
|
||||
impl<T, E, S> IntoFieldResult<T, S> for Result<T, E>
|
||||
where
|
||||
T: IntoFieldResult<T, S>,
|
||||
E: Into<FieldError<S>>,
|
||||
{
|
||||
type Item = T::Item;
|
||||
|
||||
fn into_result(self) -> Result<T, FieldError<S>> {
|
||||
self.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, S> IntoFieldResult<T, S> for T
|
||||
impl<T, S> IntoFieldResult<T, S> for T
|
||||
where
|
||||
T: Stream<Item = I>,
|
||||
T: Stream,
|
||||
{
|
||||
type Item = T::Item;
|
||||
|
||||
fn into_result(self) -> Result<T, FieldError<S>> {
|
||||
Ok(self)
|
||||
}
|
||||
|
|
|
@ -53,7 +53,8 @@ pub trait GraphQLUnion<S: ScalarValue>: GraphQLType<S> {
|
|||
/// types. Each type which can be used as an output type should
|
||||
/// implement this trait. The specification defines enum, scalar,
|
||||
/// object, union, and interface as output types.
|
||||
pub trait IsOutputType<S: ScalarValue>: GraphQLType<S> {
|
||||
// TODO: Re-enable GraphQLType requirement in #682
|
||||
pub trait IsOutputType<S: ScalarValue> /*: GraphQLType<S>*/ {
|
||||
/// An arbitrary function without meaning.
|
||||
///
|
||||
/// May contain compile timed check logic which ensures that types
|
||||
|
@ -132,13 +133,13 @@ where
|
|||
|
||||
impl<S, T> IsInputType<S> for Box<T>
|
||||
where
|
||||
T: IsInputType<S>,
|
||||
T: IsInputType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
}
|
||||
impl<S, T> IsOutputType<S> for Box<T>
|
||||
where
|
||||
T: IsOutputType<S>,
|
||||
T: IsOutputType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
}
|
||||
|
|
|
@ -195,6 +195,9 @@ fn impl_scalar_struct(
|
|||
<#inner_ty as ::juniper::ParseScalarValue<S>>::from_str(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ::juniper::ScalarValue> ::juniper::marker::IsOutputType<S> for #ident { }
|
||||
impl<S: ::juniper::ScalarValue> ::juniper::marker::IsInputType<S> for #ident { }
|
||||
);
|
||||
|
||||
Ok(content)
|
||||
|
|
|
@ -611,7 +611,7 @@ impl ToTokens for UnionDefinition {
|
|||
#where_clause
|
||||
{
|
||||
fn mark() {
|
||||
#( <#var_types as ::juniper::marker::GraphQLObjectType<#scalar>>::mark(); )*
|
||||
#( <#var_types as ::juniper::marker::IsOutputType<#scalar>>::mark(); )*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -831,12 +831,9 @@ impl GraphQLTypeDefiniton {
|
|||
if self.scalar.is_none() && self.generic_scalar {
|
||||
// No custom scalar specified, but always generic specified.
|
||||
// Therefore we inject the generic scalar.
|
||||
|
||||
generics.params.push(parse_quote!(__S));
|
||||
|
||||
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
|
||||
// Insert ScalarValue constraint.
|
||||
where_clause
|
||||
generics
|
||||
.make_where_clause()
|
||||
.predicates
|
||||
.push(parse_quote!(__S: ::juniper::ScalarValue));
|
||||
}
|
||||
|
@ -962,26 +959,29 @@ impl GraphQLTypeDefiniton {
|
|||
)
|
||||
};
|
||||
|
||||
// FIXME: enable this if interfaces are supported
|
||||
// let marks = self.fields.iter().map(|field| {
|
||||
// let field_ty = &field._type;
|
||||
let marks = self.fields.iter().map(|field| {
|
||||
let field_marks = field.args.iter().map(|arg| {
|
||||
let arg_ty = &arg._type;
|
||||
quote! { <#arg_ty as ::juniper::marker::IsInputType<#scalar>>::mark(); }
|
||||
});
|
||||
|
||||
// let field_marks = field.args.iter().map(|arg| {
|
||||
// let arg_ty = &arg._type;
|
||||
// quote!(<#arg_ty as ::juniper::marker::IsInputType<#scalar>>::mark();)
|
||||
// });
|
||||
let field_ty = &field._type;
|
||||
let resolved_ty = quote! {
|
||||
<#field_ty as ::juniper::IntoResolvable<
|
||||
'_, #scalar, _, <Self as ::juniper::GraphQLValue<#scalar>>::Context,
|
||||
>>::Type
|
||||
};
|
||||
|
||||
// quote!(
|
||||
// #( #field_marks)*
|
||||
// <#field_ty as ::juniper::marker::IsOutputType<#scalar>>::mark();
|
||||
// )
|
||||
// });
|
||||
quote! {
|
||||
#( #field_marks )*
|
||||
<#resolved_ty as ::juniper::marker::IsOutputType<#scalar>>::mark();
|
||||
}
|
||||
});
|
||||
|
||||
let output = quote!(
|
||||
impl#impl_generics ::juniper::marker::IsOutputType<#scalar> for #ty #type_generics_tokens #where_clause {
|
||||
fn mark() {
|
||||
// FIXME: enable this if interfaces are supported
|
||||
// #( #marks )*
|
||||
#( #marks )*
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1001,10 +1001,10 @@ impl GraphQLTypeDefiniton {
|
|||
) -> ::juniper::meta::MetaType<'r, #scalar>
|
||||
where #scalar : 'r,
|
||||
{
|
||||
let fields = vec![
|
||||
let fields = [
|
||||
#( #field_definitions ),*
|
||||
];
|
||||
let meta = registry.build_object_type::<#ty>( info, &fields )
|
||||
let meta = registry.build_object_type::<#ty>(info, &fields)
|
||||
#description
|
||||
#interfaces;
|
||||
meta.into_meta()
|
||||
|
@ -1226,7 +1226,37 @@ impl GraphQLTypeDefiniton {
|
|||
},
|
||||
);
|
||||
|
||||
let marks = self.fields.iter().map(|field| {
|
||||
let field_marks = field.args.iter().map(|arg| {
|
||||
let arg_ty = &arg._type;
|
||||
quote! { <#arg_ty as ::juniper::marker::IsInputType<#scalar>>::mark(); }
|
||||
});
|
||||
|
||||
let field_ty = &field._type;
|
||||
let stream_item_ty = quote! {
|
||||
<#field_ty as ::juniper::IntoFieldResult::<_, #scalar>>::Item
|
||||
};
|
||||
let resolved_ty = quote! {
|
||||
<#stream_item_ty as ::juniper::IntoResolvable<
|
||||
'_, #scalar, _, <Self as ::juniper::GraphQLValue<#scalar>>::Context,
|
||||
>>::Type
|
||||
};
|
||||
|
||||
quote! {
|
||||
#( #field_marks )*
|
||||
<#resolved_ty as ::juniper::marker::IsOutputType<#scalar>>::mark();
|
||||
}
|
||||
});
|
||||
|
||||
let graphql_implementation = quote!(
|
||||
impl#impl_generics ::juniper::marker::IsOutputType<#scalar> for #ty #type_generics_tokens
|
||||
#where_clause
|
||||
{
|
||||
fn mark() {
|
||||
#( #marks )*
|
||||
}
|
||||
}
|
||||
|
||||
impl#impl_generics ::juniper::GraphQLType<#scalar> for #ty #type_generics_tokens
|
||||
#where_clause
|
||||
{
|
||||
|
@ -1240,10 +1270,10 @@ impl GraphQLTypeDefiniton {
|
|||
) -> ::juniper::meta::MetaType<'r, #scalar>
|
||||
where #scalar : 'r,
|
||||
{
|
||||
let fields = vec![
|
||||
let fields = [
|
||||
#( #field_definitions ),*
|
||||
];
|
||||
let meta = registry.build_object_type::<#ty>( info, &fields )
|
||||
let meta = registry.build_object_type::<#ty>(info, &fields)
|
||||
#description
|
||||
#interfaces;
|
||||
meta.into_meta()
|
||||
|
@ -1690,18 +1720,16 @@ impl GraphQLTypeDefiniton {
|
|||
{}
|
||||
);
|
||||
|
||||
// FIXME: enable this if interfaces are supported
|
||||
// let marks = self.fields.iter().map(|field| {
|
||||
// let _ty = &field._type;
|
||||
// quote!(<#_ty as ::juniper::marker::IsInputType<#scalar>>::mark();)
|
||||
// });
|
||||
let marks = self.fields.iter().map(|field| {
|
||||
let field_ty = &field._type;
|
||||
quote! { <#field_ty as ::juniper::marker::IsInputType<#scalar>>::mark(); }
|
||||
});
|
||||
|
||||
let mut body = quote!(
|
||||
impl#impl_generics ::juniper::marker::IsInputType<#scalar> for #ty #type_generics_tokens
|
||||
#where_clause {
|
||||
fn mark() {
|
||||
// FIXME: enable this if interfaces are supported
|
||||
// #( #marks )*
|
||||
#( #marks )*
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue