From aa056dd562605eaeb46f014cebe09a1f7e62b32d Mon Sep 17 00:00:00 2001 From: Michael Macias Date: Wed, 4 Jan 2017 20:46:33 -0600 Subject: [PATCH] Add optional Serde serialization support This adds the Serde serialization trait to the following: * `ExecutionError` * `GraphQLError` * `InputValue` (+ deserialization) * `RuleError` * `SourcePosition` * `Spanning` * `Value` Serde support is enabled via the "serde" feature flag. --- Cargo.toml | 1 + src/integrations/mod.rs | 1 + src/integrations/serde.rs | 206 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 209 insertions(+) create mode 100644 src/integrations/serde.rs diff --git a/Cargo.toml b/Cargo.toml index a991fad6..178441b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ expose-test-schema = [] [dependencies] rustc-serialize = "^0.3.19" iron = { version = "^0.4.0", optional = true } +serde = { version = "^0.8.21", optional = true } [dev-dependencies] iron = "^0.4.0" diff --git a/src/integrations/mod.rs b/src/integrations/mod.rs index db7091e7..19a36c61 100644 --- a/src/integrations/mod.rs +++ b/src/integrations/mod.rs @@ -1 +1,2 @@ #[cfg(feature="iron-handlers")] pub mod iron_handlers; +#[cfg(feature="serde")] pub mod serde; diff --git a/src/integrations/serde.rs b/src/integrations/serde.rs new file mode 100644 index 00000000..002d7d9b --- /dev/null +++ b/src/integrations/serde.rs @@ -0,0 +1,206 @@ +use serde::{de, ser}; +use std::collections::HashMap; + +use ::{GraphQLError, Value}; +use ast::InputValue; +use executor::ExecutionError; +use parser::{ParseError, Spanning, SourcePosition}; +use validation::RuleError; + +impl ser::Serialize for ExecutionError { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + let mut state = try!(serializer.serialize_map(Some(3))); + + try!(serializer.serialize_map_key(&mut state, "message")); + try!(serializer.serialize_map_value(&mut state, self.message())); + + try!(serializer.serialize_map_key(&mut state, "locations")); + try!(serializer.serialize_map_value(&mut state, vec![self.location()])); + + try!(serializer.serialize_map_key(&mut state, "path")); + try!(serializer.serialize_map_value(&mut state, self.path())); + + serializer.serialize_map_end(state) + } +} + +impl<'a> ser::Serialize for GraphQLError<'a> { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + match *self { + GraphQLError::ParseError(ref err) => vec![err].serialize(serializer), + GraphQLError::ValidationError(ref errs) => errs.serialize(serializer), + GraphQLError::NoOperationProvided => { + serializer.serialize_str("Must provide an operation") + }, + GraphQLError::MultipleOperationsProvided => { + serializer.serialize_str("Must provide operation name if query contains multiple operations") + }, + GraphQLError::UnknownOperationName => { + serializer.serialize_str("Unknown operation") + }, + } + } +} + +impl de::Deserialize for InputValue { + fn deserialize(deserializer: &mut D) -> Result + where D: de::Deserializer, + { + struct InputValueVisitor; + + impl de::Visitor for InputValueVisitor { + type Value = InputValue; + + fn visit_bool(&mut self, value: bool) -> Result { + Ok(InputValue::boolean(value)) + } + + fn visit_i64(&mut self, value: i64) -> Result { + Ok(InputValue::int(value)) + } + + fn visit_u64(&mut self, value: u64) -> Result + where E: de::Error, + { + self.visit_f64(value as f64) + } + + fn visit_f64(&mut self, value: f64) -> Result { + Ok(InputValue::float(value)) + } + + fn visit_str(&mut self, value: &str) -> Result + where E: de::Error, + { + self.visit_string(value.into()) + } + + fn visit_string(&mut self, value: String) -> Result { + Ok(InputValue::string(value)) + } + + fn visit_none(&mut self) -> Result { + Ok(InputValue::null()) + } + + fn visit_unit(&mut self) -> Result { + Ok(InputValue::null()) + } + + fn visit_seq(&mut self, visitor: V) -> Result + where V: de::SeqVisitor, + { + let values = try!(de::impls::VecVisitor::new().visit_seq(visitor)); + Ok(InputValue::list(values)) + } + + fn visit_map(&mut self, visitor: V) -> Result + where V: de::MapVisitor, + { + let values = try!(de::impls::HashMapVisitor::::new() + .visit_map(visitor)); + Ok(InputValue::object(values)) + } + } + + deserializer.deserialize(InputValueVisitor) + } +} + +impl ser::Serialize for InputValue { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + match *self { + InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(), + InputValue::Int(v) => serializer.serialize_i64(v), + InputValue::Float(v) => serializer.serialize_f64(v), + InputValue::String(ref v) | InputValue::Enum(ref v) => serializer.serialize_str(v), + InputValue::Boolean(v) => serializer.serialize_bool(v), + InputValue::List(ref v) => { + v.iter().map(|x| x.item.clone()).collect::>().serialize(serializer) + }, + InputValue::Object(ref v) => { + v.iter() + .map(|&(ref k, ref v)| (k.item.clone(), v.item.clone())) + .collect::>() + .serialize(serializer) + }, + } + } +} + +impl ser::Serialize for RuleError { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + let mut state = try!(serializer.serialize_map(Some(2))); + + try!(serializer.serialize_map_key(&mut state, "message")); + try!(serializer.serialize_map_value(&mut state, self.message())); + + try!(serializer.serialize_map_key(&mut state, "locations")); + try!(serializer.serialize_map_value(&mut state, self.locations())); + + serializer.serialize_map_end(state) + } +} + +impl ser::Serialize for SourcePosition { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + let mut state = try!(serializer.serialize_map(Some(2))); + + try!(serializer.serialize_map_key(&mut state, "line")); + try!(serializer.serialize_map_value(&mut state, self.line() + 1)); + + try!(serializer.serialize_map_key(&mut state, "column")); + try!(serializer.serialize_map_value(&mut state, self.column() + 1)); + + serializer.serialize_map_end(state) + } +} + +impl<'a> ser::Serialize for Spanning> { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + let mut state = try!(serializer.serialize_map(Some(2))); + + let message = format!("{}", self.item); + try!(serializer.serialize_map_key(&mut state, "message")); + try!(serializer.serialize_map_value(&mut state, message)); + + let mut location = HashMap::new(); + location.insert("line".to_owned(), self.start.line() + 1); + location.insert("column".to_owned(), self.start.column() + 1); + + let locations = vec![location]; + + try!(serializer.serialize_map_key(&mut state, "locations")); + try!(serializer.serialize_map_value(&mut state, locations)); + + serializer.serialize_map_end(state) + } +} + +impl ser::Serialize for Value { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer, + { + match *self { + Value::Null => serializer.serialize_unit(), + Value::Int(v) => serializer.serialize_i64(v), + Value::Float(v) => serializer.serialize_f64(v), + Value::String(ref v) => serializer.serialize_str(v), + Value::Boolean(v) => serializer.serialize_bool(v), + Value::List(ref v) => v.serialize(serializer), + Value::Object(ref v) => v.serialize(serializer), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 1cd439ca..3e5f6c82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,6 +187,7 @@ built-in [GraphiQL][6] handler included. #![warn(missing_docs)] 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;