From 41112d0b6d50115a67066af7fcbb84616382ad81 Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 30 Apr 2017 20:07:36 -0600
Subject: [PATCH 01/11] Drop support for rustc-serialization and use serde by
 default.

---
 Cargo.toml                          |   7 +-
 examples/server.rs                  |   2 +-
 src/integrations/rustc_serialize.rs | 114 ----------------------------
 3 files changed, 5 insertions(+), 118 deletions(-)
 delete mode 100644 src/integrations/rustc_serialize.rs

diff --git a/Cargo.toml b/Cargo.toml
index da46c251..44b35bb7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,15 +16,16 @@ harness = false
 path = "benches/bench.rs"
 
 [features]
-default = ["rustc-serialize"]
+default = ["serde", "serde_json"]
 nightly = []
-iron-handlers = ["iron", "rustc-serialize"]
+iron-handlers = ["iron", "serde", "serde_json"]
 expose-test-schema = []
 
 [dependencies]
-rustc-serialize = { version = "^0.3.19", optional = true }
 iron = { version = "^0.5.1", optional = true }
 serde = { version = "^0.9.1", optional = true }
+serde_json = {version ="^0.9.1", optional = true }
+
 
 [dev-dependencies]
 iron = "^0.5.1"
diff --git a/examples/server.rs b/examples/server.rs
index e4ef3cbe..9aa31107 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -1,7 +1,7 @@
 extern crate iron;
 extern crate mount;
 extern crate logger;
-extern crate rustc_serialize;
+extern crate serde;
 #[macro_use] extern crate juniper;
 
 use std::env;
diff --git a/src/integrations/rustc_serialize.rs b/src/integrations/rustc_serialize.rs
deleted file mode 100644
index 23bfdcc8..00000000
--- a/src/integrations/rustc_serialize.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-use rustc_serialize::json::{ToJson, Json};
-
-use ::{GraphQLError, Value};
-use ast::InputValue;
-use executor::ExecutionError;
-use parser::{ParseError, Spanning, SourcePosition};
-use validation::RuleError;
-
-fn parse_error_to_json(err: &Spanning<ParseError>) -> Json {
-    Json::Array(vec![
-        Json::Object(vec![
-            ("message".to_owned(), format!("{}", err.item).to_json()),
-            ("locations".to_owned(), vec![
-                Json::Object(vec![
-                    ("line".to_owned(), (err.start.line() + 1).to_json()),
-                    ("column".to_owned(), (err.start.column() + 1).to_json())
-                ].into_iter().collect()),
-            ].to_json()),
-        ].into_iter().collect()),
-    ])
-}
-
-impl ToJson for ExecutionError {
-    fn to_json(&self) -> Json {
-        Json::Object(vec![
-            ("message".to_owned(), self.message().to_json()),
-            ("locations".to_owned(), vec![self.location().clone()].to_json()),
-            ("path".to_owned(), self.path().to_json()),
-        ].into_iter().collect())
-    }
-}
-
-impl<'a> ToJson for GraphQLError<'a> {
-    fn to_json(&self) -> Json {
-        match *self {
-            GraphQLError::ParseError(ref err) => parse_error_to_json(err),
-            GraphQLError::ValidationError(ref errs) => errs.to_json(),
-            GraphQLError::MultipleOperationsProvided => Json::String(
-                "Must provide operation name if query contains multiple operations".to_owned()),
-            GraphQLError::NoOperationProvided => Json::String(
-                "Must provide an operation".to_owned()),
-            GraphQLError::UnknownOperationName => Json::String(
-                "Unknown operation".to_owned()),
-        }
-    }
-}
-
-impl ToJson for InputValue {
-    fn to_json(&self) -> Json {
-        match *self {
-            InputValue::Null | InputValue::Variable(_) => Json::Null,
-            InputValue::Int(i) => Json::I64(i),
-            InputValue::Float(f) => Json::F64(f),
-            InputValue::String(ref s) | InputValue::Enum(ref s) => Json::String(s.clone()),
-            InputValue::Boolean(b) => Json::Boolean(b),
-            InputValue::List(ref l) => Json::Array(l.iter().map(|x| x.item.to_json()).collect()),
-            InputValue::Object(ref o) => Json::Object(o.iter().map(|&(ref k, ref v)| (k.item.clone(), v.item.to_json())).collect()),
-       }
-    }
-}
-
-impl InputValue {
-    /// Convert a `Json` structure into an `InputValue`.
-    ///
-    /// This consumes the JSON instance.
-    ///
-    /// Notes:
-    /// * No enums or variables will be produced by this method.
-    /// * All lists and objects will be unlocated
-    pub fn from_json(json: Json) -> InputValue {
-        match json {
-            Json::I64(i) => InputValue::int(i),
-            Json::U64(u) => InputValue::float(u as f64),
-            Json::F64(f) => InputValue::float(f),
-            Json::String(s) => InputValue::string(s),
-            Json::Boolean(b) => InputValue::boolean(b),
-            Json::Array(a) => InputValue::list(a.into_iter().map(InputValue::from_json).collect()),
-            Json::Object(o) => InputValue::object(o.into_iter().map(|(k, v)| (k, InputValue::from_json(v))).collect()),
-            Json::Null => InputValue::null(),
-        }
-    }
-}
-
-impl ToJson for RuleError {
-    fn to_json(&self) -> Json {
-        Json::Object(vec![
-            ("message".to_owned(), self.message().to_json()),
-            ("locations".to_owned(), self.locations().to_json()),
-        ].into_iter().collect())
-    }
-}
-
-impl ToJson for SourcePosition {
-    fn to_json(&self) -> Json {
-        Json::Object(vec![
-            ("line".to_owned(), (self.line() + 1).to_json()),
-            ("column".to_owned(), (self.column() + 1).to_json()),
-        ].into_iter().collect())
-    }
-}
-
-impl ToJson for Value {
-    fn to_json(&self) -> Json {
-        match *self {
-            Value::Null => Json::Null,
-            Value::Int(i) => Json::I64(i),
-            Value::Float(f) => Json::F64(f),
-            Value::String(ref s) => Json::String(s.clone()),
-            Value::Boolean(b) => Json::Boolean(b),
-            Value::List(ref l) => Json::Array(l.iter().map(|x| x.to_json()).collect()),
-            Value::Object(ref o) => Json::Object(o.iter().map(|(k,v)| (k.clone(), v.to_json())).collect()),
-       }
-    }
-}

From 9adc0c4479094266c4093b4062ef47710b161236 Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 30 Apr 2017 20:09:10 -0600
Subject: [PATCH 02/11] Removed rustc-serialization as dependency and updated
 the docs that referenced rustc.

---
 src/lib.rs | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index fb94eaac..919f3887 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,7 @@ schema, as well as an optional integration into the [Iron framework][2]. It
 tries to keep the number of dynamic operations to a minimum, and give you as the
 schema developer the control of the query execution path.
 
-Juniper only depends on `rustc-serialize` by default, making it lightweight and
+Juniper only depends on `serde` and `serde_json` by default, making it lightweight and
 easy to drop into any project. Through the `iron-handlers` feature, it also
 depends on Iron.
 
@@ -185,13 +185,11 @@ built-in [GraphiQL][6] handler included.
 
 #![cfg_attr(feature="nightly", feature(test))]
 #![warn(missing_docs)]
-
-#[cfg(feature="rustc-serialize")] extern crate rustc_serialize;
-#[cfg(feature="serde")] extern crate serde;
-
 #[cfg(feature="nightly")] extern crate test;
 #[cfg(feature="iron-handlers")] #[macro_use(itry, iexpect)] extern crate iron;
 #[cfg(test)] extern crate iron_test;
+extern crate serde;
+extern crate serde_json;
 
 #[macro_use] mod macros;
 mod ast;

From 54cfbeb010107f08c8066402ce241b0bf598859a Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 30 Apr 2017 20:20:08 -0600
Subject: [PATCH 03/11] - Replaced rustc-serialization logic with Serde. -
 Added a GraphQlIronError - Changed execute from lib.rs to execute_query

---
 src/integrations/iron_handlers.rs | 163 +++++++++++++++++++++---------
 1 file changed, 118 insertions(+), 45 deletions(-)

diff --git a/src/integrations/iron_handlers.rs b/src/integrations/iron_handlers.rs
index 1101aefd..a4f363eb 100644
--- a/src/integrations/iron_handlers.rs
+++ b/src/integrations/iron_handlers.rs
@@ -7,11 +7,19 @@ use iron::status;
 use iron::method;
 use iron::url::Url;
 
-use std::collections::BTreeMap;
+use std::io::Read;
+use std::io::Error as IoError;
+use std::io::ErrorKind;
+use std::collections::HashMap;
+use std::error::Error;
+use std::fmt;
+use std::boxed::Box;
 
-use rustc_serialize::json::{ToJson, Json};
+use serde_json;
+use serde_json::Value as Json;
+use serde_json::error::Error as SerdeError;
 
-use ::{InputValue, GraphQLType, RootNode, Variables, execute};
+use ::{InputValue, GraphQLType, RootNode, Variables, execute as execute_query};
 
 /// Handler that executes GraphQL queries in the given schema
 ///
@@ -77,59 +85,74 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
     }
 
     fn handle_post(&self, req: &mut Request) -> IronResult<Response> {
-        let json_data = itry!(Json::from_reader(&mut req.body));
-
-        let json_obj = match json_data {
-            Json::Object(o) => o,
-            _ => return Ok(Response::with((status::BadRequest, "No JSON object was decoded"))),
-        };
-
-        let mut query = None;
-        let mut variables = Variables::new();
-
-        for (k, v) in json_obj {
-            if k == "query" {
-                query = v.as_string().map(|s| s.to_owned());
+        let mut request_payload = String::new();
+        itry!(req.body.read_to_string(&mut request_payload));
+        let json_data =
+            match serde_json::from_str::<Json>(&*request_payload) {
+                Ok(json) => json,
+                Err(err) => {
+                    let error = IronError::new(
+                        Box::new(GraphQlIronError::Serde(err)),
+                        (status::BadRequest, "No JSON object was decoded."));
+                    return Err(error)
+                }
+            };
+        match json_data {
+            Json::Object(json_obj) => {
+                let mut query = None;
+                let mut variables = Variables::new();
+                for (k, v) in json_obj {
+                    if k == "query" {
+                        query = v.as_str().map(|query| query.to_owned());
+                    }
+                    else if k == "variables" {
+                        variables = InputValue::from_json(v).to_object_value()
+                            .map(|o| o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect())
+                            .unwrap_or_default();
+                    }
+                }
+                let query = iexpect!(query);
+                self.execute(req, &query, &variables)
             }
-            else if k == "variables" {
-                variables = InputValue::from_json(v).to_object_value()
-                    .map(|o| o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect())
-                    .unwrap_or_default();
+            _ => {
+                let error = IronError::new(
+                    Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
+                                                                "Was able parse a JSON item but it\
+                                                                 was not an object as expected."))),
+                            (status::BadRequest, "No JSON object was decoded."));
+                Err(error)
             }
         }
-
-        let query = iexpect!(query);
-
-        self.execute(req, &query, &variables)
     }
 
     fn execute(&self, req: &mut Request, query: &str, variables: &Variables) -> IronResult<Response> {
         let context = (self.context_factory)(req);
-        let result = execute(query, None, &self.root_node, variables, &context);
-
+        let result = execute_query(query, None, &self.root_node, variables, &context);
         let content_type = "application/json".parse::<Mime>().unwrap();
-        let mut map = BTreeMap::new();
+        let mut map = HashMap::new();
 
         match result {
             Ok((result, errors)) => {
-                map.insert("data".to_owned(), result.to_json());
+                let response_data = serde_json::to_value(result)
+                    .expect("Failed to convert response data to JSON.");
+                map.insert("data".to_owned(), response_data);
 
                 if !errors.is_empty() {
-                    map.insert("errors".to_owned(), errors.to_json());
+                    let response_data = serde_json::to_value(errors)
+                        .expect("Failed to convert the errors to JSON.");
+                    map.insert("errors".to_owned(), response_data);
                 }
-
-                let data = Json::Object(map);
-                let json = data.pretty();
-
-                Ok(Response::with((content_type, status::Ok, json.to_string())))
+                let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
+                let json = serde_json::to_string_pretty(&data).expect("Failed to convert response to JSON.");
+                Ok(Response::with((content_type, status::Ok, json)))
             }
-
             Err(err) => {
-                map.insert("errors".to_owned(), err.to_json());
-
-                let data = Json::Object(map);
-                let json = data.pretty();
-
+                let response_data = serde_json::to_value(err)
+                    .expect("Failed to convert error data to JSON.");
+                map.insert("errors".to_owned(), response_data);
+                let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
+                let json = serde_json::to_string_pretty(&data)
+                    .expect("Failed to convert response to JSON");
                 Ok(Response::with((content_type, status::BadRequest, json.to_string())))
             }
         }
@@ -236,11 +259,61 @@ impl Handler for GraphiQLHandler {
     }
 }
 
+/// A general error allowing the developer to see the underlying issue.
+pub enum GraphQlIronError {
+    ///Captures any errors that were caused by Serde.
+    Serde(SerdeError),
+    /// Captures any error related the IO.
+    IO(IoError)
+}
+
+impl fmt::Display for GraphQlIronError {
+    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            GraphQlIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
+            GraphQlIronError::IO(ref err) => fmt::Display::fmt(err, &mut f),
+        }
+    }
+}
+
+impl fmt::Debug for GraphQlIronError {
+    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            GraphQlIronError::Serde(ref err) => fmt::Debug::fmt(err, &mut f),
+            GraphQlIronError::IO(ref err) => fmt::Debug::fmt(err, &mut f),
+        }
+    }
+}
+
+impl Error for GraphQlIronError {
+    fn description(&self) -> &str {
+       match *self {
+           GraphQlIronError::Serde(ref err) => {
+               err.description()
+           },
+           GraphQlIronError::IO(ref err) => {
+               err.description()
+           }
+       }
+   }
+
+   fn cause(&self) -> Option<&Error> {
+       match *self {
+           GraphQlIronError::Serde(ref err) => {
+               err.cause()
+           }
+           GraphQlIronError::IO(ref err) => {
+               err.cause()
+           }
+       }
+   }
+}
+
 
 #[cfg(test)]
 mod tests {
-    use rustc_serialize::json::Json;
-
+    use serde_json::Value as Json;
+    use serde_json;
     use iron::prelude::*;
     use iron::status;
     use iron::headers;
@@ -267,7 +340,7 @@ mod tests {
     fn unwrap_json_response(resp: Response) -> Json {
         let result = response::extract_body_to_string(resp);
 
-        Json::from_str(&result).expect("Could not parse JSON object")
+        serde_json::from_str::<Json>(&result).expect("Could not parse JSON object")
     }
 
     #[test]
@@ -286,7 +359,7 @@ mod tests {
 
         assert_eq!(
             json,
-            Json::from_str(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
+            serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
                 .expect("Invalid JSON constant in test"));
     }
 
@@ -307,7 +380,7 @@ mod tests {
 
         assert_eq!(
             json,
-            Json::from_str(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
+            serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
                 .expect("Invalid JSON constant in test"));
     }
 

From 494628a7c7132e39f55f3be3d9ac2148220c2a5c Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 30 Apr 2017 20:20:41 -0600
Subject: [PATCH 04/11] Removed rustc-serialization as a dependency.

---
 src/integrations/mod.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/integrations/mod.rs b/src/integrations/mod.rs
index f597709d..02768f18 100644
--- a/src/integrations/mod.rs
+++ b/src/integrations/mod.rs
@@ -1,3 +1,2 @@
 #[cfg(feature="iron-handlers")] pub mod iron_handlers;
-#[cfg(feature="rustc-serialize")] pub mod rustc_serialize;
-#[cfg(feature="serde")] pub mod serde;
+pub mod serde;

From 1e334becda092577a2caecfb08b2eb03d2919ff4 Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 30 Apr 2017 20:22:04 -0600
Subject: [PATCH 05/11] Implemented from_json for InputValue.

---
 src/integrations/serde.rs | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/src/integrations/serde.rs b/src/integrations/serde.rs
index e8662949..6d298787 100644
--- a/src/integrations/serde.rs
+++ b/src/integrations/serde.rs
@@ -1,13 +1,42 @@
 use serde::{de, ser};
 use serde::ser::SerializeMap;
-use std::collections::HashMap;
 use std::fmt;
+use std::collections::HashMap;
 
 use ::{GraphQLError, Value};
 use ast::InputValue;
 use executor::ExecutionError;
 use parser::{ParseError, Spanning, SourcePosition};
 use validation::RuleError;
+use serde_json::Value as Json;
+
+
+impl InputValue {
+    /// Converts serde_json::Value to juniper::InputValue
+    pub fn from_json(json: Json) -> InputValue {
+        match json {
+          Json::Number(num) => {
+              if let Some(number) = num.as_i64() {
+                  InputValue::int(number)
+              }
+              else if let Some(number) = num.as_f64() {
+                  InputValue::float(number)
+              }
+              else if let Some(number) = num.as_u64() {
+                InputValue::float(number as f64)
+              }
+              else {
+                  panic!("Invalid number data type was found.");
+              }
+          }
+          Json::String(s) => InputValue::string(s),
+          Json::Bool(b) => InputValue::boolean(b),
+          Json::Array(a) => InputValue::list(a.into_iter().map(InputValue::from_json).collect()),
+          Json::Object(o) => InputValue::object(o.into_iter().map(|(k, v)| (k, InputValue::from_json(v))).collect()),
+          Json::Null => InputValue::null(),
+       }
+    }
+}
 
 impl ser::Serialize for ExecutionError {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

From 6c4b329848099bbcd39f786df5bdcd55eede7268 Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 14 May 2017 19:30:49 -0600
Subject: [PATCH 06/11] Added serde_derive and urlencoded as dependecies.

---
 Cargo.toml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index 44b35bb7..9600688a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,13 +18,15 @@ path = "benches/bench.rs"
 [features]
 default = ["serde", "serde_json"]
 nightly = []
-iron-handlers = ["iron", "serde", "serde_json"]
+iron-handlers = ["iron", "default", "serde_derive", "urlencoded"]
 expose-test-schema = []
 
 [dependencies]
 iron = { version = "^0.5.1", optional = true }
 serde = { version = "^0.9.1", optional = true }
 serde_json = {version ="^0.9.1", optional = true }
+serde_derive = {version="^0.9.1", optional = true }
+urlencoded = {version="0.5", optional=true}
 
 
 [dev-dependencies]

From e890e9b4bded6844fea0343cbf162169684953fc Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 14 May 2017 19:33:37 -0600
Subject: [PATCH 07/11] Removed from_json, leveraged the already implemented
 serialization trait and added a serialization implementation for the graphql
 result.

---
 src/integrations/serde.rs | 111 ++++++++++++++++++++++++++++----------
 1 file changed, 82 insertions(+), 29 deletions(-)

diff --git a/src/integrations/serde.rs b/src/integrations/serde.rs
index 6d298787..c4e09269 100644
--- a/src/integrations/serde.rs
+++ b/src/integrations/serde.rs
@@ -3,41 +3,13 @@ use serde::ser::SerializeMap;
 use std::fmt;
 use std::collections::HashMap;
 
-use ::{GraphQLError, Value};
+use ::{GraphQLError, Value, Variables};
 use ast::InputValue;
 use executor::ExecutionError;
 use parser::{ParseError, Spanning, SourcePosition};
 use validation::RuleError;
-use serde_json::Value as Json;
 
 
-impl InputValue {
-    /// Converts serde_json::Value to juniper::InputValue
-    pub fn from_json(json: Json) -> InputValue {
-        match json {
-          Json::Number(num) => {
-              if let Some(number) = num.as_i64() {
-                  InputValue::int(number)
-              }
-              else if let Some(number) = num.as_f64() {
-                  InputValue::float(number)
-              }
-              else if let Some(number) = num.as_u64() {
-                InputValue::float(number as f64)
-              }
-              else {
-                  panic!("Invalid number data type was found.");
-              }
-          }
-          Json::String(s) => InputValue::string(s),
-          Json::Bool(b) => InputValue::boolean(b),
-          Json::Array(a) => InputValue::list(a.into_iter().map(InputValue::from_json).collect()),
-          Json::Object(o) => InputValue::object(o.into_iter().map(|(k, v)| (k, InputValue::from_json(v))).collect()),
-          Json::Null => InputValue::null(),
-       }
-    }
-}
-
 impl ser::Serialize for ExecutionError {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: ser::Serializer,
@@ -242,3 +214,84 @@ impl ser::Serialize for Value {
         }
     }
 }
+
+/// The expected structure of the decoded JSON Document for either Post or Get requests.
+#[cfg(feature="iron-handlers")]
+#[derive(Deserialize)]
+pub struct GraphQlQuery {
+    query: String,
+    #[serde(rename = "operationName")]
+    operation_name: Option<String>,
+    variables: Option<InputValue>
+}
+
+impl GraphQlQuery {
+
+    pub fn new(query: String,
+               operation_name: Option<String>,
+               variables: Option<InputValue>
+              ) ->  GraphQlQuery {
+                GraphQlQuery {
+                    query: query,
+                    operation_name: operation_name,
+                    variables: variables
+                }
+           }
+
+    pub fn query(&self) -> &str {
+        self.query.as_str()
+    }
+
+    pub fn operation_name(&self) -> Option<&str> {
+        self.operation_name.as_ref().map(|oper_name| &**oper_name)
+    }
+
+    pub fn variables(&self) -> Variables {
+        self.variables.as_ref().and_then(|iv| {
+          iv.to_object_value().map(|o| {
+              o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect()
+          })
+      }).unwrap_or_default()
+    }
+
+}
+
+
+#[cfg(feature="iron-handlers")]
+pub struct WrappedGraphQLResult<'a>(Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>);
+
+impl<'a> WrappedGraphQLResult<'a> {
+    pub fn new(result: Result<(Value, Vec<ExecutionError>),
+               GraphQLError<'a>>
+           ) -> WrappedGraphQLResult<'a> {
+        WrappedGraphQLResult(result)
+    }
+}
+
+impl<'a> ser::Serialize for WrappedGraphQLResult<'a> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where S: ser::Serializer,
+    {
+        match self.0 {
+            Ok((ref res, ref err)) => {
+                let mut map = try!(serializer.serialize_map(None));
+
+                try!(map.serialize_key("data"));
+                try!(map.serialize_value(res));
+
+                if !err.is_empty() {
+                    try!(map.serialize_key("errors"));
+                    try!(map.serialize_value(err));
+                }
+
+                map.end()
+            },
+            Err(ref err) => {
+                let mut map = try!(serializer.serialize_map(Some(1)));
+                try!(map.serialize_key("errors"));
+                try!(map.serialize_value(err));
+                map.end()
+            },
+        }
+    }
+}

From 998faec4cb098a68a9b49b94f5e599b1ed24d4ed Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 14 May 2017 19:34:34 -0600
Subject: [PATCH 08/11] Removed iexpect macro, added urlencoded and
 serde_derive dependencies.

---
 src/lib.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/lib.rs b/src/lib.rs
index 919f3887..0e0bc631 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -186,10 +186,12 @@ built-in [GraphiQL][6] handler included.
 #![cfg_attr(feature="nightly", feature(test))]
 #![warn(missing_docs)]
 #[cfg(feature="nightly")] extern crate test;
-#[cfg(feature="iron-handlers")] #[macro_use(itry, iexpect)] extern crate iron;
+#[cfg(feature="iron-handlers")] #[macro_use(itry)] extern crate iron;
+#[cfg(feature="iron-handlers")] extern crate urlencoded;
 #[cfg(test)] extern crate iron_test;
 extern crate serde;
 extern crate serde_json;
+#[cfg(feature="iron-handlers")] #[macro_use] extern crate serde_derive;
 
 #[macro_use] mod macros;
 mod ast;

From d3b1433748288802f386343e69eed8073296a9bd Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 14 May 2017 19:37:34 -0600
Subject: [PATCH 09/11] Added more support for get requests and the get and
 post handlers leverage the GraphQlQuery struct. Added a couple more tests for
 get requests.

---
 src/integrations/iron_handlers.rs | 290 +++++++++++++++++++-----------
 1 file changed, 187 insertions(+), 103 deletions(-)

diff --git a/src/integrations/iron_handlers.rs b/src/integrations/iron_handlers.rs
index a4f363eb..87f9776a 100644
--- a/src/integrations/iron_handlers.rs
+++ b/src/integrations/iron_handlers.rs
@@ -1,25 +1,23 @@
 //! Optional handlers for the Iron framework. Requires the `iron-handlers` feature enabled.
-
 use iron::prelude::*;
 use iron::middleware::Handler;
 use iron::mime::Mime;
 use iron::status;
 use iron::method;
-use iron::url::Url;
+use urlencoded::{UrlEncodedQuery, UrlDecodingError};
 
 use std::io::Read;
 use std::io::Error as IoError;
 use std::io::ErrorKind;
-use std::collections::HashMap;
 use std::error::Error;
 use std::fmt;
 use std::boxed::Box;
 
 use serde_json;
-use serde_json::Value as Json;
 use serde_json::error::Error as SerdeError;
 
-use ::{InputValue, GraphQLType, RootNode, Variables, execute as execute_query};
+use ::{InputValue, GraphQLType, RootNode, execute};
+use super::serde::{WrappedGraphQLResult, GraphQlQuery};
 
 /// Handler that executes GraphQL queries in the given schema
 ///
@@ -46,6 +44,53 @@ pub struct GraphiQLHandler {
     graphql_url: String,
 }
 
+
+/// Get queries are allowed to repeat the same key more than once.
+fn check_for_repeat_keys(params: &Vec<String>) -> Result<(), IronError> {
+    if params.len() > 1 {
+        let error = IronError::new(
+            Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
+                                                        "Was able parse a query string \
+                                                        but a duplicate uri key was \
+                                                        found."))),
+                    (status::BadRequest, "Duplicate uri key was found."));
+        Err(error)
+    }
+    else {
+        Ok(())
+    }
+}
+
+fn parse_url_param(param: Option<Vec<String>>) -> Result<Option<String>, IronError> {
+    if let Some(values) = param {
+            check_for_repeat_keys(&values)?;
+            Ok(Some(values[0].to_owned()))
+    }
+    else {
+        Ok(None)
+    }
+}
+
+fn parse_variable_param(param: Option<Vec<String>>) -> Result<Option<InputValue>, IronError> {
+    if let Some(values) = param {
+        check_for_repeat_keys(&values)?;
+        match serde_json::from_str::<InputValue>(values[0].as_ref()) {
+            Ok(input_values) => {
+                Ok(Some(input_values))
+            }
+            Err(err) => {
+                Err(IronError::new(
+                    Box::new(GraphQlIronError::Serde(err)),
+                    (status::BadRequest, "No JSON object was decoded.")))
+            }
+        }
+    }
+    else {
+        Ok(None)
+    }
+}
+
+
 impl<'a, CtxFactory, Query, Mutation, CtxT>
     GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
     where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
@@ -67,96 +112,62 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
     }
 
 
-    fn handle_get(&self, req: &mut Request) -> IronResult<Response> {
-        let url: Url = req.url.clone().into();
-
-        let mut query = None;
-        let variables = Variables::new();
-
-        for (k, v) in url.query_pairs() {
-            if k == "query" {
-                query = Some(v.into_owned());
-            }
-        }
-
-        let query = iexpect!(query);
-
-        self.execute(req, &query, &variables)
-    }
-
-    fn handle_post(&self, req: &mut Request) -> IronResult<Response> {
-        let mut request_payload = String::new();
-        itry!(req.body.read_to_string(&mut request_payload));
-        let json_data =
-            match serde_json::from_str::<Json>(&*request_payload) {
-                Ok(json) => json,
-                Err(err) => {
-                    let error = IronError::new(
-                        Box::new(GraphQlIronError::Serde(err)),
-                        (status::BadRequest, "No JSON object was decoded."));
-                    return Err(error)
+    fn handle_get(&self, req: &mut Request) -> IronResult<GraphQlQuery> {
+         match req.get_mut::<UrlEncodedQuery>() {
+            Ok(ref mut query_string) => {
+                let input_query = parse_url_param(query_string.remove("query").to_owned())?;
+                if let Some(query) = input_query {
+                    let operation_name =
+                        parse_url_param(query_string.remove("operationName"))?;
+                    let input_variables =
+                        parse_variable_param(query_string.remove("variables"))?;
+                        Ok(GraphQlQuery::new(query,operation_name,input_variables))
+                } else {
+                    Err(IronError::new(
+                        Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
+                                                                    "No query key was found in \
+                                                                    the Get request."))),
+                                (status::BadRequest, "No query was provided.")))
                 }
-            };
-        match json_data {
-            Json::Object(json_obj) => {
-                let mut query = None;
-                let mut variables = Variables::new();
-                for (k, v) in json_obj {
-                    if k == "query" {
-                        query = v.as_str().map(|query| query.to_owned());
-                    }
-                    else if k == "variables" {
-                        variables = InputValue::from_json(v).to_object_value()
-                            .map(|o| o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect())
-                            .unwrap_or_default();
-                    }
-                }
-                let query = iexpect!(query);
-                self.execute(req, &query, &variables)
-            }
-            _ => {
-                let error = IronError::new(
-                    Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
-                                                                "Was able parse a JSON item but it\
-                                                                 was not an object as expected."))),
-                            (status::BadRequest, "No JSON object was decoded."));
-                Err(error)
-            }
-        }
-    }
-
-    fn execute(&self, req: &mut Request, query: &str, variables: &Variables) -> IronResult<Response> {
-        let context = (self.context_factory)(req);
-        let result = execute_query(query, None, &self.root_node, variables, &context);
-        let content_type = "application/json".parse::<Mime>().unwrap();
-        let mut map = HashMap::new();
-
-        match result {
-            Ok((result, errors)) => {
-                let response_data = serde_json::to_value(result)
-                    .expect("Failed to convert response data to JSON.");
-                map.insert("data".to_owned(), response_data);
-
-                if !errors.is_empty() {
-                    let response_data = serde_json::to_value(errors)
-                        .expect("Failed to convert the errors to JSON.");
-                    map.insert("errors".to_owned(), response_data);
-                }
-                let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
-                let json = serde_json::to_string_pretty(&data).expect("Failed to convert response to JSON.");
-                Ok(Response::with((content_type, status::Ok, json)))
             }
             Err(err) => {
-                let response_data = serde_json::to_value(err)
-                    .expect("Failed to convert error data to JSON.");
-                map.insert("errors".to_owned(), response_data);
-                let data = serde_json::to_value(map).expect("Failed to convert response to JSON");
-                let json = serde_json::to_string_pretty(&data)
-                    .expect("Failed to convert response to JSON");
-                Ok(Response::with((content_type, status::BadRequest, json.to_string())))
+                Err(IronError::new(
+                    Box::new(GraphQlIronError::Url(err)),
+                            (status::BadRequest, "No JSON object was decoded.")))
             }
         }
     }
+
+    fn handle_post(&self, req: &mut Request) -> IronResult<GraphQlQuery> {
+        let mut request_payload = String::new();
+        itry!(req.body.read_to_string(&mut request_payload));
+        let graphql_query = serde_json::from_str::<GraphQlQuery>(request_payload.as_str()).map_err(|err|{
+            IronError::new(
+                Box::new(GraphQlIronError::Serde(err)),
+                (status::BadRequest, "No JSON object was decoded."))
+        });
+        graphql_query
+    }
+
+    fn respond(&self, req: &mut Request, graphql: GraphQlQuery) -> IronResult<Response> {
+       let context = (self.context_factory)(req);
+       let variables = graphql.variables();
+       let result = execute(graphql.query(),
+                            graphql.operation_name(),
+                            &self.root_node,
+                            &variables,
+                            &context);
+      let content_type = "application/json".parse::<Mime>().unwrap();
+      if result.is_ok() {
+          let response = WrappedGraphQLResult::new(result);
+          let json = serde_json::to_string_pretty(&response).unwrap();
+          Ok(Response::with((content_type, status::Ok, json)))
+      } else {
+          let response = WrappedGraphQLResult::new(result);
+          let json = serde_json::to_string_pretty(&response).unwrap();
+          Ok(Response::with((content_type, status::BadRequest, json)))
+      }
+   }
 }
 
 impl GraphiQLHandler {
@@ -179,10 +190,16 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
           Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
           Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, 'a: 'static,
 {
-    fn handle(&self, req: &mut Request) -> IronResult<Response> {
+    fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
         match req.method {
-            method::Get => self.handle_get(req),
-            method::Post => self.handle_post(req),
+            method::Get =>  {
+                let graphql_query = self.handle_get(&mut req)?;
+                self.respond(&mut req, graphql_query)
+            }
+            method::Post => {
+                let graphql_query = self.handle_post(&mut req)?;
+                self.respond(&mut req, graphql_query)
+            },
             _ => Ok(Response::with((status::MethodNotAllowed)))
         }
     }
@@ -202,7 +219,6 @@ impl Handler for GraphiQLHandler {
             }
         </style>
         "#;
-
         let fetcher_source = r#"
         <script>
             function graphQLFetcher(params) {
@@ -260,11 +276,14 @@ impl Handler for GraphiQLHandler {
 }
 
 /// A general error allowing the developer to see the underlying issue.
+#[derive(Debug)]
 pub enum GraphQlIronError {
     ///Captures any errors that were caused by Serde.
     Serde(SerdeError),
     /// Captures any error related the IO.
-    IO(IoError)
+    IO(IoError),
+    /// Captures any error related to Url Decoding,
+    Url(UrlDecodingError)
 }
 
 impl fmt::Display for GraphQlIronError {
@@ -272,15 +291,7 @@ impl fmt::Display for GraphQlIronError {
         match *self {
             GraphQlIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
             GraphQlIronError::IO(ref err) => fmt::Display::fmt(err, &mut f),
-        }
-    }
-}
-
-impl fmt::Debug for GraphQlIronError {
-    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            GraphQlIronError::Serde(ref err) => fmt::Debug::fmt(err, &mut f),
-            GraphQlIronError::IO(ref err) => fmt::Debug::fmt(err, &mut f),
+            GraphQlIronError::Url(ref err) => fmt::Display::fmt(err, &mut f),
         }
     }
 }
@@ -294,6 +305,9 @@ impl Error for GraphQlIronError {
            GraphQlIronError::IO(ref err) => {
                err.description()
            }
+           GraphQlIronError::Url(ref err) => {
+               err.description()
+           }
        }
    }
 
@@ -305,6 +319,9 @@ impl Error for GraphQlIronError {
            GraphQlIronError::IO(ref err) => {
                err.cause()
            }
+           GraphQlIronError::Url(ref err) => {
+               err.cause()
+           }
        }
    }
 }
@@ -363,6 +380,73 @@ mod tests {
                 .expect("Invalid JSON constant in test"));
     }
 
+    #[test]
+    fn test_encoded_get() {
+        let response = request::get(
+            "http://localhost:3000/?query=query%20{%20%20%20human(id:%20\"1000\")%20{%20%20%20%20%20id,%20%20%20%20%20name,%20%20%20%20%20appearsIn,%20%20%20%20%20homePlanet%20%20%20}%20}",
+            Headers::new(),
+            &make_handler())
+            .expect("Unexpected IronError");
+
+        assert_eq!(response.status, Some(status::Ok));
+        assert_eq!(response.headers.get::<headers::ContentType>(),
+                   Some(&headers::ContentType::json()));
+
+        let json = unwrap_json_response(response);
+
+        assert_eq!(
+            json,
+            serde_json::from_str::<Json>(r#"{
+                    "data": {
+                        "human": {
+                            "appearsIn": [
+                                "NEW_HOPE",
+                                "EMPIRE",
+                                "JEDI"
+                                ],
+                                "homePlanet": "Tatooine",
+                                "name": "Luke Skywalker",
+                                "id": "1000"
+                            }
+                        }
+                    }"#)
+                .expect("Invalid JSON constant in test"));
+    }
+
+    #[test]
+    fn test_get_with_variables() {
+        let response = request::get(
+            "http://localhost:3000/?query=query($id:%20String!)%20{%20%20%20human(id:%20$id)%20{%20%20%20%20%20id,%20%20%20%20%20name,%20%20%20%20%20appearsIn,%20%20%20%20%20homePlanet%20%20%20}%20}&variables={%20%20%20\"id\":%20%20\"1000\"%20}",
+            Headers::new(),
+            &make_handler())
+            .expect("Unexpected IronError");
+
+        assert_eq!(response.status, Some(status::Ok));
+        assert_eq!(response.headers.get::<headers::ContentType>(),
+                   Some(&headers::ContentType::json()));
+
+        let json = unwrap_json_response(response);
+
+        assert_eq!(
+            json,
+            serde_json::from_str::<Json>(r#"{
+                    "data": {
+                        "human": {
+                            "appearsIn": [
+                                "NEW_HOPE",
+                                "EMPIRE",
+                                "JEDI"
+                                ],
+                                "homePlanet": "Tatooine",
+                                "name": "Luke Skywalker",
+                                "id": "1000"
+                            }
+                        }
+                    }"#)
+                .expect("Invalid JSON constant in test"));
+    }
+
+
     #[test]
     fn test_simple_post() {
         let response = request::post(

From ba15579531b41119320d7d906bcde3d80448f20e Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Sun, 14 May 2017 20:10:46 -0600
Subject: [PATCH 10/11] Added conditional compliation logic.

---
 src/integrations/serde.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/integrations/serde.rs b/src/integrations/serde.rs
index c4e09269..3595b9b5 100644
--- a/src/integrations/serde.rs
+++ b/src/integrations/serde.rs
@@ -3,7 +3,9 @@ use serde::ser::SerializeMap;
 use std::fmt;
 use std::collections::HashMap;
 
-use ::{GraphQLError, Value, Variables};
+use ::{GraphQLError, Value};
+#[cfg(feature="iron-handlers")]
+use ::Variables;
 use ast::InputValue;
 use executor::ExecutionError;
 use parser::{ParseError, Spanning, SourcePosition};
@@ -225,6 +227,7 @@ pub struct GraphQlQuery {
     variables: Option<InputValue>
 }
 
+#[cfg(feature="iron-handlers")]
 impl GraphQlQuery {
 
     pub fn new(query: String,
@@ -260,6 +263,7 @@ impl GraphQlQuery {
 #[cfg(feature="iron-handlers")]
 pub struct WrappedGraphQLResult<'a>(Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>);
 
+#[cfg(feature="iron-handlers")]
 impl<'a> WrappedGraphQLResult<'a> {
     pub fn new(result: Result<(Value, Vec<ExecutionError>),
                GraphQLError<'a>>
@@ -268,6 +272,7 @@ impl<'a> WrappedGraphQLResult<'a> {
     }
 }
 
+#[cfg(feature="iron-handlers")]
 impl<'a> ser::Serialize for WrappedGraphQLResult<'a> {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: ser::Serializer,

From 80de43e551b74377021b6b342f1cd7f5f3cbbe26 Mon Sep 17 00:00:00 2001
From: rpiper <asterisktheserver@gmail.com>
Date: Mon, 22 May 2017 08:24:31 -0600
Subject: [PATCH 11/11] Drop support for rust 1.13 and 1.14

---
 .travis.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 67aad4ab..55bd8f51 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,8 +7,8 @@ rust:
   - nightly
 
   # The two most recent stable releases before "stable"
-  - 1.13.0
-  - 1.14.0
+  - 1.15.0
+  - 1.16.0
 
 matrix:
   allow_failures: