Repository: graphql-rust/juniper (language: rust)

Issue:
Union with Boxed enum variants
Hi, 

Upgrading from 0.14 to 0.15 is giving me some problems with unions that have some boxed enum variant, i.e.:
```
#[derive(GraphQLUnion)]
pub enum GqlResult {
    Ok(Box<Data>),
    Err(Error),
}
```

the docs specifies that container types are supported but the compiler complains about a missing impl of the trait `GraphQLObjectType`  for Box, I'm doing something wrong?


Discussion / hints:
Seems to be a major hole in type-level stuff 🙀

Will try to submit the PR shortly.

Fix patch:
diff --git a/juniper/src/types/marker.rs b/juniper/src/types/marker.rs
index f7207fd8c..b04ee327e 100644
--- a/juniper/src/types/marker.rs
+++ b/juniper/src/types/marker.rs
@@ -5,6 +5,8 @@
 //! traits are used. Encountering an error where one of these traits
 //! is involved implies that the construct is not valid in GraphQL.
 
+use std::sync::Arc;
+
 use crate::{GraphQLType, ScalarValue};
 
 /// Maker object for GraphQL objects.
@@ -23,6 +25,39 @@ pub trait GraphQLObjectType<S: ScalarValue>: GraphQLType<S> {
     fn mark() {}
 }
 
+impl<'a, S, T> GraphQLObjectType<S> for &T
+where
+    T: GraphQLObjectType<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLObjectType<S> for Box<T>
+where
+    T: GraphQLObjectType<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLObjectType<S> for Arc<T>
+where
+    T: GraphQLObjectType<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
 /// Maker trait for [GraphQL interfaces][1].
 ///
 /// This trait extends the [`GraphQLType`] and is only used to mark an [interface][1]. During
@@ -47,6 +82,39 @@ pub trait GraphQLInterface<S: ScalarValue>: GraphQLType<S> {
     fn mark() {}
 }
 
+impl<'a, S, T> GraphQLInterface<S> for &T
+where
+    T: GraphQLInterface<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLInterface<S> for Box<T>
+where
+    T: GraphQLInterface<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLInterface<S> for Arc<T>
+where
+    T: GraphQLInterface<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
 /// Maker trait for [GraphQL unions][1].
 ///
 /// This trait extends the [`GraphQLType`] and is only used to mark an [union][1]. During compile
@@ -71,6 +139,39 @@ pub trait GraphQLUnion<S: ScalarValue>: GraphQLType<S> {
     fn mark() {}
 }
 
+impl<'a, S, T> GraphQLUnion<S> for &T
+where
+    T: GraphQLUnion<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLUnion<S> for Box<T>
+where
+    T: GraphQLUnion<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<S, T> GraphQLUnion<S> for Arc<T>
+where
+    T: GraphQLUnion<S> + ?Sized,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
 /// Marker trait for types which can be used as output types.
 ///
 /// The GraphQL specification differentiates between input and output
@@ -86,60 +187,86 @@ pub trait IsOutputType<S: ScalarValue> /*: GraphQLType<S>*/ {
     fn mark() {}
 }
 
-/// Marker trait for types which can be used as input types.
-///
-/// The GraphQL specification differentiates between input and output
-/// types. Each type which can be used as an input type should
-/// implement this trait. The specification defines enum, scalar, and
-/// input object input types.
-pub trait IsInputType<S: ScalarValue>: GraphQLType<S> {
-    /// An arbitrary function without meaning.
-    ///
-    /// May contain compile timed check logic which ensures that types
-    /// are used correctly according to the GraphQL specification.
-    fn mark() {}
-}
-
-impl<S, T> IsInputType<S> for Option<T>
+impl<'a, S, T> IsOutputType<S> for &T
 where
-    T: IsInputType<S>,
+    T: IsOutputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsOutputType<S> for Option<T>
+impl<S, T> IsOutputType<S> for Box<T>
 where
-    T: IsOutputType<S>,
+    T: IsOutputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsOutputType<S> for Vec<T>
+impl<S, T> IsOutputType<S> for Arc<T>
 where
-    T: IsOutputType<S>,
+    T: IsOutputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsOutputType<S> for [T]
+impl<S, T> IsOutputType<S> for Option<T>
 where
     T: IsOutputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsInputType<S> for Vec<T>
+impl<S, T> IsOutputType<S> for Vec<T>
 where
-    T: IsInputType<S>,
+    T: IsOutputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsInputType<S> for [T]
+impl<S, T> IsOutputType<S> for [T]
 where
-    T: IsInputType<S>,
+    T: IsOutputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
+impl<'a, S> IsOutputType<S> for str where S: ScalarValue {}
+
+/// Marker trait for types which can be used as input types.
+///
+/// The GraphQL specification differentiates between input and output
+/// types. Each type which can be used as an input type should
+/// implement this trait. The specification defines enum, scalar, and
+/// input object input types.
+pub trait IsInputType<S: ScalarValue>: GraphQLType<S> {
+    /// An arbitrary function without meaning.
+    ///
+    /// May contain compile timed check logic which ensures that types
+    /// are used correctly according to the GraphQL specification.
+    fn mark() {}
 }
 
 impl<'a, S, T> IsInputType<S> for &T
@@ -147,39 +274,65 @@ where
     T: IsInputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
-impl<'a, S, T> IsOutputType<S> for &T
+
+impl<S, T> IsInputType<S> for Box<T>
 where
-    T: IsOutputType<S> + ?Sized,
+    T: IsInputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsInputType<S> for Box<T>
+impl<S, T> IsInputType<S> for Arc<T>
 where
     T: IsInputType<S> + ?Sized,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
-impl<S, T> IsOutputType<S> for Box<T>
+
+impl<S, T> IsInputType<S> for Option<T>
 where
-    T: IsOutputType<S> + ?Sized,
+    T: IsInputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
-impl<S, T> IsInputType<S> for std::sync::Arc<T>
+impl<S, T> IsInputType<S> for Vec<T>
 where
-    T: IsInputType<S> + ?Sized,
+    T: IsInputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
-impl<S, T> IsOutputType<S> for std::sync::Arc<T>
+
+impl<S, T> IsInputType<S> for [T]
 where
-    T: IsOutputType<S> + ?Sized,
+    T: IsInputType<S>,
     S: ScalarValue,
 {
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
 }
 
 impl<'a, S> IsInputType<S> for str where S: ScalarValue {}
-impl<'a, S> IsOutputType<S> for str where S: ScalarValue {}


Test patch:
diff --git a/integration_tests/juniper_tests/src/codegen/union_derive.rs b/integration_tests/juniper_tests/src/codegen/union_derive.rs
index 8db96512e..ed90741bd 100644
--- a/integration_tests/juniper_tests/src/codegen/union_derive.rs
+++ b/integration_tests/juniper_tests/src/codegen/union_derive.rs
@@ -1472,3 +1472,91 @@ mod full_featured_struct {
         );
     }
 }
+
+mod issue_845 {
+    use std::sync::Arc;
+
+    use super::*;
+
+    #[derive(GraphQLUnion)]
+    enum Character {
+        A(Box<Human>),
+        B(Arc<Droid>),
+    }
+
+    enum QueryRoot {
+        Human,
+        Droid,
+    }
+
+    #[graphql_object]
+    impl QueryRoot {
+        fn character(&self) -> Character {
+            match self {
+                Self::Human => Character::A(Box::new(Human {
+                    id: "human-32".to_string(),
+                    home_planet: "earth".to_string(),
+                })),
+                Self::Droid => Character::B(Arc::new(Droid {
+                    id: "droid-99".to_string(),
+                    primary_function: "run".to_string(),
+                })),
+            }
+        }
+    }
+
+    const DOC: &str = r#"{
+        character {
+            ... on Human {
+                humanId: id
+                homePlanet
+            }
+            ... on Droid {
+                droidId: id
+                primaryFunction
+            }
+        }
+    }"#;
+
+    #[tokio::test]
+    async fn resolves_human() {
+        let schema = schema(QueryRoot::Human);
+
+        assert_eq!(
+            execute(DOC, None, &schema, &Variables::new(), &()).await,
+            Ok((
+                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
+                vec![],
+            )),
+        );
+    }
+
+    #[tokio::test]
+    async fn resolves_droid() {
+        let schema = schema(QueryRoot::Droid);
+
+        assert_eq!(
+            execute(DOC, None, &schema, &Variables::new(), &()).await,
+            Ok((
+                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                vec![],
+            )),
+        );
+    }
+
+    #[tokio::test]
+    async fn is_graphql_union() {
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                kind
+            }
+        }"#;
+
+        let schema = schema(QueryRoot::Human);
+
+        assert_eq!(
+            execute(DOC, None, &schema, &Variables::new(), &()).await,
+            Ok((graphql_value!({"__type": {"kind": "UNION"}}), vec![])),
+        );
+    }
+}