From 0ea61fa4936d62d9bdf0ef5344cc719b66575a3e Mon Sep 17 00:00:00 2001
From: Magnus Hallin <mhallin@fastmail.com>
Date: Sat, 12 Nov 2016 22:52:34 +0100
Subject: [PATCH] Add interface and union tests

---
 src/executor_tests/interfaces_unions.rs | 221 ++++++++++++++++++++++++
 src/executor_tests/mod.rs               |   1 +
 src/macros/interface.rs                 |  13 ++
 3 files changed, 235 insertions(+)
 create mode 100644 src/executor_tests/interfaces_unions.rs

diff --git a/src/executor_tests/interfaces_unions.rs b/src/executor_tests/interfaces_unions.rs
new file mode 100644
index 00000000..b840787d
--- /dev/null
+++ b/src/executor_tests/interfaces_unions.rs
@@ -0,0 +1,221 @@
+mod interface {
+    use value::Value;
+    use schema::model::RootNode;
+
+    trait Pet {
+        fn name(&self) -> &str;
+
+        fn as_dog(&self) -> Option<&Dog> { None }
+        fn as_cat(&self) -> Option<&Cat> { None }
+    }
+
+    graphql_interface!(<'a> &'a Pet: () as "Pet" |&self| {
+        field name() -> &str { self.name() }
+
+        instance_resolvers: |&_| {
+            &Dog => self.as_dog(),
+            &Cat => self.as_cat(),
+        }
+    });
+
+    struct Dog {
+        name: String,
+        woofs: bool,
+    }
+
+    impl Pet for Dog {
+        fn name(&self) -> &str { &self.name }
+        fn as_dog(&self) -> Option<&Dog> { Some(self) }
+    }
+
+    graphql_object!(Dog: () |&self| {
+        field name() -> &str { &self.name }
+        field woofs() -> bool { self.woofs }
+
+        interfaces: [&Pet]
+    });
+
+    struct Cat {
+        name: String,
+        meows: bool,
+    }
+
+    impl Pet for Cat {
+        fn name(&self) -> &str { &self.name }
+        fn as_cat(&self) -> Option<&Cat> { Some(self) }
+    }
+
+    graphql_object!(Cat: () |&self| {
+        field name() -> &str { &self.name }
+        field meows() -> bool { self.meows }
+
+        interfaces: [&Pet]
+    });
+
+    struct Schema {
+        pets: Vec<Box<Pet>>,
+    }
+
+    graphql_object!(Schema: () |&self| {
+        field pets() -> Vec<&Pet> {
+            self.pets.iter().map(|p| p.as_ref()).collect()
+        }
+    });
+
+    #[test]
+    fn test() {
+        let schema = RootNode::new(
+            Schema {
+                pets: vec![
+                    Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
+                    Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
+                ],
+            },
+            ());
+        let doc = r"
+          {
+            pets {
+              name
+              ... on Dog {
+                woofs
+              }
+              ... on Cat {
+                meows
+              }
+            }
+          }";
+
+        let vars = vec![
+        ].into_iter().collect();
+
+        let (result, errs) = ::execute(doc, None, &schema, &vars, &())
+            .expect("Execution failed");
+
+        assert_eq!(errs, []);
+
+        println!("Result: {:?}", result);
+
+        assert_eq!(
+            result,
+            Value::object(vec![
+                ("pets", Value::list(vec![
+                    Value::object(vec![
+                        ("name", Value::string("Odie")),
+                        ("woofs", Value::boolean(true)),
+                    ].into_iter().collect()),
+                    Value::object(vec![
+                        ("name", Value::string("Garfield")),
+                        ("meows", Value::boolean(false)),
+                    ].into_iter().collect()),
+                ])),
+            ].into_iter().collect()));
+    }
+}
+
+
+
+
+mod union {
+    use value::Value;
+    use schema::model::RootNode;
+
+    trait Pet {
+        fn as_dog(&self) -> Option<&Dog> { None }
+        fn as_cat(&self) -> Option<&Cat> { None }
+    }
+
+    graphql_union!(<'a> &'a Pet: () as "Pet" |&self| {
+        instance_resolvers: |&_| {
+            &Dog => self.as_dog(),
+            &Cat => self.as_cat(),
+        }
+    });
+
+    struct Dog {
+        name: String,
+        woofs: bool,
+    }
+
+    impl Pet for Dog {
+        fn as_dog(&self) -> Option<&Dog> { Some(self) }
+    }
+
+    graphql_object!(Dog: () |&self| {
+        field name() -> &str { &self.name }
+        field woofs() -> bool { self.woofs }
+    });
+
+    struct Cat {
+        name: String,
+        meows: bool,
+    }
+
+    impl Pet for Cat {
+        fn as_cat(&self) -> Option<&Cat> { Some(self) }
+    }
+
+    graphql_object!(Cat: () |&self| {
+        field name() -> &str { &self.name }
+        field meows() -> bool { self.meows }
+    });
+
+    struct Schema {
+        pets: Vec<Box<Pet>>,
+    }
+
+    graphql_object!(Schema: () |&self| {
+        field pets() -> Vec<&Pet> {
+            self.pets.iter().map(|p| p.as_ref()).collect()
+        }
+    });
+
+    #[test]
+    fn test() {
+        let schema = RootNode::new(
+            Schema {
+                pets: vec![
+                    Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
+                    Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
+                ],
+            },
+            ());
+        let doc = r"
+          {
+            pets {
+              ... on Dog {
+                name
+                woofs
+              }
+              ... on Cat {
+                name
+                meows
+              }
+            }
+          }";
+
+        let vars = vec![
+        ].into_iter().collect();
+
+        let (result, errs) = ::execute(doc, None, &schema, &vars, &())
+            .expect("Execution failed");
+
+        assert_eq!(errs, []);
+
+        println!("Result: {:?}", result);
+
+        assert_eq!(
+            result,
+            Value::object(vec![
+                ("pets", Value::list(vec![
+                    Value::object(vec![
+                        ("name", Value::string("Odie")),
+                        ("woofs", Value::boolean(true)),
+                    ].into_iter().collect()),
+                    Value::object(vec![
+                        ("name", Value::string("Garfield")),
+                        ("meows", Value::boolean(false)),
+                    ].into_iter().collect()),
+                ])),
+            ].into_iter().collect()));
+    }
+}
diff --git a/src/executor_tests/mod.rs b/src/executor_tests/mod.rs
index 33440ba8..bf962966 100644
--- a/src/executor_tests/mod.rs
+++ b/src/executor_tests/mod.rs
@@ -3,3 +3,4 @@ mod variables;
 mod enums;
 mod directives;
 mod executor;
+mod interfaces_unions;
diff --git a/src/macros/interface.rs b/src/macros/interface.rs
index be120a6a..2fddfbe6 100644
--- a/src/macros/interface.rs
+++ b/src/macros/interface.rs
@@ -169,6 +169,19 @@ macro_rules! graphql_interface {
         graphql_interface!(@gather_meta, ($reg, $acc, $descr), $( $rest )*)
     };
 
+    // instance_resolvers: | <ctxtvar> | [...]
+    (
+        @ gather_meta,
+        ($reg:expr, $acc:expr, $descr:expr),
+        instance_resolvers : | $ctxtvar:pat | { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
+    ) => {
+        $(
+            let _ = $reg.get_type::<$srctype>();
+        )*
+
+            graphql_interface!(@gather_meta, ($reg, $acc, $descr), $( $rest )*)
+    };
+
     // instance_resolvers: | <ctxtvar> | [...]
     (
         @ concrete_type_name,