diff --git a/integration_tests/juniper_tests/Cargo.toml b/integration_tests/juniper_tests/Cargo.toml index e21f60f44..0a6ce2c1c 100644 --- a/integration_tests/juniper_tests/Cargo.toml +++ b/integration_tests/juniper_tests/Cargo.toml @@ -12,6 +12,7 @@ juniper_subscriptions = { path = "../../juniper_subscriptions" } [dev-dependencies] async-trait = "0.1.39" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" fnv = "1.0" tokio = { version = "1", features = ["rt", "macros", "time"] } diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index a1703a6f7..9ba6cf77b 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -1,15 +1,16 @@ -use std::{fmt, pin::Pin}; +use std::{convert::TryInto as _, fmt, pin::Pin}; use futures::{stream, Stream}; use juniper::{ execute, graphql_object, graphql_scalar, graphql_subscription, parser::{ParseError, ScalarToken, Spanning, Token}, - serde::de, + serde::{de, Deserialize, Deserializer, Serialize}, EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult, RootNode, ScalarValue, Value, Variables, }; -#[derive(GraphQLScalarValue, Clone, Debug, PartialEq)] +#[derive(GraphQLScalarValue, Clone, Debug, PartialEq, Serialize)] +#[serde(untagged)] pub(crate) enum MyScalarValue { Int(i32), Long(i64), @@ -19,18 +20,16 @@ pub(crate) enum MyScalarValue { } impl ScalarValue for MyScalarValue { - type Visitor = MyScalarValueVisitor; - fn as_int(&self) -> Option { - match *self { - Self::Int(ref i) => Some(*i), + match self { + Self::Int(i) => Some(*i), _ => None, } } fn as_string(&self) -> Option { - match *self { - Self::String(ref s) => Some(s.clone()), + match self { + Self::String(s) => Some(s.clone()), _ => None, } } @@ -43,100 +42,92 @@ impl ScalarValue for MyScalarValue { } fn as_str(&self) -> Option<&str> { - match *self { - Self::String(ref s) => Some(s.as_str()), + match self { + Self::String(s) => Some(s.as_str()), _ => None, } } fn as_float(&self) -> Option { - match *self { - Self::Int(ref i) => Some(f64::from(*i)), - Self::Float(ref f) => Some(*f), + match self { + Self::Int(i) => Some(f64::from(*i)), + Self::Float(f) => Some(*f), _ => None, } } fn as_boolean(&self) -> Option { - match *self { - Self::Boolean(ref b) => Some(*b), + match self { + Self::Boolean(b) => Some(*b), _ => None, } } } -#[derive(Debug, Default)] -pub(crate) struct MyScalarValueVisitor; - -impl<'de> de::Visitor<'de> for MyScalarValueVisitor { - type Value = MyScalarValue; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid input value") - } - - fn visit_bool(self, value: bool) -> Result { - Ok(MyScalarValue::Boolean(value)) - } - - fn visit_i32(self, value: i32) -> Result - where - E: de::Error, - { - Ok(MyScalarValue::Int(value)) - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - if value <= i64::from(i32::max_value()) { - self.visit_i32(value as i32) - } else { - Ok(MyScalarValue::Long(value)) - } - } - - fn visit_u32(self, value: u32) -> Result - where - E: de::Error, - { - if value <= i32::max_value() as u32 { - self.visit_i32(value as i32) - } else { - self.visit_u64(value as u64) - } - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value <= i64::max_value() as u64 { - self.visit_i64(value as i64) - } else { - // Browser's JSON.stringify serialize all numbers having no - // fractional part as integers (no decimal point), so we - // must parse large integers as floating point otherwise - // we would error on transferring large floating point - // numbers. - Ok(MyScalarValue::Float(value as f64)) +impl<'de> Deserialize<'de> for MyScalarValue { + fn deserialize>(de: D) -> Result { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = MyScalarValue; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a valid input value") + } + + fn visit_bool(self, b: bool) -> Result { + Ok(MyScalarValue::Boolean(b)) + } + + fn visit_i32(self, n: i32) -> Result { + Ok(MyScalarValue::Int(n)) + } + + fn visit_i64(self, b: i64) -> Result { + if b <= i64::from(i32::MAX) { + self.visit_i32(b.try_into().unwrap()) + } else { + Ok(MyScalarValue::Long(b)) + } + } + + fn visit_u32(self, n: u32) -> Result { + if n <= i32::MAX as u32 { + self.visit_i32(n.try_into().unwrap()) + } else { + self.visit_u64(n.into()) + } + } + + fn visit_u64(self, n: u64) -> Result { + if n <= i64::MAX as u64 { + self.visit_i64(n.try_into().unwrap()) + } else { + // Browser's `JSON.stringify()` serializes all numbers + // having no fractional part as integers (no decimal point), + // so we must parse large integers as floating point, + // otherwise we would error on transferring large floating + // point numbers. + // TODO: Use `FloatToInt` conversion once stabilized: + // https://github.com/rust-lang/rust/issues/67057 + Ok(MyScalarValue::Float(n as f64)) + } + } + + fn visit_f64(self, f: f64) -> Result { + Ok(MyScalarValue::Float(f)) + } + + fn visit_str(self, s: &str) -> Result { + self.visit_string(s.into()) + } + + fn visit_string(self, s: String) -> Result { + Ok(MyScalarValue::String(s)) + } } - } - - fn visit_f64(self, value: f64) -> Result { - Ok(MyScalarValue::Float(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - self.visit_string(value.into()) - } - fn visit_string(self, value: String) -> Result { - Ok(MyScalarValue::String(value)) + de.deserialize_any(Visitor) } } @@ -169,7 +160,7 @@ struct TestType; #[graphql_object(scalar = MyScalarValue)] impl TestType { fn long_field() -> i64 { - i64::from(i32::max_value()) + 1 + i64::from(i32::MAX) + 1 } fn long_with_arg(long_arg: i64) -> i64 { diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 60a900880..f3db543ec 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -2,15 +2,17 @@ ## Breaking Changes -- `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971) -- `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#971](https://github.com/graphql-rust/juniper/pull/971) -- Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979) +- Replaced `Visitor` associated type with `DeserializeOwned` requirement in `ScalarValue` trait. ([#985](https://github.com/graphql-rust/juniper/pull/985)) +- Removed `Serialize` implementation from `#[derive(GraphQLScalarValue)]`macro, now should be provided explicitly. ([#985](https://github.com/graphql-rust/juniper/pull/985)) +- `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971)) +- `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#971](https://github.com/graphql-rust/juniper/pull/971)) +- Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979)) ## Features - Support using Rust array as GraphQL list. ([#966](https://github.com/graphql-rust/juniper/pull/966), [#918](https://github.com/graphql-rust/juniper/issues/918)) - Expose `GraphQLRequest` fields. ([#750](https://github.com/graphql-rust/juniper/issues/750)) -- `#[graphql_interface]` macro now supports `rename_all = ""` argument influencing its fields and their arguments. ([#971](https://github.com/graphql-rust/juniper/pull/971) +- `#[graphql_interface]` macro now supports `rename_all = ""` argument influencing its fields and their arguments. ([#971](https://github.com/graphql-rust/juniper/pull/971)) ## Fixes diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 06ac627d3..3ad050240 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -214,80 +214,53 @@ impl<'a> fmt::Display for Type<'a> { } } -impl InputValue -where - S: ScalarValue, -{ - /// Construct a null value. +impl InputValue { + /// Construct a `null` value. pub fn null() -> Self { - InputValue::Null - } - - /// Construct an integer value. - #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] - pub fn int(i: i32) -> Self { - Self::scalar(i) - } - - /// Construct a floating point value. - #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] - pub fn float(f: f64) -> Self { - Self::scalar(f) - } - - /// Construct a boolean value. - #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] - pub fn boolean(b: bool) -> Self { - Self::scalar(b) - } - - /// Construct a string value. - #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] - pub fn string>(s: T) -> Self { - InputValue::scalar(s.as_ref().to_owned()) + Self::Null } /// Construct a scalar value pub fn scalar(v: T) -> Self where - T: Into, + S: From, { - InputValue::Scalar(v.into()) + Self::Scalar(v.into()) } /// Construct an enum value. pub fn enum_value>(s: T) -> Self { - InputValue::Enum(s.as_ref().to_owned()) + Self::Enum(s.as_ref().to_owned()) } /// Construct a variable value. pub fn variable>(v: T) -> Self { - InputValue::Variable(v.as_ref().to_owned()) + Self::Variable(v.as_ref().to_owned()) } - /// Construct an unlocated list. + /// Construct a [`Spanning::unlocated`] list. /// - /// Convenience function to make each `InputValue` in the input vector - /// not contain any location information. Can be used from `ToInputValue` + /// Convenience function to make each [`InputValue`] in the input vector + /// not contain any location information. Can be used from [`ToInputValue`] /// implementations, where no source code position information is available. pub fn list(l: Vec) -> Self { - InputValue::List(l.into_iter().map(Spanning::unlocated).collect()) + Self::List(l.into_iter().map(Spanning::unlocated).collect()) } /// Construct a located list. pub fn parsed_list(l: Vec>) -> Self { - InputValue::List(l) + Self::List(l) } - /// Construct an unlocated object. + /// Construct aa [`Spanning::unlocated`] object. /// - /// Similar to `InputValue::list`, it makes each key and value in the given - /// hash map not contain any location information. + /// Similarly to [`InputValue::list`] it makes each key and value in the + /// given hash map not contain any location information. pub fn object(o: IndexMap) -> Self where K: AsRef + Eq + Hash, { - InputValue::Object( + Self::Object( o.into_iter() .map(|(k, v)| { ( @@ -301,19 +274,22 @@ where /// Construct a located object. pub fn parsed_object(o: Vec<(Spanning, Spanning)>) -> Self { - InputValue::Object(o) + Self::Object(o) } /// Resolve all variables to their values. - pub fn into_const(self, vars: &Variables) -> Self { + pub fn into_const(self, vars: &Variables) -> Self + where + S: Clone, + { match self { - InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone), - InputValue::List(l) => InputValue::List( + Self::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone), + Self::List(l) => Self::List( l.into_iter() .map(|s| s.map(|v| v.into_const(vars))) .collect(), ), - InputValue::Object(o) => InputValue::Object( + Self::Object(o) => Self::Object( o.into_iter() .map(|(sk, sv)| (sk, sv.map(|v| v.into_const(vars)))) .collect(), @@ -322,7 +298,7 @@ where } } - /// Shorthand form of invoking `FromInputValue::from()`. + /// Shorthand form of invoking [`FromInputValue::from()`]. pub fn convert(&self) -> Option where T: FromInputValue, @@ -330,43 +306,52 @@ where >::from_input_value(self) } - /// Does the value represent null? + /// Does the value represent a `null`? pub fn is_null(&self) -> bool { - matches!(*self, InputValue::Null) + matches!(self, Self::Null) } /// Does the value represent a variable? pub fn is_variable(&self) -> bool { - matches!(*self, InputValue::Variable(_)) + matches!(self, Self::Variable(_)) } /// View the underlying enum value, if present. pub fn as_enum_value(&self) -> Option<&str> { - match *self { - InputValue::Enum(ref e) => Some(e), + match self { + Self::Enum(e) => Some(e.as_str()), _ => None, } } /// View the underlying int value, if present. - pub fn as_int_value(&self) -> Option { + pub fn as_int_value(&self) -> Option + where + S: ScalarValue, + { self.as_scalar_value().and_then(|s| s.as_int()) } /// View the underlying float value, if present. - pub fn as_float_value(&self) -> Option { + pub fn as_float_value(&self) -> Option + where + S: ScalarValue, + { self.as_scalar_value().and_then(|s| s.as_float()) } /// View the underlying string value, if present. - pub fn as_string_value(&self) -> Option<&str> { + pub fn as_string_value(&self) -> Option<&str> + where + S: ScalarValue, + { self.as_scalar_value().and_then(|s| s.as_str()) } /// View the underlying scalar value, if present. pub fn as_scalar(&self) -> Option<&S> { - match *self { - InputValue::Scalar(ref s) => Some(s), + match self { + Self::Scalar(s) => Some(s), _ => None, } } @@ -375,69 +360,71 @@ where pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T> where T: 'a, - &'a S: Into>, + Option<&'a T>: From<&'a S>, { self.as_scalar().and_then(Into::into) } - /// Convert the input value to an unlocated object value. + /// Converts this [`InputValue`] to a [`Spanning::unlocated`] object value. /// - /// This constructs a new IndexMap that contain references to the keys - /// and values in `self`. + /// This constructs a new [`IndexMap`] containing references to the keys + /// and values of `self`. pub fn to_object_value(&self) -> Option> { - match *self { - InputValue::Object(ref o) => Some( + match self { + Self::Object(o) => Some( o.iter() - .map(|&(ref sk, ref sv)| (sk.item.as_str(), &sv.item)) + .map(|(sk, sv)| (sk.item.as_str(), &sv.item)) .collect(), ), _ => None, } } - /// Convert the input value to an unlocated list value. + /// Converts this [`InputValue`] to a [`Spanning::unlocated`] list value. /// - /// This constructs a new vector that contain references to the values - /// in `self`. + /// This constructs a new [`Vec`] containing references to the values of + /// `self`. pub fn to_list_value(&self) -> Option> { - match *self { - InputValue::List(ref l) => Some(l.iter().map(|s| &s.item).collect()), + match self { + Self::List(l) => Some(l.iter().map(|s| &s.item).collect()), _ => None, } } - /// Recursively find all variables + /// Recursively finds all variables pub fn referenced_variables(&self) -> Vec<&str> { - match *self { - InputValue::Variable(ref name) => vec![name], - InputValue::List(ref l) => l + match self { + Self::Variable(name) => vec![name.as_str()], + Self::List(l) => l .iter() .flat_map(|v| v.item.referenced_variables()) .collect(), - InputValue::Object(ref obj) => obj + Self::Object(o) => o .iter() - .flat_map(|&(_, ref v)| v.item.referenced_variables()) + .flat_map(|(_, v)| v.item.referenced_variables()) .collect(), _ => vec![], } } - /// Compare equality with another `InputValue` ignoring any source position information. - pub fn unlocated_eq(&self, other: &Self) -> bool { - use crate::InputValue::*; - + /// Compares equality with another [`InputValue``] ignoring any source + /// position information. + pub fn unlocated_eq(&self, other: &Self) -> bool + where + S: PartialEq, + { match (self, other) { - (&Null, &Null) => true, - (&Scalar(ref s1), &Scalar(ref s2)) => s1 == s2, - (&Enum(ref s1), &Enum(ref s2)) | (&Variable(ref s1), &Variable(ref s2)) => s1 == s2, - (&List(ref l1), &List(ref l2)) => l1 + (Self::Null, Self::Null) => true, + (Self::Scalar(s1), Self::Scalar(s2)) => s1 == s2, + (Self::Enum(s1), Self::Enum(s2)) | (Self::Variable(s1), Self::Variable(s2)) => s1 == s2, + (Self::List(l1), Self::List(l2)) => l1 .iter() .zip(l2.iter()) .all(|(v1, v2)| v1.item.unlocated_eq(&v2.item)), - (&Object(ref o1), &Object(ref o2)) => { + (Self::Object(o1), Self::Object(o2)) => { o1.len() == o2.len() - && o1.iter().all(|&(ref sk1, ref sv1)| { - o2.iter().any(|&(ref sk2, ref sv2)| { + && o1.iter().all(|(sk1, sv1)| { + o2.iter().any(|(sk2, sv2)| { sk1.item == sk2.item && sv1.item.unlocated_eq(&sv2.item) }) }) @@ -447,23 +434,20 @@ where } } -impl fmt::Display for InputValue -where - S: ScalarValue, -{ +impl fmt::Display for InputValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - InputValue::Null => write!(f, "null"), - InputValue::Scalar(ref s) => { + match self { + Self::Null => write!(f, "null"), + Self::Scalar(s) => { if let Some(s) = s.as_str() { write!(f, "\"{}\"", s) } else { write!(f, "{}", s) } } - InputValue::Enum(ref v) => write!(f, "{}", v), - InputValue::Variable(ref v) => write!(f, "${}", v), - InputValue::List(ref v) => { + Self::Enum(v) => write!(f, "{}", v), + Self::Variable(v) => write!(f, "${}", v), + Self::List(v) => { write!(f, "[")?; for (i, spanning) in v.iter().enumerate() { @@ -475,7 +459,7 @@ where write!(f, "]") } - InputValue::Object(ref o) => { + Self::Object(o) => { write!(f, "{{")?; for (i, &(ref k, ref v)) in o.iter().enumerate() { diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index 72d796038..17420de3a 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -24,7 +24,7 @@ use crate::{ /// /// For GET, you will need to parse the query string and extract "query", /// "operationName", and "variables" manually. -#[derive(Deserialize, Clone, Serialize, PartialEq, Debug)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct GraphQLRequest where S: ScalarValue, @@ -37,7 +37,10 @@ where pub operation_name: Option, /// Optional variables to execute the GraphQL operation with. - #[serde(bound(deserialize = "InputValue: Deserialize<'de> + Serialize"))] + #[serde(bound( + deserialize = "InputValue: Deserialize<'de>", + serialize = "InputValue: Serialize", + ))] pub variables: Option>, } diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index d29638c4f..8954bb722 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -1,34 +1,27 @@ +use std::{ + convert::{TryFrom as _, TryInto as _}, + fmt, + marker::PhantomData, +}; + use indexmap::IndexMap; use serde::{ - de, - ser::{self, SerializeMap}, - Serialize, + de::{self, Deserializer, IntoDeserializer as _}, + ser::{SerializeMap as _, Serializer}, + serde_if_integer128, Deserialize, Serialize, }; -use std::fmt; - use crate::{ ast::InputValue, executor::ExecutionError, parser::{ParseError, SourcePosition, Spanning}, validation::RuleError, - GraphQLError, Object, ScalarValue, Value, + DefaultScalarValue, GraphQLError, Object, Value, }; -#[derive(Serialize)] -struct SerializeHelper { - message: &'static str, -} - -impl ser::Serialize for ExecutionError -where - T: ScalarValue, -{ - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - let mut map = serializer.serialize_map(Some(4))?; +impl Serialize for ExecutionError { + fn serialize(&self, ser: S) -> Result { + let mut map = ser.serialize_map(Some(4))?; map.serialize_key("message")?; map.serialize_value(self.error().message())?; @@ -49,274 +42,184 @@ where } } -impl<'a> ser::Serialize for GraphQLError<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - match *self { - GraphQLError::ParseError(ref err) => vec![err].serialize(serializer), - GraphQLError::ValidationError(ref errs) => errs.serialize(serializer), - GraphQLError::NoOperationProvided => [SerializeHelper { +impl<'a> Serialize for GraphQLError<'a> { + fn serialize(&self, ser: S) -> Result { + #[derive(Serialize)] + struct Helper { + message: &'static str, + } + + match self { + Self::ParseError(e) => [e].serialize(ser), + Self::ValidationError(es) => es.serialize(ser), + Self::NoOperationProvided => [Helper { message: "Must provide an operation", }] - .serialize(serializer), - GraphQLError::MultipleOperationsProvided => [SerializeHelper { + .serialize(ser), + Self::MultipleOperationsProvided => [Helper { message: "Must provide operation name \ if query contains multiple operations", }] - .serialize(serializer), - GraphQLError::UnknownOperationName => [SerializeHelper { + .serialize(ser), + Self::UnknownOperationName => [Helper { message: "Unknown operation", }] - .serialize(serializer), - GraphQLError::IsSubscription => [SerializeHelper { + .serialize(ser), + Self::IsSubscription => [Helper { message: "Expected query, got subscription", }] - .serialize(serializer), - GraphQLError::NotSubscription => [SerializeHelper { + .serialize(ser), + Self::NotSubscription => [Helper { message: "Expected subscription, got query", }] - .serialize(serializer), + .serialize(ser), } } } -impl<'de, S> de::Deserialize<'de> for InputValue -where - S: ScalarValue, -{ - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - struct InputValueVisitor(S::Visitor); - - impl Default for InputValueVisitor { - fn default() -> Self { - InputValueVisitor(S::Visitor::default()) - } - } +impl<'de, S: Deserialize<'de>> Deserialize<'de> for InputValue { + fn deserialize>(de: D) -> Result { + struct Visitor(PhantomData); - impl<'de, S> de::Visitor<'de> for InputValueVisitor - where - S: ScalarValue, - { + impl<'de, S: Deserialize<'de>> de::Visitor<'de> for Visitor { type Value = InputValue; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid input value") + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a valid input value") } - fn visit_bool(self, value: bool) -> Result, E> - where - E: de::Error, - { - self.0.visit_bool(value).map(InputValue::Scalar) + fn visit_bool(self, b: bool) -> Result { + S::deserialize(b.into_deserializer()).map(InputValue::Scalar) } - fn visit_i8(self, value: i8) -> Result, E> - where - E: de::Error, - { - self.0.visit_i8(value).map(InputValue::Scalar) + fn visit_i8(self, n: i8) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_i16(self, value: i16) -> Result, E> - where - E: de::Error, - { - self.0.visit_i16(value).map(InputValue::Scalar) + fn visit_i16(self, n: i16) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_i32(self, value: i32) -> Result, E> - where - E: de::Error, - { - self.0.visit_i32(value).map(InputValue::Scalar) + fn visit_i32(self, n: i32) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_i64(self, value: i64) -> Result, E> - where - E: de::Error, - { - self.0.visit_i64(value).map(InputValue::Scalar) + fn visit_i64(self, n: i64) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - serde::serde_if_integer128! { - fn visit_i128(self, value: i128) -> Result, E> - where - E: de::Error, - { - self.0.visit_i128(value).map(InputValue::Scalar) + serde_if_integer128! { + fn visit_i128(self, n: i128) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } } - fn visit_u8(self, value: u8) -> Result, E> - where - E: de::Error, - { - self.0.visit_u8(value).map(InputValue::Scalar) + fn visit_u8(self, n: u8) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_u16(self, value: u16) -> Result, E> - where - E: de::Error, - { - self.0.visit_u16(value).map(InputValue::Scalar) + fn visit_u16(self, n: u16) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_u32(self, value: u32) -> Result, E> - where - E: de::Error, - { - self.0.visit_u32(value).map(InputValue::Scalar) + fn visit_u32(self, n: u32) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_u64(self, value: u64) -> Result, E> - where - E: de::Error, - { - self.0.visit_u64(value).map(InputValue::Scalar) + fn visit_u64(self, n: u64) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - serde::serde_if_integer128! { - fn visit_u128(self, value: u128) -> Result, E> - where - E: de::Error, - { - self.0.visit_u128(value).map(InputValue::Scalar) + serde_if_integer128! { + fn visit_u128(self, n: u128) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } } - fn visit_f32(self, value: f32) -> Result, E> - where - E: de::Error, - { - self.0.visit_f32(value).map(InputValue::Scalar) + fn visit_f32(self, n: f32) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_f64(self, value: f64) -> Result, E> - where - E: de::Error, - { - self.0.visit_f64(value).map(InputValue::Scalar) + fn visit_f64(self, n: f64) -> Result { + S::deserialize(n.into_deserializer()).map(InputValue::Scalar) } - fn visit_char(self, value: char) -> Result, E> - where - E: de::Error, - { - self.0.visit_char(value).map(InputValue::Scalar) + fn visit_char(self, c: char) -> Result { + S::deserialize(c.into_deserializer()).map(InputValue::Scalar) } - fn visit_str(self, value: &str) -> Result, E> - where - E: de::Error, - { - self.0.visit_str(value).map(InputValue::Scalar) + fn visit_str(self, s: &str) -> Result { + S::deserialize(s.into_deserializer()).map(InputValue::Scalar) } - fn visit_string(self, value: String) -> Result, E> - where - E: de::Error, - { - self.0.visit_string(value).map(InputValue::Scalar) + fn visit_string(self, s: String) -> Result { + S::deserialize(s.into_deserializer()).map(InputValue::Scalar) } - fn visit_bytes(self, bytes: &[u8]) -> Result, E> - where - E: de::Error, - { - self.0.visit_bytes(bytes).map(InputValue::Scalar) + fn visit_bytes(self, b: &[u8]) -> Result { + S::deserialize(b.into_deserializer()).map(InputValue::Scalar) } - fn visit_byte_buf(self, bytes: Vec) -> Result, E> - where - E: de::Error, - { - self.0.visit_byte_buf(bytes).map(InputValue::Scalar) + fn visit_byte_buf(self, b: Vec) -> Result { + S::deserialize(b.into_deserializer()).map(InputValue::Scalar) } - fn visit_none(self) -> Result, E> - where - E: de::Error, - { - Ok(InputValue::null()) + fn visit_none(self) -> Result { + Ok(InputValue::Null) } - fn visit_unit(self) -> Result, E> - where - E: de::Error, - { - Ok(InputValue::null()) + fn visit_unit(self) -> Result { + Ok(InputValue::Null) } - fn visit_seq(self, mut visitor: V) -> Result, V::Error> + fn visit_seq(self, mut visitor: V) -> Result where V: de::SeqAccess<'de>, { - let mut values = Vec::new(); - - while let Some(el) = visitor.next_element()? { - values.push(el); + let mut vals = Vec::new(); + while let Some(v) = visitor.next_element()? { + vals.push(v); } - - Ok(InputValue::list(values)) + Ok(InputValue::list(vals)) } - fn visit_map(self, mut visitor: V) -> Result, V::Error> + fn visit_map(self, mut visitor: V) -> Result where V: de::MapAccess<'de>, { - let mut object = IndexMap::>::with_capacity( + let mut obj = IndexMap::>::with_capacity( visitor.size_hint().unwrap_or(0), ); - - while let Some((key, value)) = visitor.next_entry()? { - object.insert(key, value); + while let Some((key, val)) = visitor.next_entry()? { + obj.insert(key, val); } - - Ok(InputValue::object(object)) + Ok(InputValue::object(obj)) } } - deserializer.deserialize_any(InputValueVisitor::default()) + de.deserialize_any(Visitor(PhantomData)) } } -impl ser::Serialize for InputValue -where - T: ScalarValue, -{ - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - match *self { - InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(), - InputValue::Scalar(ref s) => s.serialize(serializer), - InputValue::Enum(ref v) => serializer.serialize_str(v), - InputValue::List(ref v) => v - .iter() - .map(|x| x.item.clone()) - .collect::>() - .serialize(serializer), - InputValue::Object(ref v) => v +impl Serialize for InputValue { + fn serialize(&self, ser: S) -> Result { + match self { + Self::Null | Self::Variable(_) => ser.serialize_unit(), + Self::Scalar(s) => s.serialize(ser), + Self::Enum(e) => ser.serialize_str(e), + Self::List(l) => l.iter().map(|x| &x.item).collect::>().serialize(ser), + Self::Object(o) => o .iter() - .map(|&(ref k, ref v)| (k.item.clone(), v.item.clone())) + .map(|(k, v)| (k.item.as_str(), &v.item)) .collect::>() - .serialize(serializer), + .serialize(ser), } } } -impl ser::Serialize for RuleError { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - let mut map = serializer.serialize_map(Some(2))?; +impl Serialize for RuleError { + fn serialize(&self, ser: S) -> Result { + let mut map = ser.serialize_map(Some(2))?; map.serialize_key("message")?; map.serialize_value(self.message())?; @@ -328,12 +231,9 @@ impl ser::Serialize for RuleError { } } -impl ser::Serialize for SourcePosition { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - let mut map = serializer.serialize_map(Some(2))?; +impl Serialize for SourcePosition { + fn serialize(&self, ser: S) -> Result { + let mut map = ser.serialize_map(Some(2))?; let line = self.line() + 1; map.serialize_key("line")?; @@ -347,23 +247,19 @@ impl ser::Serialize for SourcePosition { } } -impl<'a> ser::Serialize for Spanning> { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - let mut map = serializer.serialize_map(Some(2))?; +impl<'a> Serialize for Spanning> { + fn serialize(&self, ser: S) -> Result { + let mut map = ser.serialize_map(Some(2))?; - let message = format!("{}", self.item); + let msg = format!("{}", self.item); map.serialize_key("message")?; - map.serialize_value(&message)?; + map.serialize_value(&msg)?; - let mut location = IndexMap::new(); - location.insert("line".to_owned(), self.start.line() + 1); - location.insert("column".to_owned(), self.start.column() + 1); - - let locations = vec![location]; + let mut loc = IndexMap::new(); + loc.insert("line".to_owned(), self.start.line() + 1); + loc.insert("column".to_owned(), self.start.column() + 1); + let locations = vec![loc]; map.serialize_key("locations")?; map.serialize_value(&locations)?; @@ -371,57 +267,107 @@ impl<'a> ser::Serialize for Spanning> { } } -impl ser::Serialize for Object -where - T: ser::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - let mut map = serializer.serialize_map(Some(self.field_count()))?; - - for (ref f, ref v) in self.iter() { +impl Serialize for Object { + fn serialize(&self, ser: S) -> Result { + let mut map = ser.serialize_map(Some(self.field_count()))?; + for (f, v) in self.iter() { map.serialize_key(f)?; map.serialize_value(v)?; } - map.end() } } -impl ser::Serialize for Value -where - T: ser::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - match *self { - Value::Null => serializer.serialize_unit(), - Value::Scalar(ref s) => s.serialize(serializer), - Value::List(ref v) => v.serialize(serializer), - Value::Object(ref v) => v.serialize(serializer), +impl Serialize for Value { + fn serialize(&self, ser: S) -> Result { + match self { + Self::Null => ser.serialize_unit(), + Self::Scalar(s) => s.serialize(ser), + Self::List(l) => l.serialize(ser), + Self::Object(o) => o.serialize(ser), + } + } +} + +impl<'de> Deserialize<'de> for DefaultScalarValue { + fn deserialize>(de: D) -> Result { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = DefaultScalarValue; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a valid input value") + } + + fn visit_bool(self, b: bool) -> Result { + Ok(DefaultScalarValue::Boolean(b)) + } + + fn visit_i64(self, n: i64) -> Result { + if n >= i64::from(i32::MIN) && n <= i64::from(i32::MAX) { + Ok(DefaultScalarValue::Int(n.try_into().unwrap())) + } else { + // Browser's `JSON.stringify()` serializes all numbers + // having no fractional part as integers (no decimal point), + // so we must parse large integers as floating point, + // otherwise we would error on transferring large floating + // point numbers. + // TODO: Use `FloatToInt` conversion once stabilized: + // https://github.com/rust-lang/rust/issues/67057 + Ok(DefaultScalarValue::Float(n as f64)) + } + } + + fn visit_u64(self, n: u64) -> Result { + if n <= u64::try_from(i32::MAX).unwrap() { + self.visit_i64(n.try_into().unwrap()) + } else { + // Browser's `JSON.stringify()` serializes all numbers + // having no fractional part as integers (no decimal point), + // so we must parse large integers as floating point, + // otherwise we would error on transferring large floating + // point numbers. + // TODO: Use `FloatToInt` conversion once stabilized: + // https://github.com/rust-lang/rust/issues/67057 + Ok(DefaultScalarValue::Float(n as f64)) + } + } + + fn visit_f64(self, f: f64) -> Result { + Ok(DefaultScalarValue::Float(f)) + } + + fn visit_str(self, s: &str) -> Result { + self.visit_string(s.into()) + } + + fn visit_string(self, s: String) -> Result { + Ok(DefaultScalarValue::String(s)) + } } + + de.deserialize_any(Visitor) } } #[cfg(test)] mod tests { - use super::{ExecutionError, GraphQLError}; + use serde_json::{from_str, to_string}; + use crate::{ ast::InputValue, value::{DefaultScalarValue, Object}, FieldError, Value, }; - use serde_json::{from_str, to_string}; + + use super::{ExecutionError, GraphQLError}; #[test] fn int() { assert_eq!( from_str::>("1235").unwrap(), - InputValue::scalar(1235) + InputValue::scalar(1235), ); } @@ -429,12 +375,12 @@ mod tests { fn float() { assert_eq!( from_str::>("2.0").unwrap(), - InputValue::scalar(2.0) + InputValue::scalar(2.0), ); // large value without a decimal part is also float assert_eq!( from_str::>("123567890123").unwrap(), - InputValue::scalar(123_567_890_123.0) + InputValue::scalar(123_567_890_123.0), ); } @@ -442,7 +388,7 @@ mod tests { fn errors() { assert_eq!( to_string(&GraphQLError::UnknownOperationName).unwrap(), - r#"[{"message":"Unknown operation"}]"# + r#"[{"message":"Unknown operation"}]"#, ); } @@ -456,7 +402,7 @@ mod tests { Value::Object(obj), ))) .unwrap(), - r#"{"message":"foo error","locations":[{"line":1,"column":1}],"path":[],"extensions":{"foo":"bar"}}"# + r#"{"message":"foo error","locations":[{"line":1,"column":1}],"path":[],"extensions":{"foo":"bar"}}"#, ); } } diff --git a/juniper/src/parser/utils.rs b/juniper/src/parser/utils.rs index 771adfc47..fdad16795 100644 --- a/juniper/src/parser/utils.rs +++ b/juniper/src/parser/utils.rs @@ -82,7 +82,7 @@ impl Spanning { } /// Modify the contents of the spanned item - pub fn map O>(self, f: F) -> Spanning { + pub fn map O>(self, f: F) -> Spanning { Spanning { item: f(self.item), start: self.start, diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index 14e1989cd..ce37ad001 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -35,7 +35,7 @@ pub enum Value { Object(Object), } -impl Value { +impl Value { // CONSTRUCTORS /// Construct a null value. @@ -43,30 +43,6 @@ impl Value { Self::Null } - /// Construct an integer value. - #[deprecated(since = "0.11.0", note = "Use `Value::scalar` instead")] - pub fn int(i: i32) -> Self { - Self::scalar(i) - } - - /// Construct a floating point value. - #[deprecated(since = "0.11.0", note = "Use `Value::scalar` instead")] - pub fn float(f: f64) -> Self { - Self::scalar(f) - } - - /// Construct a string value. - #[deprecated(since = "0.11.0", note = "Use `Value::scalar` instead")] - pub fn string(s: &str) -> Self { - Self::scalar(s.to_owned()) - } - - /// Construct a boolean value. - #[deprecated(since = "0.11.0", note = "Use `Value::scalar` instead")] - pub fn boolean(b: bool) -> Self { - Self::scalar(b) - } - /// Construct a list value. pub fn list(l: Vec) -> Self { Self::List(l) @@ -80,7 +56,7 @@ impl Value { /// Construct a scalar value pub fn scalar(s: T) -> Self where - T: Into, + S: From, { Self::Scalar(s.into()) } @@ -95,26 +71,29 @@ impl Value { /// View the underlying scalar value if present pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T> where - &'a S: Into>, + Option<&'a T>: From<&'a S>, { - match *self { - Self::Scalar(ref s) => s.into(), + match self { + Self::Scalar(s) => s.into(), _ => None, } } /// View the underlying float value, if present. - pub fn as_float_value(&self) -> Option { + pub fn as_float_value(&self) -> Option + where + S: ScalarValue, + { match self { - Self::Scalar(ref s) => s.as_float(), + Self::Scalar(s) => s.as_float(), _ => None, } } /// View the underlying object value, if present. pub fn as_object_value(&self) -> Option<&Object> { - match *self { - Self::Object(ref o) => Some(o), + match self { + Self::Object(o) => Some(o), _ => None, } } @@ -131,24 +110,24 @@ impl Value { /// Mutable view into the underlying object value, if present. pub fn as_mut_object_value(&mut self) -> Option<&mut Object> { - match *self { - Self::Object(ref mut o) => Some(o), + match self { + Self::Object(o) => Some(o), _ => None, } } /// View the underlying list value, if present. pub fn as_list_value(&self) -> Option<&Vec> { - match *self { - Self::List(ref l) => Some(l), + match self { + Self::List(l) => Some(l), _ => None, } } /// View the underlying scalar value, if present pub fn as_scalar(&self) -> Option<&S> { - match *self { - Self::Scalar(ref s) => Some(s), + match self { + Self::Scalar(s) => Some(s), _ => None, } } @@ -158,11 +137,15 @@ impl Value { where Option<&'a String>: From<&'a S>, { - self.as_scalar_value::().map(|s| s as &str) + self.as_scalar_value::().map(String::as_str) } /// Maps the [`ScalarValue`] type of this [`Value`] into the specified one. - pub fn map_scalar_value(self) -> Value { + pub fn map_scalar_value(self) -> Value + where + S: ScalarValue, + Into: ScalarValue, + { if TypeId::of::() == TypeId::of::() { // SAFETY: This is safe, because we're transmuting the value into // itself, so no invariants may change and we're just @@ -187,17 +170,17 @@ impl Value { } } -impl ToInputValue for Value { +impl ToInputValue for Value { fn to_input_value(&self) -> InputValue { - match *self { - Value::Null => InputValue::Null, - Value::Scalar(ref s) => InputValue::Scalar(s.clone()), - Value::List(ref l) => InputValue::List( + match self { + Self::Null => InputValue::Null, + Self::Scalar(s) => InputValue::Scalar(s.clone()), + Self::List(l) => InputValue::List( l.iter() .map(|x| Spanning::unlocated(x.to_input_value())) .collect(), ), - Value::Object(ref o) => InputValue::Object( + Self::Object(o) => InputValue::Object( o.iter() .map(|(k, v)| { ( @@ -214,15 +197,15 @@ impl ToInputValue for Value { impl Display for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Value::Null => write!(f, "null"), - Value::Scalar(s) => { + Self::Null => write!(f, "null"), + Self::Scalar(s) => { if let Some(string) = s.as_string() { write!(f, "\"{}\"", string) } else { write!(f, "{}", s) } } - Value::List(list) => { + Self::List(list) => { write!(f, "[")?; for (idx, item) in list.iter().enumerate() { write!(f, "{}", item)?; @@ -234,7 +217,7 @@ impl Display for Value { Ok(()) } - Value::Object(obj) => { + Self::Object(obj) => { write!(f, "{{")?; for (idx, (key, value)) in obj.iter().enumerate() { write!(f, "\"{}\": {}", key, value)?; @@ -253,59 +236,43 @@ impl Display for Value { impl From> for Value where - S: ScalarValue, - Value: From, + Self: From, { - fn from(v: Option) -> Value { + fn from(v: Option) -> Self { match v { Some(v) => v.into(), - None => Value::null(), + None => Self::Null, } } } -impl<'a, S> From<&'a str> for Value -where - S: ScalarValue, -{ +impl<'a, S: From> From<&'a str> for Value { fn from(s: &'a str) -> Self { - Value::scalar(s.to_owned()) + Self::scalar(s.to_owned()) } } -impl From for Value -where - S: ScalarValue, -{ +impl> From for Value { fn from(s: String) -> Self { - Value::scalar(s) + Self::scalar(s) } } -impl From for Value -where - S: ScalarValue, -{ +impl> From for Value { fn from(i: i32) -> Self { - Value::scalar(i) + Self::scalar(i) } } -impl From for Value -where - S: ScalarValue, -{ +impl> From for Value { fn from(f: f64) -> Self { - Value::scalar(f) + Self::scalar(f) } } -impl From for Value -where - S: ScalarValue, -{ +impl> From for Value { fn from(b: bool) -> Self { - Value::scalar(b) + Self::scalar(b) } } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 397860e18..d9f6ddfa0 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -1,6 +1,6 @@ use std::fmt; -use serde::{de, ser::Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use crate::{ parser::{ParseError, ScalarToken}, @@ -24,9 +24,8 @@ pub trait ParseScalarValue { /// needs. /// There is a custom derive (`#[derive(juniper::GraphQLScalarValue)]`) available that implements /// most of the required traits automatically for a enum representing a scalar value. -/// This derives needs a additional annotation of the form -/// `#[juniper(visitor = "VisitorType")]` to specify a type that implements -/// `serde::de::Visitor` and that is used to deserialize the value. +/// However, [`Serialize`](trait@serde::Serialize) and [`Deserialize`](trait@serde::Deserialize) +/// implementations are expected to be provided. /// /// # Implementing a new scalar value representation /// The preferred way to define a new scalar value representation is @@ -34,12 +33,13 @@ pub trait ParseScalarValue { /// at the lowest level. /// The following example introduces an new variant that is able to store 64 bit integers. /// -/// ``` -/// # use std::fmt; -/// # use serde::{de, Deserialize, Deserializer}; -/// # use juniper::ScalarValue; +/// ```rust +/// # use std::{fmt, convert::TryInto as _}; +/// # use serde::{de, Deserialize, Deserializer, Serialize}; +/// # use juniper::{GraphQLScalarValue, ScalarValue}; /// # -/// #[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)] +/// #[derive(Clone, Debug, GraphQLScalarValue, PartialEq, Serialize)] +/// #[serde(untagged)] /// enum MyScalarValue { /// Int(i32), /// Long(i64), @@ -49,18 +49,16 @@ pub trait ParseScalarValue { /// } /// /// impl ScalarValue for MyScalarValue { -/// type Visitor = MyScalarValueVisitor; -/// -/// fn as_int(&self) -> Option { -/// match *self { -/// Self::Int(ref i) => Some(*i), +/// fn as_int(&self) -> Option { +/// match self { +/// Self::Int(i) => Some(*i), /// _ => None, /// } /// } /// /// fn as_string(&self) -> Option { -/// match *self { -/// Self::String(ref s) => Some(s.clone()), +/// match self { +/// Self::String(s) => Some(s.clone()), /// _ => None, /// } /// } @@ -73,110 +71,99 @@ pub trait ParseScalarValue { /// } /// /// fn as_str(&self) -> Option<&str> { -/// match *self { -/// Self::String(ref s) => Some(s.as_str()), +/// match self { +/// Self::String(s) => Some(s.as_str()), /// _ => None, /// } /// } /// /// fn as_float(&self) -> Option { -/// match *self { -/// Self::Int(ref i) => Some(*i as f64), -/// Self::Float(ref f) => Some(*f), +/// match self { +/// Self::Int(i) => Some(f64::from(*i)), +/// Self::Float(f) => Some(*f), /// _ => None, /// } /// } /// /// fn as_boolean(&self) -> Option { -/// match *self { -/// Self::Boolean(ref b) => Some(*b), +/// match self { +/// Self::Boolean(b) => Some(*b), /// _ => None, /// } /// } /// } /// -/// #[derive(Default)] -/// struct MyScalarValueVisitor; +/// impl<'de> Deserialize<'de> for MyScalarValue { +/// fn deserialize>(de: D) -> Result { +/// struct Visitor; /// -/// impl<'de> de::Visitor<'de> for MyScalarValueVisitor { -/// type Value = MyScalarValue; +/// impl<'de> de::Visitor<'de> for Visitor { +/// type Value = MyScalarValue; /// -/// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { -/// formatter.write_str("a valid input value") -/// } +/// fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { +/// f.write_str("a valid input value") +/// } /// -/// fn visit_bool(self, value: bool) -> Result { -/// Ok(MyScalarValue::Boolean(value)) -/// } +/// fn visit_bool(self, b: bool) -> Result { +/// Ok(MyScalarValue::Boolean(b)) +/// } /// -/// fn visit_i32(self, value: i32) -> Result -/// where -/// E: de::Error, -/// { -/// Ok(MyScalarValue::Int(value)) -/// } +/// fn visit_i32(self, n: i32) -> Result { +/// Ok(MyScalarValue::Int(n)) +/// } /// -/// fn visit_i64(self, value: i64) -> Result -/// where -/// E: de::Error, -/// { -/// if value <= i32::max_value() as i64 { -/// self.visit_i32(value as i32) -/// } else { -/// Ok(MyScalarValue::Long(value)) -/// } -/// } +/// fn visit_i64(self, n: i64) -> Result { +/// if n <= i64::from(i32::MAX) { +/// self.visit_i32(n.try_into().unwrap()) +/// } else { +/// Ok(MyScalarValue::Long(n)) +/// } +/// } /// -/// fn visit_u32(self, value: u32) -> Result -/// where -/// E: de::Error, -/// { -/// if value <= i32::max_value() as u32 { -/// self.visit_i32(value as i32) -/// } else { -/// self.visit_u64(value as u64) -/// } -/// } +/// fn visit_u32(self, n: u32) -> Result { +/// if n <= i32::MAX as u32 { +/// self.visit_i32(n.try_into().unwrap()) +/// } else { +/// self.visit_u64(n.into()) +/// } +/// } /// -/// fn visit_u64(self, value: u64) -> Result -/// where -/// E: de::Error, -/// { -/// if value <= i64::max_value() as u64 { -/// self.visit_i64(value as i64) -/// } else { -/// // Browser's JSON.stringify serialize all numbers having no -/// // fractional part as integers (no decimal point), so we -/// // must parse large integers as floating point otherwise -/// // we would error on transferring large floating point -/// // numbers. -/// Ok(MyScalarValue::Float(value as f64)) -/// } -/// } +/// fn visit_u64(self, n: u64) -> Result { +/// if n <= i64::MAX as u64 { +/// self.visit_i64(n.try_into().unwrap()) +/// } else { +/// // Browser's `JSON.stringify()` serialize all numbers +/// // having no fractional part as integers (no decimal +/// // point), so we must parse large integers as floating +/// // point, otherwise we would error on transferring large +/// // floating point numbers. +/// Ok(MyScalarValue::Float(n as f64)) +/// } +/// } /// -/// fn visit_f64(self, value: f64) -> Result { -/// Ok(MyScalarValue::Float(value)) -/// } +/// fn visit_f64(self, f: f64) -> Result { +/// Ok(MyScalarValue::Float(f)) +/// } /// -/// fn visit_str(self, value: &str) -> Result -/// where -/// E: de::Error, -/// { -/// self.visit_string(value.into()) -/// } +/// fn visit_str(self, s: &str) -> Result { +/// self.visit_string(s.into()) +/// } +/// +/// fn visit_string(self, s: String) -> Result { +/// Ok(MyScalarValue::String(s)) +/// } +/// } /// -/// fn visit_string(self, value: String) -> Result { -/// Ok(MyScalarValue::String(value)) +/// de.deserialize_any(Visitor) /// } /// } -/// -/// # fn main() {} /// ``` pub trait ScalarValue: fmt::Debug + fmt::Display + PartialEq + Clone + + DeserializeOwned + Serialize + From + From @@ -184,65 +171,68 @@ pub trait ScalarValue: + From + 'static { - /// Serde visitor used to deserialize this scalar value - type Visitor: for<'de> de::Visitor<'de, Value = Self> + Default; - - /// Checks if the current value contains the a value of the current type + /// Checks whether this [`ScalarValue`] contains the value of the given + /// type. /// /// ``` /// # use juniper::{ScalarValue, DefaultScalarValue}; - /// + /// # /// let value = DefaultScalarValue::Int(42); /// /// assert_eq!(value.is_type::(), true); /// assert_eq!(value.is_type::(), false); - /// /// ``` + #[must_use] fn is_type<'a, T>(&'a self) -> bool where T: 'a, - &'a Self: Into>, + Option<&'a T>: From<&'a Self>, { - self.into().is_some() + >::from(self).is_some() } - /// Convert the given scalar value into an integer value + /// Represents this [`ScalarValue`] as an integer value. /// - /// This function is used for implementing `GraphQLValue` for `i32` for all - /// scalar values. Implementations should convert all supported integer - /// types with 32 bit or less to an integer if requested. + /// This function is used for implementing [`GraphQLValue`] for [`i32`] for + /// all possible [`ScalarValue`]s. Implementations should convert all the + /// supported integer types with 32 bit or less to an integer, if requested. + #[must_use] fn as_int(&self) -> Option; - /// Represents this [`ScalarValue`] a [`String`] value. + /// Represents this [`ScalarValue`] as a [`String`] value. /// - /// This function is used for implementing `GraphQLValue` for `String` for all - /// scalar values + /// This function is used for implementing [`GraphQLValue`] for [`String`] + /// for all possible [`ScalarValue`]s. + #[must_use] fn as_string(&self) -> Option; /// Converts this [`ScalarValue`] into a [`String`] value. /// - /// Same as [`ScalarValue::as_string`], but takes ownership, so allows to omit redundant - /// cloning. + /// Same as [`ScalarValue::as_string()`], but takes ownership, so allows to + /// omit redundant cloning. + #[must_use] fn into_string(self) -> Option; - /// Convert the given scalar value into a string value + /// Represents this [`ScalarValue`] as a [`str`] value. /// - /// This function is used for implementing `GraphQLValue` for `String` for all - /// scalar values + /// This function is used for implementing [`GraphQLValue`] for [`str`] for + /// all possible [`ScalarValue`]s. + #[must_use] fn as_str(&self) -> Option<&str>; - /// Convert the given scalar value into a float value + /// Represents this [`ScalarValue`] as a float value. /// - /// This function is used for implementing `GraphQLValue` for `f64` for all - /// scalar values. Implementations should convert all supported integer - /// types with 64 bit or less and all floating point values with 64 bit or - /// less to a float if requested. + /// This function is used for implementing [`GraphQLValue`] for [`f64`] for + /// all possible [`ScalarValue`]s. Implementations should convert all + /// supported integer types with 64 bit or less and all floating point + /// values with 64 bit or less to a float, if requested. + #[must_use] fn as_float(&self) -> Option; - /// Convert the given scalar value into a boolean value + /// Represents this [`ScalarValue`] as a boolean value /// - /// This function is used for implementing `GraphQLValue` for `bool` for all - /// scalar values. + /// This function is used for implementing [`GraphQLValue`] for [`bool`] for + /// all possible [`ScalarValue`]s. fn as_boolean(&self) -> Option; /// Converts this [`ScalarValue`] into another one. @@ -261,46 +251,64 @@ pub trait ScalarValue: } } -/// The default scalar value representation in juniper +/// The default [`ScalarValue`] representation in [`juniper`]. +/// +/// These types closely follow the [GraphQL specification][0]. /// -/// This types closely follows the graphql specification. -#[derive(Debug, PartialEq, Clone, GraphQLScalarValue)] -#[allow(missing_docs)] +/// [0]: https://spec.graphql.org/June2018 +#[derive(Clone, Debug, GraphQLScalarValue, PartialEq, Serialize)] +#[serde(untagged)] pub enum DefaultScalarValue { + /// [`Int` scalar][0] as a signed 32‐bit numeric non‐fractional value. + /// + /// [0]: https://spec.graphql.org/June2018/#sec-Int Int(i32), + + /// [`Float` scalar][0] as a signed double‐precision fractional values as + /// specified by [IEEE 754]. + /// + /// [0]: https://spec.graphql.org/June2018/#sec-Float + /// [IEEE 754]: https://en.wikipedia.org/wiki/IEEE_floating_point Float(f64), + + /// [`String` scalar][0] as a textual data, represented as UTF‐8 character + /// sequences. + /// + /// [0]: https://spec.graphql.org/June2018/#sec-String String(String), + + /// [`Boolean` scalar][0] as a `true` or `false` value. + /// + /// [0]: https://spec.graphql.org/June2018/#sec-Boolean Boolean(bool), } impl ScalarValue for DefaultScalarValue { - type Visitor = DefaultScalarValueVisitor; - fn as_int(&self) -> Option { - match *self { - Self::Int(ref i) => Some(*i), + match self { + Self::Int(i) => Some(*i), _ => None, } } fn as_float(&self) -> Option { - match *self { - Self::Int(ref i) => Some(*i as f64), - Self::Float(ref f) => Some(*f), + match self { + Self::Int(i) => Some(f64::from(*i)), + Self::Float(f) => Some(*f), _ => None, } } fn as_str(&self) -> Option<&str> { - match *self { - Self::String(ref s) => Some(s.as_str()), + match self { + Self::String(s) => Some(s.as_str()), _ => None, } } fn as_string(&self) -> Option { - match *self { - Self::String(ref s) => Some(s.clone()), + match self { + Self::String(s) => Some(s.clone()), _ => None, } } @@ -313,8 +321,8 @@ impl ScalarValue for DefaultScalarValue { } fn as_boolean(&self) -> Option { - match *self { - Self::Boolean(ref b) => Some(*b), + match self { + Self::Boolean(b) => Some(*b), _ => None, } } @@ -334,65 +342,3 @@ impl<'a> From<&'a str> for DefaultScalarValue { Self::String(s.into()) } } - -#[derive(Default, Clone, Copy, Debug)] -pub struct DefaultScalarValueVisitor; - -impl<'de> de::Visitor<'de> for DefaultScalarValueVisitor { - type Value = DefaultScalarValue; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid input value") - } - - fn visit_bool(self, value: bool) -> Result { - Ok(DefaultScalarValue::Boolean(value)) - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) { - Ok(DefaultScalarValue::Int(value as i32)) - } else { - // Browser's JSON.stringify serialize all numbers having no - // fractional part as integers (no decimal point), so we - // must parse large integers as floating point otherwise - // we would error on transferring large floating point - // numbers. - Ok(DefaultScalarValue::Float(value as f64)) - } - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value <= i32::max_value() as u64 { - self.visit_i64(value as i64) - } else { - // Browser's JSON.stringify serialize all numbers having no - // fractional part as integers (no decimal point), so we - // must parse large integers as floating point otherwise - // we would error on transferring large floating point - // numbers. - Ok(DefaultScalarValue::Float(value as f64)) - } - } - - fn visit_f64(self, value: f64) -> Result { - Ok(DefaultScalarValue::Float(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - self.visit_string(value.into()) - } - - fn visit_string(self, value: String) -> Result { - Ok(DefaultScalarValue::String(value)) - } -} diff --git a/juniper_codegen/src/derive_scalar_value.rs b/juniper_codegen/src/derive_scalar_value.rs index 9a9fc466f..12a5136b6 100644 --- a/juniper_codegen/src/derive_scalar_value.rs +++ b/juniper_codegen/src/derive_scalar_value.rs @@ -236,14 +236,11 @@ fn impl_scalar_enum( .map(|v| derive_from_variant(v, ident, &error)) .collect::, _>>()?; - let serialize = derive_serialize(data.variants.iter(), ident); - let display = derive_display(data.variants.iter(), ident); Ok(quote! { #(#froms)* - #serialize #display }) } @@ -268,28 +265,6 @@ where } } -fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> TokenStream -where - I: Iterator, -{ - let arms = variants.map(|v| { - let variant = &v.ident; - quote!(#ident::#variant(ref v) => v.serialize(serializer),) - }); - - quote! { - impl ::juniper::serde::Serialize for #ident { - fn serialize(&self, serializer: S) -> ::std::result::Result - where S: ::juniper::serde::Serializer - { - match *self { - #(#arms)* - } - } - } - } -} - fn derive_from_variant( variant: &Variant, ident: &Ident, diff --git a/juniper_graphql_ws/src/client_message.rs b/juniper_graphql_ws/src/client_message.rs index a36e174a2..08745bd28 100644 --- a/juniper_graphql_ws/src/client_message.rs +++ b/juniper_graphql_ws/src/client_message.rs @@ -1,4 +1,4 @@ -use juniper::{ScalarValue, Variables}; +use juniper::Variables; use serde::Deserialize; use crate::utils::default_for_null; @@ -6,9 +6,9 @@ use crate::utils::default_for_null; /// The payload for a client's "start" message. This triggers execution of a query, mutation, or /// subscription. #[derive(Debug, Deserialize, PartialEq)] -#[serde(bound(deserialize = "S: ScalarValue"))] +#[serde(bound(deserialize = "S: Deserialize<'de>"))] #[serde(rename_all = "camelCase")] -pub struct StartPayload { +pub struct StartPayload { /// The document body. pub query: String, @@ -22,10 +22,10 @@ pub struct StartPayload { /// ClientMessage defines the message types that clients can send. #[derive(Debug, Deserialize, PartialEq)] -#[serde(bound(deserialize = "S: ScalarValue"))] +#[serde(bound(deserialize = "S: Deserialize<'de>"))] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] -pub enum ClientMessage { +pub enum ClientMessage { /// ConnectionInit is sent by the client upon connecting. ConnectionInit { /// Optional parameters of any type sent from the client. These are often used for diff --git a/juniper_graphql_ws/src/server_message.rs b/juniper_graphql_ws/src/server_message.rs index 5bcc5111c..da7daf038 100644 --- a/juniper_graphql_ws/src/server_message.rs +++ b/juniper_graphql_ws/src/server_message.rs @@ -1,6 +1,6 @@ use std::{any::Any, fmt, marker::PhantomPinned, mem}; -use juniper::{ExecutionError, GraphQLError, ScalarValue, Value}; +use juniper::{ExecutionError, GraphQLError, Value}; use serde::{Serialize, Serializer}; /// The payload for errors that are not associated with a GraphQL operation. @@ -14,7 +14,6 @@ pub struct ConnectionErrorPayload { /// Sent after execution of an operation. For queries and mutations, this is sent to the client /// once. For subscriptions, this is sent for every event in the event stream. #[derive(Debug, Serialize, PartialEq)] -#[serde(bound(serialize = "S: ScalarValue"))] #[serde(rename_all = "camelCase")] pub struct DataPayload { /// The result data. @@ -90,10 +89,9 @@ impl From> for ErrorPayload { /// ServerMessage defines the message types that servers can send. #[derive(Debug, Serialize, PartialEq)] -#[serde(bound(serialize = "S: ScalarValue"))] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] -pub enum ServerMessage { +pub enum ServerMessage { /// ConnectionError is used for errors that are not associated with a GraphQL operation. For /// example, this will be used when: /// diff --git a/juniper_iron/src/lib.rs b/juniper_iron/src/lib.rs index 118c64862..5a6651878 100644 --- a/juniper_iron/src/lib.rs +++ b/juniper_iron/src/lib.rs @@ -185,15 +185,12 @@ fn parse_variable_param(params: Option>) -> IronResult>(get_single_value(values)?.as_ref()) - .map(Some) - .map_err(GraphQLIronError::Serde)?, - ) - } else { - Ok(None) - } + params + .map(|vals| { + serde_json::from_str::>(get_single_value(vals)?.as_ref()) + .map_err(|e| GraphQLIronError::Serde(e).into()) + }) + .transpose() } impl<'a, CtxFactory, Query, Mutation, Subscription, CtxT, S> diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index 677a992a5..af3e86f29 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -40,13 +40,14 @@ Check the LICENSE file for details. #![deny(warnings)] #![doc(html_root_url = "https://docs.rs/juniper_warp/0.2.0")] +use std::{collections::HashMap, str, sync::Arc}; + use anyhow::anyhow; use futures::{FutureExt as _, TryFutureExt}; use juniper::{ http::{GraphQLBatchRequest, GraphQLRequest}, ScalarValue, }; -use std::{collections::HashMap, str, sync::Arc}; use tokio::task; use warp::{body, filters::BoxedFilter, http, hyper::body::Bytes, query, Filter}; @@ -381,6 +382,8 @@ fn playground_response( /// [1]: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md #[cfg(feature = "subscriptions")] pub mod subscriptions { + use std::{convert::Infallible, fmt, sync::Arc}; + use juniper::{ futures::{ future::{self, Either}, @@ -390,7 +393,6 @@ pub mod subscriptions { GraphQLSubscriptionType, GraphQLTypeAsync, RootNode, ScalarValue, }; use juniper_graphql_ws::{ArcSchema, ClientMessage, Connection, Init}; - use std::{convert::Infallible, fmt, sync::Arc}; struct Message(warp::ws::Message);